source: coopr.pysp/stable/coopr/pysp/phinit.py @ 3185

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

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

........

r3120 | jwatson | 2010-10-18 21:11:21 -0600 (Mon, 18 Oct 2010) | 3 lines


Adding PySP options for linearizing expressions.

........

r3134 | jwatson | 2010-10-21 15:45:49 -0600 (Thu, 21 Oct 2010) | 3 lines


Bug fix associated with linear expressions.

........

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


Add name as contributor.

........

r3138 | jwatson | 2010-10-22 14:11:43 -0600 (Fri, 22 Oct 2010) | 3 lines


Added "--simplify-expressions" option to runph, in order to eliminate the memory and time costs associated with simplifying expressions (e.g., in formulation of the PH objective) for which simpification is very unlikely to help. By default, expression simplification is disabled. This may cause issues with certain writers, e.g., NL - which is why I have retained the option.

........

r3139 | jwatson | 2010-10-22 15:20:49 -0600 (Fri, 22 Oct 2010) | 3 lines


When forming the PH linear terms for all variables and the proximal terms for binary variables, use value() to immediately extract the fixed value of the underlying parameters. This speeds up the associated expression tree construction time, which was non-trivial (5% of the total run-time, for example). Because we re-form the expressions each iteration, this is OK.

........

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


Modifying more PH parameters to be of the "nochecking" variety. Also slightly improved the performance of the update_variable_statistics PH function.

........

r3145 | jwatson | 2010-10-22 22:21:06 -0600 (Fri, 22 Oct 2010) | 3 lines


Update of PySP baseline output files, due to small numerical differences (4th significant digit) associated with some code mods I recently performed in the course of optimization.

........

r3146 | jwatson | 2010-10-23 13:27:38 -0600 (Sat, 23 Oct 2010) | 3 lines


Speed improvements in weight update.

........

r3147 | jwatson | 2010-10-23 13:38:52 -0600 (Sat, 23 Oct 2010) | 3 lines


More speed improvements to PH weight computation procedure.

........

r3152 | jwatson | 2010-10-24 13:20:03 -0600 (Sun, 24 Oct 2010) | 3 lines


Further code optimizations to PH weight and statistic update routines.

........

r3155 | jwatson | 2010-10-24 22:15:29 -0600 (Sun, 24 Oct 2010) | 3 lines


Added --linearize-expression and associated processing to runef PySP script.

........

r3156 | jwatson | 2010-10-24 22:19:44 -0600 (Sun, 24 Oct 2010) | 3 lines


Correcting shift from "integer" to "general" variable blocks when writing the LP file for an extensive form.

........

r3165 | jwatson | 2010-10-26 18:46:24 -0600 (Tue, 26 Oct 2010) | 3 lines


Update of PySP CHANGELOG for 2.4 release.

........

r3168 | jwatson | 2010-10-26 20:12:23 -0600 (Tue, 26 Oct 2010) | 3 lines


Completing implementation of optional processing of expression simplification in PySP.

........

  • Property svn:executable set to *
File size: 30.2 KB
Line 
1#  _________________________________________________________________________
2#
3#  Coopr: A COmmon Optimization Python Repository
4#  Copyright (c) 2009 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
12import os
13import sys
14
15from optparse import OptionParser, OptionGroup
16
17import pyutilib.services
18import pyutilib.misc
19
20# garbage collection control.
21import gc
22
23# for profiling
24try:
25    import cProfile as profile
26except ImportError:
27    import profile
28import pstats
29
30# for serializing
31import pickle
32
33from coopr.pysp.convergence import *
34from coopr.pysp.scenariotree import *
35from coopr.pysp.ph import *
36from coopr.pysp.ef import *
37from coopr.opt.base import SolverFactory
38from coopr.opt.parallel import SolverManagerFactory
39
40from pyutilib.component.core import ExtensionPoint
41from coopr.pysp.solutionwriter import ISolutionWriterExtension
42
43#
44# utility method to construct an option parser for ph arguments,
45# to be supplied as an argument to the runph method.
46#
47
48def construct_ph_options_parser(usage_string):
49
50   parser = OptionParser()
51   parser.usage = usage_string
52
53   inputOpts       = OptionGroup( parser, 'Input Options' )
54   phOpts          = OptionGroup( parser, 'PH Options' )
55   solverOpts      = OptionGroup( parser, 'Solver Options' )
56   postprocessOpts = OptionGroup( parser, 'Postprocessing Options' )   
57   outputOpts      = OptionGroup( parser, 'Output Options' )
58   otherOpts       = OptionGroup( parser, 'Other Options' )
59   parser.add_option_group( inputOpts )
60   parser.add_option_group( phOpts )
61   parser.add_option_group( solverOpts )
62   parser.add_option_group( postprocessOpts )   
63   parser.add_option_group( outputOpts )
64   parser.add_option_group( otherOpts )
65
66   inputOpts.add_option('-m','--model-directory',
67     help="The directory in which all model (reference and scenario) definitions are stored. Default is \".\".",
68     action="store",
69     dest="model_directory",
70     type="string",
71     default=".")
72   inputOpts.add_option('-i','--instance-directory',
73     help="The directory in which all instance (reference and scenario) definitions are stored. Default is '.'.",
74     action="store",
75     dest="instance_directory",
76     type="string",
77     default=".")
78   inputOpts.add_option('--bounds-cfgfile',
79     help="The name of a configuration script to set variable bound values. Default is None.",
80     action="store",
81     dest="bounds_cfgfile",
82     default=None)
83
84   phOpts.add_option('-r','--default-rho',
85     help="The default (global) rho for all blended variables. Default is 1.",
86     action="store",
87     dest="default_rho",
88     type="float",
89     default=1.0)
90   phOpts.add_option('--rho-cfgfile',
91     help="The name of a configuration script to compute PH rho values. Default is None.",
92     action="store",
93     dest="rho_cfgfile",
94     type="string",
95     default=None)
96   phOpts.add_option('--max-iterations',
97     help="The maximal number of PH iterations. Default is 100.",
98     action="store",
99     dest="max_iterations",
100     type="int",
101     default=100)
102   phOpts.add_option('--termdiff-threshold',
103     help="The convergence threshold used in the term-diff and normalized term-diff convergence criteria. Default is 0.01.",
104     action="store",
105     dest="termdiff_threshold",
106     type="float",
107     default=0.01)
108   phOpts.add_option('--enable-free-discrete-count-convergence',
109     help="Terminate PH based on the free discrete variable count convergence metric. Default is False.",
110     action="store_true",
111     dest="enable_free_discrete_count_convergence",
112     default=False)
113   phOpts.add_option('--enable-normalized-termdiff-convergence',
114     help="Terminate PH based on the normalized termdiff convergence metric. Default is True.",
115     action="store_true",
116     dest="enable_normalized_termdiff_convergence",
117     default=False)
118   phOpts.add_option('--enable-termdiff-convergence',
119     help="Terminate PH based on the termdiff convergence metric. Default is True.",
120     action="store_true",
121     dest="enable_termdiff_convergence",
122     default=True)
123   phOpts.add_option('--free-discrete-count-threshold',
124     help="The convergence threshold used in the criterion based on when the free discrete variable count convergence criterion. Default is 20.",
125     action="store",
126     dest="free_discrete_count_threshold",
127     type="float",
128     default=20)
129   phOpts.add_option('--linearize-nonbinary-penalty-terms',
130     help="Approximate the PH quadratic term for non-binary variables with a piece-wise linear function, using the supplied number of equal-length pieces from each bound to the average",
131     action="store",
132     dest="linearize_nonbinary_penalty_terms",
133     type="int",
134     default=0)
135   phOpts.add_option('--breakpoint-strategy',
136     help="Specify the strategy to distribute breakpoints on the [lb, ub] interval of each variable when linearizing. 0 indicates uniform distribution. 1 indicates breakpoints at the node min and max, uniformly in-between. 2 indicates more aggressive concentration of breakpoints near the observed node min/max.",
137     action="store",
138     dest="breakpoint_strategy",
139     type="int",
140     default=0)   
141   phOpts.add_option('--retain-quadratic-binary-terms',
142     help="Do not linearize PH objective terms involving binary decision variables",
143     action="store_true",
144     dest="retain_quadratic_binary_terms",
145     default=False)
146   phOpts.add_option('--drop-proximal-terms',
147     help="Eliminate proximal terms (i.e., the quadratic penalty terms) from the weighted PH objective. Default is False.",
148     action="store_true",
149     dest="drop_proximal_terms",
150     default=False)
151   phOpts.add_option('--enable-ww-extensions',
152     help="Enable the Watson-Woodruff PH extensions plugin. Default is False.",
153     action="store_true",
154     dest="enable_ww_extensions",
155     default=False)
156   phOpts.add_option('--ww-extension-cfgfile',
157     help="The name of a configuration file for the Watson-Woodruff PH extensions plugin. Default is wwph.cfg.",
158     action="store",
159     dest="ww_extension_cfgfile",
160     type="string",
161     default="")
162   phOpts.add_option('--ww-extension-suffixfile',
163     help="The name of a variable suffix file for the Watson-Woodruff PH extensions plugin. Default is wwph.suffixes.",
164     action="store",
165     dest="ww_extension_suffixfile",
166     type="string",
167     default="")
168   phOpts.add_option('--user-defined-extension',
169     help="The name of a python module specifying a user-defined PH extension plugin.",
170     action="store",
171     dest="user_defined_extension",
172     type="string",
173     default=None)
174   phOpts.add_option("--linearize-expressions",
175     help="EXPERIMENTAL: An option intended for use on linear or mixed-integer models " \
176          "in which expression trees in a model (constraints or objectives) are compacted " \
177          "into a more memory-efficient and concise form. The trees themselves are eliminated. ",
178     action="store_true",
179     dest="linearize_expressions",
180     default=False)
181   phOpts.add_option("--simplify-expressions",
182     help="Enable expression simplification during both model instance creation and any " \
183          "subsequent modifications of the model, e.g., during manipulation of the objective.",
184     action="store_true",
185     dest="simplify_expressions",
186     default=False)
187
188   solverOpts.add_option('--scenario-mipgap',
189     help="Specifies the mipgap for all PH scenario sub-problems",
190     action="store",
191     dest="scenario_mipgap",
192     type="float",
193     default=None)
194   solverOpts.add_option('--scenario-solver-options',
195     help="Solver options for all PH scenario sub-problems",
196     action="append",
197     dest="scenario_solver_options",
198     type="string",
199     default=[])
200   solverOpts.add_option('--solver',
201     help="The type of solver used to solve scenario sub-problems. Default is cplex.",
202     action="store",
203     dest="solver_type",
204     type="string",
205     default="cplex")
206   solverOpts.add_option('--solver-manager',
207     help="The type of solver manager used to coordinate scenario sub-problem solves. Default is serial.",
208     action="store",
209     dest="solver_manager_type",
210     type="string",
211     default="serial")
212   solverOpts.add_option('--disable-warmstarts',
213     help="Disable warm-start of scenario sub-problem solves in PH iterations >= 1. Default is False.",
214     action="store_true",
215     dest="disable_warmstarts",
216     default=False)
217
218   postprocessOpts.add_option('--ef-output-file',
219     help="The name of the extensive form output file (currently only LP format is supported), if writing of the extensive form is enabled. Default is efout.lp.",
220     action="store",
221     dest="ef_output_file",
222     type="string",
223     default="efout.lp")
224   postprocessOpts.add_option('--solve-ef',
225     help="Following write of the extensive form model, solve it.",
226     action="store_true",
227     dest="solve_ef",
228     default=False)
229   postprocessOpts.add_option('--ef-mipgap',
230     help="Specifies the mipgap for the EF solve",
231     action="store",
232     dest="ef_mipgap",
233     type="float",
234     default=None)
235   postprocessOpts.add_option('--ef-solver-options',
236     help="Solver options for the extension form problem",
237     action="append",
238     dest="ef_solver_options",
239     type="string",
240     default=[])   
241   postprocessOpts.add_option('--output-ef-solver-log',
242     help="Output solver log during the extensive form solve",
243     action="store_true",
244     dest="output_ef_solver_log",
245     default=False)
246   
247   outputOpts.add_option('--output-scenario-tree-solution',
248     help="Report the full solution (even leaves) in scenario tree format upon termination. Values represent averages, so convergence is not an issue. Default is False.",
249     action="store_true",
250     dest="output_scenario_tree_solution",
251     default=False)
252   outputOpts.add_option('--output-solver-logs',
253     help="Output solver logs during scenario sub-problem solves",
254     action="store_true",
255     dest="output_solver_logs",
256     default=False)
257   outputOpts.add_option('--output-solver-results',
258     help="Output solutions obtained after each scenario sub-problem solve",
259     action="store_true",
260     dest="output_solver_results",
261     default=False)
262   outputOpts.add_option('--output-times',
263     help="Output timing statistics for various PH components",
264     action="store_true",
265     dest="output_times",
266     default=False)
267   outputOpts.add_option('--report-only-statistics',
268     help="When reporting solutions (if enabled), only output per-variable statistics - not the individual scenario values. Default is False.",
269     action="store_true",
270     dest="report_only_statistics",
271     default=False)
272   outputOpts.add_option('--report-solutions',
273     help="Always report PH solutions after each iteration. Enabled if --verbose is enabled. Default is False.",
274     action="store_true",
275     dest="report_solutions",
276     default=False)
277   outputOpts.add_option('--report-weights',
278     help="Always report PH weights prior to each iteration. Enabled if --verbose is enabled. Default is False.",
279     action="store_true",
280     dest="report_weights",
281     default=False)
282   outputOpts.add_option('--restore-from-checkpoint',
283     help="The name of the checkpoint file from which PH should be initialized. Default is \"\", indicating no checkpoint restoration",
284     action="store",
285     dest="restore_from_checkpoint",
286     type="string",
287     default="")
288   outputOpts.add_option('--solution-writer',
289     help="The plugin invoked to write the scenario tree solution. Defaults to the empty list.",
290     action="append",
291     dest="solution_writer",
292     type="string",
293     default = [])
294   outputOpts.add_option('--suppress-continuous-variable-output',
295     help="Eliminate PH-related output involving continuous variables.",
296     action="store_true",
297     dest="suppress_continuous_variable_output",
298     default=False)
299   outputOpts.add_option('--verbose',
300     help="Generate verbose output for both initialization and execution. Default is False.",
301     action="store_true",
302     dest="verbose",
303     default=False)
304   outputOpts.add_option('--write-ef',
305     help="Upon termination, write the extensive form of the model - accounting for all fixed variables.",
306     action="store_true",
307     dest="write_ef",
308     default=False)
309
310   otherOpts.add_option('--disable-gc',
311     help="Disable the python garbage collecter. Default is False.",
312     action="store_true",
313     dest="disable_gc",
314     default=False)
315   otherOpts.add_option('--keep-solver-files',
316     help="Retain temporary input and output files for scenario sub-problem solves",
317     action="store_true",
318     dest="keep_solver_files",
319     default=False)
320   otherOpts.add_option('--profile',
321     help="Enable profiling of Python code.  The value of this option is the number of functions that are summarized.",
322     action="store",
323     dest="profile",
324     type="int",
325     default=0)
326   otherOpts.add_option('--checkpoint-interval',
327     help="The number of iterations between writing of a checkpoint file. Default is 0, indicating never.",
328     action="store",
329     dest="checkpoint_interval",
330     type="int",
331     default=0)
332   otherOpts.add_option('--traceback',
333     help="When an exception is thrown, show the entire call stack. Ignored if profiling is enabled. Default is False.",
334     action="store_true",
335     dest="traceback",
336     default=False)   
337
338   return parser
339
340#
341# Create the reference model / instance and scenario tree instance for PH.
342# IMPT: This method should be moved into a more generic module - it has nothing
343#       to do with PH, and is used elsewhere (by routines that shouldn't have
344#       to know about PH).
345#
346
347def load_reference_and_scenario_models(options):
348
349   #
350   # create and populate the reference model/instance pair.
351   #
352
353   reference_model = None
354   reference_instance = None
355
356   try:
357      reference_model_filename = os.path.expanduser(options.model_directory)+os.sep+"ReferenceModel.py"
358      if options.verbose is True:
359         print "Scenario reference model filename="+reference_model_filename
360      model_import = pyutilib.misc.import_file(reference_model_filename)
361      if "model" not in dir(model_import):
362         print ""
363         print "***ERROR: Exiting test driver: No 'model' object created in module "+reference_model_filename
364         return None, None, None, None
365
366      if model_import.model is None:
367         print ""
368         print "***ERROR: Exiting test driver: 'model' object equals 'None' in module "+reference_model_filename
369         return None, None, None, None
370
371      reference_model = model_import.model
372   except IOError:
373      print "***ERROR: Failed to load scenario reference model from file="+reference_model_filename
374      return None, None, None, None
375
376   try:
377      reference_instance_filename = os.path.expanduser(options.instance_directory)+os.sep+"ReferenceModel.dat"
378      if options.verbose is True:
379         print "Scenario reference instance filename="+reference_instance_filename
380      reference_instance = reference_model.create(reference_instance_filename, preprocess=False, simplify=options.simplify_expressions)
381      # IMPT: disable canonical representation construction for ASL solvers.
382      #       this is a hack, in that we need to address encodings and
383      #       the like at a more general level.
384      if options.solver_type == "asl":
385         reference_instance.skip_canonical_repn = True
386      else:
387         reference_instance.preprocess()
388     
389   except IOError:
390      print "***ERROR: Failed to load scenario reference instance data from file="+reference_instance_filename
391      return None, None, None, None
392
393   #
394   # create and populate the scenario tree model
395   #
396
397   from coopr.pysp.util.scenariomodels import scenario_tree_model
398   scenario_tree_instance = None
399
400   try:
401      scenario_tree_instance_filename = os.path.expanduser(options.instance_directory)+os.sep+"ScenarioStructure.dat"
402      if options.verbose is True:
403         print "Scenario tree instance filename="+scenario_tree_instance_filename
404      scenario_tree_instance = scenario_tree_model.create(scenario_tree_instance_filename, simplify=options.simplify_expressions)
405   except IOError:
406      print "***ERROR: Failed to load scenario tree reference instance data from file="+scenario_tree_instance_filename
407      return None, None, None, None
408
409   #
410   # construct the scenario tree
411   #
412   scenario_tree = ScenarioTree(scenarioinstance=reference_instance,
413                                scenariotreeinstance=scenario_tree_instance)
414
415   return reference_model, reference_instance, scenario_tree, scenario_tree_instance
416
417#
418# Create a PH object from a (pickl) checkpoint. Experimental at the moment.
419#
420def create_ph_from_checkpoint(options):
421
422   # we need to load the reference model, as pickle doesn't save contents of .py files!
423   try:
424      reference_model_filename = os.path.expanduser(options.model_directory)+os.sep+"ReferenceModel.py"
425      if options.verbose is True:
426         print "Scenario reference model filename="+reference_model_filename
427      model_import = pyutilib.misc.import_file(reference_model_filename)
428      if "model" not in dir(model_import):
429         print "***ERROR: Exiting test driver: No 'model' object created in module "+reference_model_filename
430         return
431
432      if model_import.model is None:
433         print "***ERROR: Exiting test driver: 'model' object equals 'None' in module "+reference_model_filename
434         return None
435
436      reference_model = model_import.model
437   except IOError:
438      print "***ERROR: Failed to load scenario reference model from file="+reference_model_filename
439      return None
440
441   # import the saved state
442
443   try:
444      checkpoint_file = open(options.restore_from_checkpoint,"r")
445      ph = pickle.load(checkpoint_file)
446      checkpoint_file.close()
447
448   except IOError, msg:
449      raise RuntimeError, msg
450
451   # tell PH to build the right solver manager and solver TBD - AND PLUGINS, BUT LATER
452
453   raise RuntimeError, "Checkpoint restoration is not fully supported/tested yet!"
454
455   return ph
456
457#
458# Create a PH object from scratch.
459#
460
461def create_ph_from_scratch(options, reference_model, reference_instance, scenario_tree):
462
463   #
464   # print the input tree for validation/information purposes.
465   #
466   if options.verbose is True:
467      scenario_tree.pprint()
468
469   #
470   # validate the tree prior to doing anything serious
471   #
472   if scenario_tree.validate() is False:
473      print "***ERROR: Scenario tree is invalid****"
474      return None
475   else:
476      if options.verbose is True:
477         print "Scenario tree is valid!"
478
479   #
480   # if any of the ww extension configuration options are specified without the
481   # ww extension itself being enabled, halt and warn the user - this has led
482   # to confusion in the past, and will save user support time.
483   #
484   if len(options.ww_extension_cfgfile) > 0 and options.enable_ww_extensions is False:
485      print "***ERROR: A configuration file was specified for the WW extension module, but the WW extensions are not enabled!"
486      return None
487
488   if len(options.ww_extension_suffixfile) > 0 and options.enable_ww_extensions is False:
489      print "***ERROR: A suffix file was specified for the WW extension module, but the WW extensions are not enabled!"
490      return None
491
492   #
493   # if a breakpoint strategy is specified without linearization eanbled, halt and warn the user.
494   #
495   if (options.breakpoint_strategy > 0) and (options.linearize_nonbinary_penalty_terms == 0):
496      print "***ERROR: A breakpoint distribution strategy was specified, but linearization is not enabled!"
497      return None
498
499   #
500   # deal with any plugins. ww extension comes first currently, followed by an option user-defined plugin.
501   # order only matters if both are specified.
502   #
503   if options.enable_ww_extensions is True:
504
505      from coopr.pysp import wwphextension
506
507      plugin = ExtensionPoint(IPHExtension)
508      if len(options.ww_extension_cfgfile) > 0:
509         plugin.service()._configuration_filename = options.ww_extension_cfgfile
510      if len(options.ww_extension_suffixfile) > 0:
511         plugin.service()._suffix_filename = options.ww_extension_suffixfile
512
513   if options.user_defined_extension is not None:
514      print "Trying to import user-defined PH extension module="+options.user_defined_extension
515      # JPW removed the exception handling logic, as the module importer
516      # can raise a broad array of exceptions.
517      __import__(options.user_defined_extension)
518      print "Module successfully loaded"
519
520   #
521   # construct the convergence "computer" class.
522   #
523   converger = None
524   # go with the non-defaults first, and then with the default.
525   if options.enable_free_discrete_count_convergence is True:
526      converger = NumFixedDiscreteVarConvergence(convergence_threshold=options.free_discrete_count_threshold)
527   elif options.enable_normalized_termdiff_convergence is True:
528      converger = NormalizedTermDiffConvergence(convergence_threshold=options.termdiff_threshold)
529   else:
530      converger = TermDiffConvergence(convergence_threshold=options.termdiff_threshold)
531
532
533   #
534   # construct and initialize PH
535   #
536   ph = ProgressiveHedging(max_iterations=options.max_iterations, \
537                           rho=options.default_rho, \
538                           rho_setter=options.rho_cfgfile, \
539                           bounds_setter=options.bounds_cfgfile, \
540                           solver=options.solver_type, \
541                           solver_manager=options.solver_manager_type, \
542                           output_scenario_tree_solution=options.output_scenario_tree_solution, \
543                           scenario_solver_options=options.scenario_solver_options, \
544                           scenario_mipgap=options.scenario_mipgap, \
545                           keep_solver_files=options.keep_solver_files, \
546                           output_solver_log=options.output_solver_logs, \
547                           output_solver_results=options.output_solver_results, \
548                           verbose=options.verbose, \
549                           report_solutions=options.report_solutions, \
550                           report_weights=options.report_weights, \
551                           report_only_statistics=options.report_only_statistics, \
552                           output_times=options.output_times, \
553                           disable_warmstarts=options.disable_warmstarts,
554                           drop_proximal_terms=options.drop_proximal_terms,
555                           retain_quadratic_binary_terms=options.retain_quadratic_binary_terms, \
556                           linearize_nonbinary_penalty_terms=options.linearize_nonbinary_penalty_terms, \
557                           breakpoint_strategy=options.breakpoint_strategy, \
558                           checkpoint_interval=options.checkpoint_interval, \
559                           simplify_expressions=options.simplify_expressions)
560
561   ph.initialize(scenario_data_directory_name=os.path.expanduser(options.instance_directory), \
562                 model=reference_model, \
563                 model_instance=reference_instance, \
564                 scenario_tree=scenario_tree, \
565                 converger=converger, \
566                 linearize=options.linearize_expressions)
567
568   if options.suppress_continuous_variable_output is True:
569      ph._output_continuous_variable_stats = False # clutters up the screen, when we really only care about the binaries.
570
571   return ph
572
573
574
575
576#
577# Given a PH object, execute it and optionally solve the EF at the end.
578#
579
580def run_ph(options, ph):
581
582   #
583   # at this point, we have an initialized PH object by some means.
584   #
585   start_time = time.time()
586
587   #
588   # kick off the solve
589   #
590   ph.solve()
591
592   end_time = time.time()
593
594   print ""
595   print "Total PH execution time=%8.2f seconds" %(end_time - start_time)
596   print ""
597   if options.output_times is True:
598      ph.print_time_stats()
599
600   solution_writer_plugins = ExtensionPoint(ISolutionWriterExtension)
601   for plugin in solution_writer_plugins:
602      plugin.write(ph._scenario_tree, "ph")
603
604   # store the binding instance, if created, in order to load
605   # the solution back into the scenario tree.
606   binding_instance = None
607
608   #
609   # write the extensive form, accounting (implicitly) for any fixed variables.
610   #
611   if (options.write_ef is True) or (options.solve_ef is True):
612      print ""
613      print "Writing EF for remainder problem"
614      print ""
615      binding_instance = create_and_write_ef(ph._scenario_tree, ph._instances, os.path.expanduser(options.ef_output_file))
616
617   #
618   # solve the extensive form and load the solution back into the PH scenario tree.
619   # contents from the PH solve will obviously be over-written!
620   #
621   if options.solve_ef is True:
622      print ""
623      print "Solving extensive form written to file="+os.path.expanduser(options.ef_output_file)
624      print ""
625
626      ef_solver = SolverFactory(options.solver_type)
627      if ef_solver is None:
628         raise ValueError, "Failed to create solver of type="+options.solver_type+" for use in extensive form solve"
629      if len(options.ef_solver_options) > 0:
630         print "Initializing ef solver with options="+str(options.ef_solver_options)
631         ef_solver.set_options("".join(options.ef_solver_options))
632      if options.ef_mipgap is not None:
633         if (options.ef_mipgap < 0.0) or (options.ef_mipgap > 1.0):
634            raise ValueError, "Value of the mipgap parameter for the EF solve must be on the unit interval; value specified=" + `options.ef_mipgap`
635         else:
636            ef_solver.mipgap = options.ef_mipgap
637
638      ef_solver_manager = SolverManagerFactory(options.solver_manager_type)
639      if ef_solver is None:
640         raise ValueError, "Failed to create solver manager of type="+options.solver_type+" for use in extensive form solve"
641
642      print "Queuing extensive form solve"
643      ef_action_handle = ef_solver_manager.queue(os.path.expanduser(options.ef_output_file), opt=ef_solver, tee=options.output_ef_solver_log)
644      print "Waiting for extensive form solve"
645      ef_results = ef_solver_manager.wait_for(ef_action_handle)
646
647      load_ef_solution(ef_results, binding_instance, ph._instances)
648      ph._scenario_tree.snapshotSolutionFromInstances(ph._instances)
649
650      print ""
651      print "Extensive form solution:"
652      ph._scenario_tree.pprintSolution()
653      print ""
654      print "Extensive form costs:"
655      ph._scenario_tree.pprintCosts(ph._instances)
656
657      solution_writer_plugins = ExtensionPoint(ISolutionWriterExtension)
658      for plugin in solution_writer_plugins:
659         plugin.write(ph._scenario_tree, "postphef")
660
661#
662# The main PH initialization / runner routine. Really only branches based on
663# the construction source - a checkpoint or from scratch.
664#
665
666def exec_ph(options):
667
668   ph = None
669
670   # validate the solution writer plugin exists, to avoid a lot of wasted work.
671   for solution_writer_name in options.solution_writer:
672      print "Trying to import solution writer="+solution_writer_name
673      __import__(solution_writer_name)
674      print "Module successfully loaded"
675
676   # if we are restoring from a checkpoint file, do so - otherwise, construct PH from scratch.
677   if len(options.restore_from_checkpoint) > 0:
678      ph = create_ph_from_checkpoint(options)
679
680   else:
681      reference_model, reference_instance, scenario_tree, scenario_tree_instance = load_reference_and_scenario_models(options)
682      if reference_model is None or reference_instance is None or scenario_tree is None:
683         return
684      ph = create_ph_from_scratch(options, reference_model, reference_instance, scenario_tree)
685
686   if ph is None:
687      print "***FAILED TO CREATE PH OBJECT"
688      return
689
690   run_ph(options, ph)
691
692#
693# the main driver routine for the runph script.
694#
695
696def run(args=None):
697
698    #
699    # Top-level command that executes the extensive form writer.
700    # This is segregated from run_ef_writer to enable profiling.
701    #
702
703    #
704    # Parse command-line options.
705    #
706    try:
707       ph_options_parser = construct_ph_options_parser("runph [options]")
708       (options, args) = ph_options_parser.parse_args(args=args)
709    except SystemExit:
710       # the parser throws a system exit if "-h" is specified - catch
711       # it to exit gracefully.
712       return
713    #
714    # Control the garbage collector - more critical than I would like at the moment.
715    #
716
717    if options.disable_gc is True:
718       gc.disable()
719    else:
720       gc.enable()
721
722    #
723    # Run PH - precise invocation depends on whether we want profiling output.
724    #
725
726    # if an exception is triggered and traceback is enabled, 'ans' won't
727    # have a value and the return statement from this function will flag
728    # an error, masking the stack trace that you really want to see.
729    ans = None       
730
731    if options.profile > 0:
732        #
733        # Call the main PH routine with profiling.
734        #
735        tfile = pyutilib.services.TempfileManager.create_tempfile(suffix=".profile")
736        tmp = profile.runctx('exec_ph(options)',globals(),locals(),tfile)
737        p = pstats.Stats(tfile).strip_dirs()
738        p.sort_stats('time', 'cum')
739        p = p.print_stats(options.profile)
740        p.print_callers(options.profile)
741        p.print_callees(options.profile)
742        p = p.sort_stats('cum','calls')
743        p.print_stats(options.profile)
744        p.print_callers(options.profile)
745        p.print_callees(options.profile)
746        p = p.sort_stats('calls')
747        p.print_stats(options.profile)
748        p.print_callers(options.profile)
749        p.print_callees(options.profile)
750        pyutilib.services.TempfileManager.clear_tempfiles()
751        ans = [tmp, None]
752    else:
753        #
754        # Call the main PH routine without profiling.
755        #
756
757        if options.traceback is True:
758           ans = exec_ph(options)
759        else:
760           try:
761              ans = exec_ph(options)
762           except ValueError, str:
763              print "VALUE ERROR:"
764              print str
765           except TypeError, str:
766              print "TYPE ERROR:"
767              print str
768           except NameError, str:
769              print "NAME ERROR:"
770              print str                           
771           except IOError, str:
772              print "IO ERROR:"
773              print str
774           except pyutilib.common.ApplicationError, str:
775              print "APPLICATION ERROR:"
776              print str
777           except RuntimeError, str:
778              print "RUN-TIME ERROR:"   
779              print str       
780           except:
781              print "Encountered unhandled exception"
782              traceback.print_exc()
783
784    gc.enable()
785
786    return ans
Note: See TracBrowser for help on using the repository browser.