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

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

NFC: EOL whitespace removal

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