source: coopr.pysp/trunk/coopr/pysp/ef_writer_script.py @ 2990

Last change on this file since 2990 was 2990, checked in by jwatson, 11 years ago

Added option to PySP scripts to allow for output of the scenario tree solutions in CSV file format. Option is --solution-writer=coopr.pysp.csvsolutionwriter.py. This also required some overdue refactoring of the PySP code, plus re-work of how the post-PH extensive form solution is displayed.

  • Property svn:executable set to *
File size: 11.1 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 sys
13import os
14from optparse import OptionParser
15
16import pyutilib.services
17import textwrap
18import traceback
19try:
20    import cProfile as profile
21except ImportError:
22    import profile
23import pstats
24import gc
25
26from coopr.pysp.ef import *
27
28from coopr.opt.base import SolverFactory
29from coopr.opt.parallel import SolverManagerFactory
30
31from pyutilib.component.core import ExtensionPoint
32from coopr.pysp.solutionwriter import ISolutionWriterExtension
33
34#
35# utility method to construct an option parser for ef writer arguments
36#
37
38def construct_ef_writer_options_parser(usage_string):
39
40   parser = OptionParser()
41   parser.add_option("--verbose",
42                     help="Generate verbose output, beyond the usual status output. Default is False.",
43                     action="store_true",
44                     dest="verbose",
45                     default=False)
46   parser.add_option("--model-directory",
47                     help="The directory in which all model (reference and scenario) definitions are stored. Default is \".\".",
48                     action="store",
49                     dest="model_directory",
50                     type="string",
51                     default=".")
52   parser.add_option("--instance-directory",
53                     help="The directory in which all instance (reference and scenario) definitions are stored. Default is \".\".",
54                     action="store",
55                     dest="instance_directory",
56                     type="string",
57                     default=".")
58   parser.add_option("--generate-weighted-cvar",
59                     help="Add a weighted CVaR term to the primary objective",
60                     action="store_true",
61                     dest="generate_weighted_cvar",
62                     default=False)
63   parser.add_option("--cvar-weight",
64                     help="The weight associated with the CVaR term in the risk-weighted objective formulation. Default is 1.0. If the weight is 0, then *only* a non-weighted CVaR cost will appear in the EF objective - the expected cost component will be dropped.",
65                     action="store",
66                     dest="cvar_weight",
67                     type="float",
68                     default=1.0)
69   parser.add_option("--risk-alpha",
70                     help="The probability threshold associated with cvar (or any future) risk-oriented performance metrics. Default is 0.95.",
71                     action="store",
72                     dest="risk_alpha",
73                     type="float",
74                     default=0.95)
75   parser.add_option("--output-file",
76                     help="Specify the name of the extensive form output file",
77                     action="store",
78                     dest="output_file",
79                     type="string",
80                     default="efout.lp")
81   parser.add_option("--solve",
82                     help="Following write of the extensive form model, solve it.",
83                     action="store_true",
84                     dest="solve_ef",
85                     default=False)
86   parser.add_option("--solver",
87                     help="The type of solver used to solve scenario sub-problems. Default is cplex.",
88                     action="store",
89                     dest="solver_type",
90                     type="string",
91                     default="cplex")
92   parser.add_option("--solver-manager",
93                     help="The type of solver manager used to coordinate scenario sub-problem solves. Default is serial.",
94                     action="store",
95                     dest="solver_manager_type",
96                     type="string",
97                     default="serial")
98   parser.add_option("--solver-options",
99                     help="Solver options for the extension form problem.",
100                     action="append",
101                     dest="solver_options",
102                     type="string",
103                     default=[])
104   parser.add_option("--mipgap",
105                     help="Specifies the mipgap for the EF solve.",
106                     action="store",
107                     dest="mipgap",
108                     type="float",
109                     default=None)   
110   parser.add_option("--solution-writer",
111                     help="The plugin invoked to write the scenario tree solution. Defaults to the empty list.",
112                     action="append",
113                     dest="solution_writer",
114                     type="string",
115                     default = [])
116   parser.add_option("--output-solver-log",
117                     help="Output solver log during the extensive form solve.",
118                     action="store_true",
119                     dest="output_solver_log",
120                     default=False)
121   parser.add_option("--keep-solver-files",
122                     help="Retain temporary input and output files for solve.",
123                     action="store_true",
124                     dest="keep_solver_files",
125                     default=False)   
126   parser.add_option("--profile",
127                     help="Enable profiling of Python code.  The value of this option is the number of functions that are summarized.",
128                     action="store",
129                     dest="profile",
130                     default=0)
131   parser.add_option("--disable-gc",
132                     help="Disable the python garbage collecter. Default is False.",
133                     action="store_true",
134                     dest="disable_gc",
135                     default=False)
136   parser.usage=usage_string
137
138   return parser
139   
140def run_ef_writer(options, args):
141
142   # if the user enabled the addition of the weighted cvar term to the objective,
143   # then validate the associated parameters.
144   generate_weighted_cvar = False
145   cvar_weight = None
146   risk_alpha = None
147
148   if options.generate_weighted_cvar is True:
149
150      generate_weighted_cvar = True
151      cvar_weight = options.cvar_weight
152      risk_alpha = options.risk_alpha
153
154   # validate the solution writer plugin exists, to avoid a lot of wasted work.
155   for solution_writer_name in options.solution_writer:
156      print "Trying to import solution writer="+solution_writer_name
157      __import__(solution_writer_name)
158      print "Module successfully loaded"
159
160   scenario_tree, binding_instance, scenario_instances = write_ef_from_scratch(os.path.expanduser(options.model_directory),
161                                                                               os.path.expanduser(options.instance_directory),
162                                                                               os.path.expanduser(options.output_file),
163                                                                               options.verbose,
164                                                                               generate_weighted_cvar, cvar_weight, risk_alpha)
165
166   if (scenario_tree is None) or (binding_instance is None) or (scenario_instances is None):
167      raise RuntimeError, "Failed to write extensive form."     
168
169   if options.solve_ef is True:
170
171      ef_solver = SolverFactory(options.solver_type)
172      if ef_solver is None:
173         raise ValueError, "Failed to create solver of type="+options.solver_type+" for use in extensive form solve"
174      if len(options.solver_options) > 0:
175         print "Initializing ef solver with options="+str(options.solver_options)         
176         ef_solver.set_options("".join(options.solver_options))
177      if options.mipgap is not None:
178         if (options.mipgap < 0.0) or (options.mipgap > 1.0):
179            raise ValueError, "Value of the mipgap parameter for the EF solve must be on the unit interval; value specified=" + `options.mipgap`
180         else:
181            ef_solver.mipgap = options.mipgap
182      if options.keep_solver_files is True:
183         ef_solver.keepFiles = True         
184
185      ef_solver_manager = SolverManagerFactory(options.solver_manager_type)
186      if ef_solver is None:
187         raise ValueError, "Failed to create solver manager of type="+options.solver_type+" for use in extensive form solve"
188
189      # at this point you have a specific solver - communicate solver capabilities
190      # to the writer via the instance.
191      binding_instance.has_capability = ef_solver.has_capability
192      for scenario_name, scenario_instance in scenario_instances.items():
193         scenario_instance.has_capability = ef_solver.has_capability
194
195      print "Queuing extensive form solve"
196      ef_action_handle = ef_solver_manager.queue(os.path.expanduser(options.output_file), opt=ef_solver, tee=options.output_solver_log)
197      print "Waiting for extensive form solve"
198      ef_results = ef_solver_manager.wait_for(ef_action_handle)
199      load_ef_solution(ef_results, binding_instance, scenario_instances)
200      scenario_tree.snapshotSolutionFromInstances(scenario_instances)
201
202      # handle output of solution from the scenario tree.
203      print ""
204      print "Extensive form solution:"
205      scenario_tree.pprintSolution()
206      print ""
207      print "Extensive form costs:"
208      scenario_tree.pprintCosts(scenario_instances)
209
210      solution_writer_plugins = ExtensionPoint(ISolutionWriterExtension)
211      for plugin in solution_writer_plugins:
212         plugin.write(scenario_tree, "ef")
213
214def run(args=None):
215
216    #
217    # Top-level command that executes the extensive form writer.
218    # This is segregated from run_ef_writer to enable profiling.
219    #
220
221    #
222    # Parse command-line options.
223    #
224    try:
225       options_parser = construct_ef_writer_options_parser("runef [options]")
226       (options, args) = options_parser.parse_args(args=args)
227    except SystemExit:
228       # the parser throws a system exit if "-h" is specified - catch
229       # it to exit gracefully.
230       return
231
232    if options.disable_gc is True:
233       gc.disable()
234    else:
235       gc.enable()
236
237    if options.profile > 0:
238        #
239        # Call the main ef writer with profiling.
240        #
241        tfile = pyutilib.services.TempfileManager.create_tempfile(suffix=".profile")
242        tmp = profile.runctx('run_ef_writer(options,args)',globals(),locals(),tfile)
243        p = pstats.Stats(tfile).strip_dirs()
244        p.sort_stats('time', 'cum')
245        options.profile = eval(options.profile)
246        p = p.print_stats(options.profile)
247        p.print_callers(options.profile)
248        p.print_callees(options.profile)
249        p = p.sort_stats('cum','calls')
250        p.print_stats(options.profile)
251        p.print_callers(options.profile)
252        p.print_callees(options.profile)
253        p = p.sort_stats('calls')
254        p.print_stats(options.profile)
255        p.print_callers(options.profile)
256        p.print_callees(options.profile)
257        pyutilib.services.TempfileManager.clear_tempfiles()
258        ans = [tmp, None]
259    else:
260        #
261        # Call the main EF writer without profiling.
262        #
263        ans = run_ef_writer(options, args)
264
265    gc.enable()
266   
267    return ans
268
Note: See TracBrowser for help on using the repository browser.