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

Last change on this file since 3107 was 3107, checked in by jwatson, 10 years ago

Minor touch-ups relating to traceback facilities.

  • Property svn:executable set to *
File size: 11.7 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, OptionGroup
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.usage=usage_string
42
43   inputOpts  = OptionGroup( parser, 'Input Options' )
44   efOpts     = OptionGroup( parser, 'EF Options' )
45   solverOpts = OptionGroup( parser, 'Solver Options' )
46   outputOpts = OptionGroup( parser, 'Output Options' )
47   otherOpts  = OptionGroup( parser, 'Other Options' )
48   parser.add_option_group( inputOpts )
49   parser.add_option_group( efOpts )
50   parser.add_option_group( solverOpts )
51   parser.add_option_group( outputOpts )
52   parser.add_option_group( otherOpts )
53
54   inputOpts.add_option('-i','--instance-directory',
55     help='The directory in which all instance (reference and scenario) definitions are stored. Default is ".".',
56     action='store',
57     dest='instance_directory',
58     type='string',
59     default='.')
60   inputOpts.add_option('-m','--model-directory',
61     help='The directory in which all model (reference and scenario) definitions are stored. Default is ".".',
62     action='store',
63     dest='model_directory',
64     type='string',
65     default='.')
66
67   efOpts.add_option('--cvar-weight',
68     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.',
69     action='store',
70     dest='cvar_weight',
71     type='float',
72     default=1.0)
73   efOpts.add_option('--generate-weighted-cvar',
74     help='Add a weighted CVaR term to the primary objective',
75     action='store_true',
76     dest='generate_weighted_cvar',
77     default=False)
78   efOpts.add_option('--risk-alpha',
79     help='The probability threshold associated with cvar (or any future) risk-oriented performance metrics. Default is 0.95.',
80     action='store',
81     dest='risk_alpha',
82     type='float',
83     default=0.95)
84
85   solverOpts.add_option('--mipgap',
86     help='Specifies the mipgap for the EF solve.',
87     action='store',
88     dest='mipgap',
89     type='float',
90     default=None)
91   solverOpts.add_option('--solve',
92     help='Following write of the extensive form model, solve it.',
93     action='store_true',
94     dest='solve_ef',
95     default=False)
96   solverOpts.add_option('--solver',
97     help='The type of solver used to solve scenario sub-problems. Default is cplex.',
98     action='store',
99     dest='solver_type',
100     type='string',
101     default='cplex')
102   solverOpts.add_option('--solver-manager',
103     help='The type of solver manager used to coordinate scenario sub-problem solves. Default is serial.',
104     action='store',
105     dest='solver_manager_type',
106     type='string',
107     default='serial')
108   solverOpts.add_option('--solver-options',
109     help='Solver options for the extension form problem.',
110     action='append',
111     dest='solver_options',
112     type='string',
113     default=[])
114
115   outputOpts.add_option('--output-file',
116     help='Specify the name of the extensive form output file',
117     action='store',
118     dest='output_file',
119     type='string',
120     default='efout.lp')
121   outputOpts.add_option('--output-solver-log',
122     help='Output solver log during the extensive form solve.',
123     action='store_true',
124     dest='output_solver_log',
125     default=False)
126   outputOpts.add_option('--solution-writer',
127     help='The plugin invoked to write the scenario tree solution. Defaults to the empty list.',
128     action='append',
129     dest='solution_writer',
130     type='string',
131     default = [])
132   outputOpts.add_option('--verbose',
133     help='Generate verbose output, beyond the usual status output. Default is False.',
134     action='store_true',
135     dest='verbose',
136     default=False)
137
138   otherOpts.add_option('--disable-gc',
139     help='Disable the python garbage collecter. Default is False.',
140     action='store_true',
141     dest='disable_gc',
142     default=False)
143   otherOpts.add_option('--keep-solver-files',
144     help='Retain temporary input and output files for solve.',
145     action='store_true',
146     dest='keep_solver_files',
147     default=False)
148   otherOpts.add_option('--profile',
149     help='Enable profiling of Python code.  The value of this option is the number of functions that are summarized.',
150     action='store',
151     dest='profile',
152     default=0)
153   otherOpts.add_option('--traceback',
154     help='When an exception is thrown, show the entire call stack. Ignored if profiling is enabled. Default is False.',
155     action='store_true',
156     dest='traceback',
157     default=False)
158
159   return parser
160
161
162def run_ef_writer(options, args):
163
164   # if the user enabled the addition of the weighted cvar term to the objective,
165   # then validate the associated parameters.
166   generate_weighted_cvar = False
167   cvar_weight = None
168   risk_alpha = None
169
170   if options.generate_weighted_cvar is True:
171
172      generate_weighted_cvar = True
173      cvar_weight = options.cvar_weight
174      risk_alpha = options.risk_alpha
175
176   # validate the solution writer plugin exists, to avoid a lot of wasted work.
177   for solution_writer_name in options.solution_writer:
178      print "Trying to import solution writer="+solution_writer_name
179      __import__(solution_writer_name)
180      print "Module successfully loaded"
181
182   scenario_tree, binding_instance, scenario_instances = write_ef_from_scratch(os.path.expanduser(options.model_directory),
183                                                                               os.path.expanduser(options.instance_directory),
184                                                                               os.path.expanduser(options.output_file),
185                                                                               options.verbose,
186                                                                               generate_weighted_cvar, cvar_weight, risk_alpha)
187
188   if (scenario_tree is None) or (binding_instance is None) or (scenario_instances is None):
189      raise RuntimeError, "Failed to write extensive form."
190
191   if options.solve_ef is True:
192
193      ef_solver = SolverFactory(options.solver_type)
194      if ef_solver is None:
195         raise ValueError, "Failed to create solver of type="+options.solver_type+" for use in extensive form solve"
196      if len(options.solver_options) > 0:
197         print "Initializing ef solver with options="+str(options.solver_options)
198         ef_solver.set_options("".join(options.solver_options))
199      if options.mipgap is not None:
200         if (options.mipgap < 0.0) or (options.mipgap > 1.0):
201            raise ValueError, "Value of the mipgap parameter for the EF solve must be on the unit interval; value specified=" + `options.mipgap`
202         else:
203            ef_solver.mipgap = options.mipgap
204      if options.keep_solver_files is True:
205         ef_solver.keepFiles = True
206
207      ef_solver_manager = SolverManagerFactory(options.solver_manager_type)
208      if ef_solver is None:
209         raise ValueError, "Failed to create solver manager of type="+options.solver_type+" for use in extensive form solve"
210
211      # at this point you have a specific solver - communicate solver capabilities
212      # to the writer via the instance.
213      binding_instance.has_capability = ef_solver.has_capability
214      for scenario_name, scenario_instance in scenario_instances.items():
215         scenario_instance.has_capability = ef_solver.has_capability
216
217      print "Queuing extensive form solve"
218      ef_action_handle = ef_solver_manager.queue(os.path.expanduser(options.output_file), opt=ef_solver, tee=options.output_solver_log)
219      print "Waiting for extensive form solve"
220      ef_results = ef_solver_manager.wait_for(ef_action_handle)
221      load_ef_solution(ef_results, binding_instance, scenario_instances)
222      scenario_tree.snapshotSolutionFromInstances(scenario_instances)
223
224      # handle output of solution from the scenario tree.
225      print ""
226      print "Extensive form solution:"
227      scenario_tree.pprintSolution()
228      print ""
229      print "Extensive form costs:"
230      scenario_tree.pprintCosts(scenario_instances)
231
232      solution_writer_plugins = ExtensionPoint(ISolutionWriterExtension)
233      for plugin in solution_writer_plugins:
234         plugin.write(scenario_tree, "ef")
235
236def run(args=None):
237
238    #
239    # Top-level command that executes the extensive form writer.
240    # This is segregated from run_ef_writer to enable profiling.
241    #
242
243    #
244    # Parse command-line options.
245    #
246    try:
247       options_parser = construct_ef_writer_options_parser("runef [options]")
248       (options, args) = options_parser.parse_args(args=args)
249    except SystemExit:
250       # the parser throws a system exit if "-h" is specified - catch
251       # it to exit gracefully.
252       return
253
254    if options.disable_gc is True:
255       gc.disable()
256    else:
257       gc.enable()
258
259    # if an exception is triggered and traceback is enabled, 'ans' won't
260    # have a value and the return statement from this function will flag
261    # an error, masking the stack trace that you really want to see.
262    ans = None
263
264    if options.profile > 0:
265        #
266        # Call the main ef writer with profiling.
267        #
268        tfile = pyutilib.services.TempfileManager.create_tempfile(suffix=".profile")
269        tmp = profile.runctx('run_ef_writer(options,args)',globals(),locals(),tfile)
270        p = pstats.Stats(tfile).strip_dirs()
271        p.sort_stats('time', 'cum')
272        options.profile = eval(options.profile)
273        p = p.print_stats(options.profile)
274        p.print_callers(options.profile)
275        p.print_callees(options.profile)
276        p = p.sort_stats('cum','calls')
277        p.print_stats(options.profile)
278        p.print_callers(options.profile)
279        p.print_callees(options.profile)
280        p = p.sort_stats('calls')
281        p.print_stats(options.profile)
282        p.print_callers(options.profile)
283        p.print_callees(options.profile)
284        pyutilib.services.TempfileManager.clear_tempfiles()
285        ans = [tmp, None]
286    else:
287        #
288        # Call the main EF writer without profiling.
289        #
290        if options.traceback is True:
291           ans = run_ef_writer(options, args)
292        else:
293           errmsg = None
294           try:
295              ans = run_ef_writer(options, args)
296           except ValueError, err:
297              errmsg = 'VALUE ERROR: %s' % err
298           except TypeError, err:
299              errmsg = 'TYPE ERROR: %s' % err
300           except NameError, err:
301              errmsg = 'NAME ERROR: %s' % err
302           except IOError, err:
303              errmsg = 'I/O ERROR: %s' % err
304           except RuntimeError, err:
305              errmsg = 'RUN-TIME ERROR: %s' % err
306           except pyutilib.common.ApplicationError, err:
307              errmsg = 'APPLICATION ERROR: %s' % err
308           except Exception, err:
309              errmsg = 'UNKNOWN ERROR: %s' % err
310              traceback.print_exc()
311
312           if errmsg is not None:
313              print >>sys.stderr, errmsg
314
315    gc.enable()
316
317    return ans
318
Note: See TracBrowser for help on using the repository browser.