source: coopr.pysp/trunk/coopr/pysp/phinit.py @ 3482

Last change on this file since 3482 was 3482, checked in by khunter, 9 years ago

Dynamic solver list, minor import fixups.

Make the --help options show the list of available solvers, like pyomo --help.

Also some driveby option additions:

-k (--keep-solver-solutions)
-m (--model-directory)
-i (--instance-directory)

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