source: coopr.pysp/trunk/coopr/pysp/ef.py @ 3104

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

Fairly extensive re-work and simplification of the PySP extensive form writer, focusing on a cleaner implementation of the cvar term generation.

File size: 30.8 KB
Line 
1import pyutilib
2import sys
3import os
4import time
5import traceback
6import copy
7import gc
8
9from coopr.pysp.scenariotree import *
10from coopr.pysp.convergence import *
11from coopr.pysp.ph import *
12
13from coopr.pyomo.base import *
14from coopr.pyomo.io import *
15
16from coopr.pyomo.base.var import _VarValue, _VarBase
17
18from coopr.opt.results.solution import Solution
19
20#
21# brain-dead utility for determing if there is a binary to write in the
22# composite model - need to know this, because CPLEX doesn't like empty
23# binary blocks in the LP file.
24#
25
26def binaries_present(master_model, scenario_instances):
27
28   # check the master model first.
29   for var in master_model.active_components(Var).values():
30      if isinstance(var.domain, BooleanSet):
31         return True
32
33   # scan the scenario instances next.
34   for scenario_name in scenario_instances.keys():
35      scenario_instance = scenario_instances[scenario_name]
36      for var in scenario_instance.active_components(Var).values():
37         if isinstance(var.domain, BooleanSet):
38            return True
39
40   return False
41
42#
43# brain-dead utility for determing if there is a binary to write in the
44# composite model - need to know this, because CPLEX doesn't like empty
45# integer blocks in the LP file.
46#
47
48def integers_present(master_model, scenario_instances):
49
50   # check the master model first.
51   for var in master_model.active_components(Var).values():
52      if isinstance(var.domain, IntegerSet):
53         return True
54
55   # scan the scenario instances next.
56   for scenario_name in scenario_instances.keys():
57      scenario_instance = scenario_instances[scenario_name]
58      for var in scenario_instance.active_components(Var).values():
59         if isinstance(var.domain, IntegerSet):
60            return True
61
62   return False
63
64#
65# a routine to create the extensive form, given an input scenario tree and instances.
66# IMPT: unlike scenario instances, the extensive form instance is *not* self-contained.
67#       in particular, it has binding constraints that cross the binding instance and
68#       the scenario instances. it is up to the caller to keep track of which scenario
69#       instances are associated with the extensive form. this might be something we
70#       encapsulate at some later time.
71# NOTE: if cvar terms are generated, then the input scenario tree is modified accordingly,
72#       i.e., with the addition of the "eta" variable at the root node and the excess
73#       variables at (for lack of a better place - they are per-scenario, but are not
74#       blended) the second stage.
75#
76
77def create_ef_instance(scenario_tree, scenario_instances,
78                       verbose_output = False,
79                       generate_weighted_cvar = False, cvar_weight = None, risk_alpha = None):
80
81   #
82   # validate cvar options, if specified.
83   #
84   if generate_weighted_cvar is True:
85      if (cvar_weight is None) or (cvar_weight < 0.0):
86         raise RuntimeError, "Weight of CVaR term must be >= 0.0 - value supplied="+str(cvar_weight)
87      if (risk_alpha is None) or (risk_alpha <= 0.0) or (risk_alpha >= 1.0):
88         raise RuntimeError, "CVaR risk alpha must be between 0 and 1, exclusive - value supplied="+str(risk_alpha)
89
90      if verbose_output is True:
91         print "Writing CVaR weighted objective"
92         print "CVaR term weight="+str(cvar_weight)
93         print "CVaR alpha="+str(risk_alpha)
94         print ""
95
96      # update the scenario tree with cvar-specific variable names, so
97      # they will get cloned accordingly in the master instance.
98      first_stage = scenario_tree._stages[0]
99      second_stage = scenario_tree._stages[1]
100      root_node = first_stage._tree_nodes[0]
101
102      # NOTE: because we currently don't have access to the reference
103      #       instance in this method, temporarily (and largely orphaned)
104      #       variables are constructed to supply to the scenario tree.
105      #       this decision should be ultimately revisited.
106      cvar_eta_variable_name = "CVAR_ETA"
107      cvar_eta_variable = Var(name=cvar_eta_variable_name)
108      cvar_eta_variable.construct()               
109
110      first_stage.add_variable(cvar_eta_variable, "*", [None])
111
112      cvar_excess_variable_name = "CVAR_EXCESS"
113      cvar_excess_variable = Var(name=cvar_excess_variable_name)
114      cvar_excess_variable.construct()
115
116      second_stage.add_variable(cvar_excess_variable, "*", [None])
117
118      # create the eta and excess variable on a per-scenario basis,
119      # in addition to the constraint relating to the two.
120      for scenario_name, scenario_instance in scenario_instances.items():
121
122         cvar_excess_variable_name = "CVAR_EXCESS"
123         cvar_excess_variable = Var(name=cvar_excess_variable_name, domain=NonNegativeReals)
124         cvar_excess_variable.construct()         
125         setattr(scenario_instance, cvar_excess_variable_name, cvar_excess_variable)
126
127         cvar_eta_variable_name = "CVAR_ETA"
128         cvar_eta_variable = Var(name=cvar_eta_variable_name)
129         cvar_eta_variable.construct()         
130         setattr(scenario_instance, cvar_eta_variable_name, cvar_eta_variable)
131
132         compute_excess_constraint_name = "COMPUTE_SCENARIO_EXCESS"
133         compute_excess_constraint = Constraint(name=compute_excess_constraint_name)
134         compute_excess_expression = cvar_excess_variable
135         for node in scenario_tree._scenario_map[scenario_name]._node_list:
136            (cost_variable, cost_variable_idx) = node._stage._cost_variable
137            compute_excess_expression -= getattr(scenario_instance, cost_variable.name)[cost_variable_idx]
138         compute_excess_expression += cvar_eta_variable
139         compute_excess_constraint.add(None, (0.0, compute_excess_expression, None))
140         compute_excess_constraint._model = scenario_instance
141         setattr(scenario_instance, compute_excess_constraint_name, compute_excess_constraint)
142
143         # re-process the scenario instance due to the newly added constraints/variables associated
144         # with CVaR. a complete preprocess is overkill, of course - the correct approach would be
145         # to just preprocess those newly added variables and constraints.
146         scenario_instance.preprocess()
147
148   #
149   # create the new and empty binding instance.
150   #
151
152   binding_instance = Model()
153   binding_instance.name = "MASTER"
154
155   # walk the scenario tree - create variables representing the common values for all scenarios
156   # associated with that node, along with equality constraints to enforce non-anticipativity.
157   # also create expected cost variables for each node, to be computed via constraints/objectives
158   # defined in a subsequent pass. master variables are created for all nodes but those in the
159   # last stage. expected cost variables are, for no particularly good reason other than easy
160   # coding, created for nodes in all stages.
161   if verbose_output is True:
162      print "Creating variables for master binding instance"
163
164   for stage in scenario_tree._stages:
165
166      # first loop is to create master (blended) variables across all stages but the last.
167      for (stage_variable, index_template, stage_variable_indices) in stage._variables:
168
169         if verbose_output is True:
170            print "Creating master variable and blending constraints for decision variable="+stage_variable.name+", indices="+str(index_template)
171
172         for tree_node in stage._tree_nodes:
173
174            if stage != scenario_tree._stages[-1]:     
175
176               master_variable_name = stage_variable.name               
177
178               # because there may be a single stage variable and multiple indices, check
179               # for the existence of the variable at this node - if you don't, you'll
180               # inadvertently over-write what was there previously!
181               master_variable = None
182               try:
183                  master_variable = getattr(binding_instance, master_variable_name)
184               except:
185                  new_master_variable_index = stage_variable._index
186                  new_master_variable = None
187                  if (len(new_master_variable_index) is 1) and (None in new_master_variable_index):
188                     new_master_variable = Var(name=stage_variable.name)
189                  else:
190                     new_master_variable = Var(new_master_variable_index, name=stage_variable.name)
191                  new_master_variable.construct()
192                  new_master_variable._model = binding_instance
193                  setattr(binding_instance, master_variable_name, new_master_variable)
194
195                  master_variable = new_master_variable
196
197               # TBD: we should create an indexed variable here, and then add entries within the loop.
198               for index in stage_variable_indices:
199
200                  is_used = True # until proven otherwise                     
201                  for scenario in tree_node._scenarios:
202                     instance = scenario_instances[scenario._name]
203                     if getattr(instance,stage_variable.name)[index].status == VarStatus.unused:
204                        is_used = False
205
206                  is_fixed = False # until proven otherwise
207                  for scenario in tree_node._scenarios:
208                     instance = scenario_instances[scenario._name]
209                     if getattr(instance,stage_variable.name)[index].fixed is True:
210                        is_fixed = True
211
212                  if (is_used is True) and (is_fixed is False):
213                           
214                     for scenario in tree_node._scenarios:
215                        scenario_instance = scenario_instances[scenario._name]
216                        scenario_variable = getattr(scenario_instance, stage_variable.name)
217                        new_constraint_name = scenario._name + "_" + master_variable_name + "_" + str(index)
218                        new_constraint = Constraint(name=new_constraint_name)
219                        new_expr = master_variable[index] - scenario_variable[index]
220                        new_constraint.add(None, (0.0, new_expr, 0.0))
221                        new_constraint._model = binding_instance
222                        setattr(binding_instance, new_constraint_name, new_constraint)
223
224      # the second loop is for creating the stage cost variable in each tree node.
225      for tree_node in stage._tree_nodes:                       
226
227         # create a variable to represent the expected cost at this node -
228         # the constraint to compute this comes later.
229         expected_cost_variable_name = "EXPECTED_COST_" + tree_node._name
230         expected_cost_variable = Var(name=expected_cost_variable_name)
231         expected_cost_variable._model = binding_instance
232         setattr(binding_instance, expected_cost_variable_name, expected_cost_variable)
233
234   if generate_weighted_cvar is True:
235
236      cvar_cost_variable_name = "CVAR_COST_" + root_node._name
237      cvar_cost_variable = Var(name=cvar_cost_variable_name)
238      cvar_cost_variable.construct()           
239      setattr(binding_instance, cvar_cost_variable_name, cvar_cost_variable)
240
241   binding_instance.preprocess()
242
243   # ditto above for the (non-expected) cost variable.
244   for stage in scenario_tree._stages:
245
246      (cost_variable,cost_variable_index) = stage._cost_variable
247
248      if verbose_output is True:
249         print "Creating master variable and blending constraints for cost variable="+cost_variable.name+", index="+str(cost_variable_index)
250
251      for tree_node in stage._tree_nodes:
252
253         # TBD - the following is bad - check to see if it's already there (I suspect some of them are!!!)         
254
255         # this is undoubtedly wasteful, in that a cost variable
256         # for each tree node is created with *all* indices.         
257         new_cost_variable_name = tree_node._name + "_" + cost_variable.name
258         new_cost_variable_index = cost_variable._index
259         new_cost_variable = None
260         if (len(new_cost_variable_index) is 1) and (None in new_cost_variable_index):
261            new_cost_variable = Var(name=new_cost_variable_name)
262         else:
263            new_cost_variable = Var(new_cost_variable_index, name=new_cost_variable_name)
264         new_cost_variable.construct()
265         new_cost_variable._model = binding_instance
266         setattr(binding_instance, new_cost_variable_name, new_cost_variable)         
267
268         # the following is necessary, specifically to get the name - deepcopy won't reset these attributes.
269         new_cost_variable[cost_variable_index].var = new_cost_variable
270         if cost_variable_index is not None:
271            # if the variable index is None, the variable is derived from a VarValue, so the
272            # name gets updated automagically.
273            new_cost_variable[cost_variable_index].name = tree_node._name + "_" + new_cost_variable[cost_variable_index].name
274
275         for scenario in tree_node._scenarios:
276
277            scenario_instance = scenario_instances[scenario._name]
278            scenario_cost_variable = getattr(scenario_instance, cost_variable.name)
279            new_constraint_name = scenario._name + "_" + new_cost_variable_name + "_" + str(cost_variable_index)
280            new_constraint = Constraint(name=new_constraint_name)
281            new_expr = new_cost_variable[cost_variable_index] - scenario_cost_variable[cost_variable_index]
282            new_constraint.add(None, (0.0, new_expr, 0.0))
283            new_constraint._model = binding_instance
284            setattr(binding_instance, new_constraint_name, new_constraint)
285
286   # create the constraints for computing the master per-node cost variables,
287   # i.e., the current node cost and the expected cost of the child nodes.
288   # if the root, then the constraint is just the objective.
289   for stage in scenario_tree._stages:
290
291      (stage_cost_variable,stage_cost_variable_index) = stage._cost_variable
292
293      for tree_node in stage._tree_nodes:
294
295         node_expected_cost_variable_name = "EXPECTED_COST_" + tree_node._name
296         node_expected_cost_variable = getattr(binding_instance, node_expected_cost_variable_name)
297
298         node_cost_variable_name = tree_node._name + "_" + stage_cost_variable.name
299         node_cost_variable = getattr(binding_instance, node_cost_variable_name)                       
300           
301         constraint_expr = node_expected_cost_variable - node_cost_variable[stage_cost_variable_index]
302
303         for child_node in tree_node._children:
304
305            child_node_expected_cost_variable_name = "EXPECTED_COST_" + child_node._name
306            child_node_expected_cost_variable = getattr(binding_instance, child_node_expected_cost_variable_name)
307            constraint_expr = constraint_expr - (child_node._conditional_probability * child_node_expected_cost_variable)
308
309         new_constraint_name = "COST" + "_" + node_cost_variable_name + "_" + str(cost_variable_index)
310         new_constraint = Constraint(name=new_constraint_name)
311         new_constraint.add(None, (0.0, constraint_expr, 0.0))
312         new_constraint._model = binding_instance                     
313         setattr(binding_instance, new_constraint_name, new_constraint)
314
315         if tree_node._parent is None:
316
317            an_instance = scenario_instances[scenario_instances.keys()[0]]
318            an_objective = an_instance.active_components(Objective)
319            opt_sense = an_objective[an_objective.keys()[0]].sense
320
321            opt_expression = node_expected_cost_variable
322
323            if generate_weighted_cvar is True:
324               cvar_cost_variable_name = "CVAR_COST_" + tree_node._name
325               cvar_cost_variable = getattr(binding_instance, cvar_cost_variable_name)
326               if cvar_weight == 0.0:
327                  # if the cvar weight is 0, then we're only doing cvar - no mean.
328                  opt_expression = cvar_cost_variable                             
329               else:
330                  opt_expression += cvar_weight * cvar_cost_variable           
331
332            new_objective = Objective(name="MASTER", sense=opt_sense)
333            new_objective._data[None].expr = opt_expression
334            setattr(binding_instance, "MASTER", new_objective)
335
336   # CVaR requires the addition of a variable per scenario to represent the cost excess,
337   # and a constraint to compute the cost excess relative to eta. we also replicate (following
338   # what we do for node cost variables) an eta variable for each scenario instance, and
339   # require equality with the master eta variable via constraints.
340   if generate_weighted_cvar is True:
341     
342      # add the constraint to compute the master CVaR variable value. iterate
343      # over scenario instances to create the expected excess component first.
344      cvar_cost_variable_name = "CVAR_COST_" + root_node._name
345      cvar_cost_variable = getattr(binding_instance, cvar_cost_variable_name)
346      cvar_eta_variable_name = "CVAR_ETA"
347      cvar_eta_variable = getattr(binding_instance, cvar_eta_variable_name)
348     
349      cvar_cost_expression = cvar_cost_variable - cvar_eta_variable
350     
351      for scenario_name, scenario_instance in scenario_instances.items():
352
353         scenario_probability = scenario_tree._scenario_map[scenario_name]._probability
354
355         scenario_excess_variable_name = "CVAR_EXCESS"
356         scenario_excess_variable = getattr(scenario_instance, scenario_excess_variable_name)
357
358         cvar_cost_expression = cvar_cost_expression - (scenario_probability * scenario_excess_variable) / (1.0 - risk_alpha)
359
360      compute_cvar_cost_constraint_name = "COMPUTE_CVAR_COST"
361      compute_cvar_cost_constraint = Constraint(name=compute_cvar_cost_constraint_name)
362      compute_cvar_cost_constraint.add(None, (0.0, cvar_cost_expression, 0.0))
363      compute_cvar_cost_constraint._model = binding_instance
364      setattr(binding_instance, compute_cvar_cost_constraint_name, compute_cvar_cost_constraint)
365
366   # always preprocess the binding instance, so that it is ready for solution.
367   binding_instance.preprocess()
368
369   return binding_instance
370
371#
372# write the EF binding instance and all sub-instances. currently only outputs the CPLEX LP file format.
373#
374
375def write_ef(binding_instance, scenario_instances, output_filename):
376
377   # create the output file.
378   problem_writer = cpxlp.ProblemWriter_cpxlp()
379   output_file = open(output_filename,"w")
380
381   problem_writer._output_prefixes = True # we always want prefixes
382
383   ################################################################################################
384   #### WRITE THE MASTER OBJECTIVE ################################################################
385   ################################################################################################
386
387   # write the objective for the master binding instance.
388   problem_writer._output_objectives = True
389   problem_writer._output_constraints = False
390   problem_writer._output_variables = False
391
392   print >>output_file, "\\ Begin objective block for master"
393   problem_writer._print_model_LP(binding_instance, output_file)
394   print >>output_file, "\\ End objective block for master"
395   print >>output_file, ""
396
397   ################################################################################################
398   #### WRITE THE CONSTRAINTS FOR THE MASTER MODEL AND ALL SCENARIO MODELS ########################
399   ################################################################################################
400
401   print >>output_file, "s.t."
402   print >>output_file, ""
403   
404   problem_writer._output_objectives = False
405   problem_writer._output_constraints = True
406   problem_writer._output_variables = False
407
408   print >>output_file, "\\ Begin constraint block for master"
409   problem_writer._print_model_LP(binding_instance, output_file)
410   print >>output_file, "\\ End constraint block for master",
411   print >>output_file, ""
412
413   for scenario_name in scenario_instances.keys():
414      instance = scenario_instances[scenario_name]
415      print >>output_file, "\\ Begin constraint block for scenario",scenario_name       
416      problem_writer._print_model_LP(instance, output_file)
417      print >>output_file, "\\ End constraint block for scenario",scenario_name
418      print >>output_file, ""
419
420   ################################################################################################
421   #### WRITE THE VARIABLES FOR THE MASTER MODEL AND ALL SCENARIO MODELS ##########################
422   ################################################################################################
423
424   # write the variables for the master binding instance, and then for each scenario.
425   print >>output_file, "bounds"
426   print >>output_file, ""
427   
428   problem_writer._output_objectives = False
429   problem_writer._output_constraints = False
430   problem_writer._output_variables = True
431
432   # first step: write variable bounds
433
434   problem_writer._output_continuous_variables = True
435   problem_writer._output_integer_variables = False
436   problem_writer._output_binary_variables = False
437
438   print >>output_file, "\\ Begin variable bounds block for master"
439   problem_writer._print_model_LP(binding_instance, output_file)
440   print >>output_file, "\\ End variable bounds block for master"
441   print >>output_file, ""
442   
443   for scenario_name in scenario_instances.keys():
444      instance = scenario_instances[scenario_name]
445      print >>output_file, "\\ Begin variable bounds block for scenario",scenario_name
446      problem_writer._print_model_LP(instance, output_file)
447      print >>output_file, "\\ End variable bounds block for scenario",scenario_name
448      print >>output_file, ""
449
450   # second step: write integer indicators.
451
452   problem_writer._output_continuous_variables = False
453   problem_writer._output_integer_variables = True
454
455   if integers_present(binding_instance, scenario_instances) is True:
456
457      print >>output_file, "integer"
458      print >>output_file, ""
459
460      print >>output_file, "\\ Begin integer variable block for master"
461      problem_writer._print_model_LP(binding_instance, output_file)
462      print >>output_file, "\\ End integer variable block for master"
463      print >>output_file, ""
464   
465      for scenario_name in scenario_instances.keys():
466         instance = scenario_instances[scenario_name]
467         print >>output_file, "\\ Begin integer variable block for scenario",scenario_name
468         problem_writer._print_model_LP(instance, output_file)
469         print >>output_file, "\\ End integer variable block for scenario",scenario_name
470         print >>output_file, ""
471
472   # third step: write binary indicators.
473
474   problem_writer._output_integer_variables = False
475   problem_writer._output_binary_variables = True
476
477   if binaries_present(binding_instance, scenario_instances) is True:
478
479      print >>output_file, "binary"
480      print >>output_file, ""
481
482      print >>output_file, "\\ Begin binary variable block for master"
483      problem_writer._print_model_LP(binding_instance, output_file)
484      print >>output_file, "\\ End binary variable block for master"
485      print >>output_file, ""
486   
487      for scenario_name in scenario_instances.keys():
488         instance = scenario_instances[scenario_name]
489         print >>output_file, "\\ Begin binary variable block for scenario",scenario_name
490         problem_writer._print_model_LP(instance, output_file)
491         print >>output_file, "\\ End integer binary block for scenario",scenario_name
492         print >>output_file, ""
493
494   # wrap up.
495   print >>output_file, "end"
496
497   # clean up.
498   output_file.close()
499   
500
501#
502# the main extensive-form writer routine - including read of scenarios/etc.
503# returns a triple consisting of the scenario tree, master binding instance, and scenario instance map
504#
505
506def write_ef_from_scratch(model_directory, instance_directory, output_filename,
507                          verbose_output,
508                          generate_weighted_cvar, cvar_weight, risk_alpha):
509
510   start_time = time.time()
511
512   scenario_data_directory_name = instance_directory
513
514   print "Loading scenario and instance data"
515
516   #
517   # create and populate the core model
518   #
519   master_scenario_model = None
520   master_scenario_instance = None
521
522   if verbose_output:
523      print "Constructing reference model and instance"
524   
525   try:
526     
527      reference_model_filename = model_directory+os.sep+"ReferenceModel.py"   
528      modelimport = pyutilib.misc.import_file(reference_model_filename)
529      if "model" not in dir(modelimport):
530         print ""
531         print "Exiting ef module: No 'model' object created in module "+reference_model_filename
532         sys.exit(0)
533      if modelimport.model is None:
534         print ""
535         print "Exiting ef module: 'model' object equals 'None' in module "+reference_model_filename
536         sys.exit(0)
537   
538      master_scenario_model = modelimport.model
539
540   except IOError:
541     
542      print "***ERROR: Failed to load scenario reference model from file="+reference_model_filename
543      return None, None, None
544
545   try:
546     
547      reference_scenario_filename = instance_directory+os.sep+"ReferenceModel.dat"
548      master_scenario_instance = master_scenario_model.create(reference_scenario_filename)
549     
550   except IOError:
551     
552      print "***ERROR: Failed to load scenario reference instance data from file="+reference_scenario_filename
553      return None, None, None           
554
555   #
556   # create and populate the scenario tree model
557   #
558
559   from coopr.pysp.util.scenariomodels import scenario_tree_model
560
561   if verbose_output:
562      print "Constructing scenario tree instance"
563
564   scenario_tree_instance = scenario_tree_model.create(instance_directory+os.sep+"ScenarioStructure.dat")
565
566   #
567   # construct the scenario tree
568   #
569   if verbose_output:
570      print "Constructing scenario tree object"
571   
572   scenario_tree = ScenarioTree(scenarioinstance=master_scenario_instance,
573                                scenariotreeinstance=scenario_tree_instance)
574
575   #
576   # print the input tree for validation/information purposes.
577   #
578   if verbose_output is True:
579      scenario_tree.pprint()
580
581   #
582   # validate the tree prior to doing anything serious
583   #
584   if scenario_tree.validate() is False:
585      print "***Scenario tree is invalid****"
586      sys.exit(1)
587   else:
588      if verbose_output is True:
589         print "Scenario tree is valid!"
590
591   #
592   # construct instances for each scenario
593   #
594
595   # the construction of instances takes little overhead in terms of
596   # memory potentially lost in the garbage-collection sense (mainly
597   # only that due to parsing and instance simplification/prep-processing).
598   # to speed things along, disable garbage collection if it enabled in
599   # the first place through the instance construction process.
600   # IDEA: If this becomes too much for truly large numbers of scenarios,
601   #       we could manually collect every time X instances have been created.
602
603   re_enable_gc = False
604   if gc.isenabled() is True:
605      re_enable_gc = True
606      gc.disable()
607
608   scenario_instances = {}
609   
610   if scenario_tree._scenario_based_data == 1:
611      if verbose_output is True:
612         print "Scenario-based instance initialization enabled"
613   else:
614      if verbose_output is True:
615         print "Node-based instance initialization enabled"
616         
617   for scenario in scenario_tree._scenarios:
618
619      scenario_instance = None
620
621      if verbose_output is True:
622         print "Creating instance for scenario=" + scenario._name
623
624      try:
625         if scenario_tree._scenario_based_data == 1:
626            scenario_data_filename = scenario_data_directory_name + os.sep + scenario._name + ".dat"
627            scenario_instance = master_scenario_model.create(scenario_data_filename)
628         else:
629            scenario_instance = master_scenario_model.clone()
630            scenario_data = ModelData()
631            current_node = scenario._leaf_node
632            while current_node is not None:
633               node_data_filename = scenario_data_directory_name + os.sep + current_node._name + ".dat"
634               scenario_data.add(node_data_filename)
635               current_node = current_node._parent
636            scenario_data.read(model=scenario_instance)
637            scenario_instance.load(scenario_data)
638            scenario_instance.preprocess()
639      except:
640         print "Encountered exception in model instance creation - traceback:"
641         traceback.print_exc()
642         raise RuntimeError, "Failed to create model instance for scenario=" + scenario._name
643
644      # name each instance with the scenario name, so the prefixes in the EF make sense.
645      scenario_instance.name = scenario._name
646     
647      scenario_instances[scenario._name] = scenario_instance
648
649   if re_enable_gc is True:
650      gc.enable()
651
652   print "Creating extensive form binding instance"
653
654   binding_instance = create_ef_instance(scenario_tree, scenario_instances,
655                                         verbose_output = verbose_output,
656                                         generate_weighted_cvar = generate_weighted_cvar,
657                                         cvar_weight = cvar_weight,
658                                         risk_alpha = risk_alpha)
659
660   print "Starting to write extensive form"
661
662   write_ef(binding_instance, scenario_instances, output_filename)   
663
664   print "Output file written to file=",output_filename
665
666   end_time = time.time()
667
668   print "Total execution time=%8.2f seconds" %(end_time - start_time)
669
670   return scenario_tree, binding_instance, scenario_instances
671
672#
673# does what it says, with the added functionality of returning the master binding instance.
674#
675
676def create_and_write_ef(scenario_tree, scenario_instances, output_filename):
677
678   start_time = time.time()
679
680   binding_instance = create_ef_instance(scenario_tree, scenario_instances)
681
682   print "Starting to write extensive form"
683
684   write_ef(binding_instance, scenario_instances, output_filename)
685
686   print "Output file written to file=",output_filename
687
688   end_time = time.time()
689
690   print "Total execution time=%8.2f seconds" %(end_time - start_time)
691
692   return binding_instance
693
694#
695# a utility to load an EF solution into the corresponding instances.
696#
697
698def load_ef_solution(ef_results, binding_instance, scenario_instances):
699
700   # type is coopr.opt.results.results.SolverResults
701   if len(ef_results.solution) == 0:
702      raise RuntimeError, "Method load_ef_solution invoked with results object containing no solutions!"
703   elif len(ef_results.solution) > 1:
704      raise RuntimeError, "Method load_ef_solution invoked with results object containing more than one solution!"
705   
706   # type is coopr.opt.results.solution.Solution
707   solution = ef_results.solution[0]
708
709   # shotgun the ef solution into individual solutions for the binding and scenario instances.
710   sub_solutions = {} # map between instance name and the corresponding Solution
711   sub_solutions[binding_instance.name] = Solution()
712   for scenario_name, scenario in scenario_instances.items():
713      sub_solutions[scenario_name] = Solution()
714
715   for key, attr_dictionary in solution.variable.items():
716      tokens = string.split(key, '_', 1)
717      instance_name = tokens[0]
718      variable_name = tokens[1]
719      subsolution_variable = sub_solutions[instance_name].variable[variable_name]
720      for attr_name in attr_dictionary.keys():
721         attr_value = attr_dictionary[attr_name]
722         setattr(subsolution_variable, attr_name, attr_value)
723
724   # load the sub-solutions into the appropriate instances.
725   for instance_name, sub_solution in sub_solutions.items():
726      if instance_name == binding_instance.name:
727         binding_instance.load(sub_solution)
728      else:
729         scenario_instances[instance_name].load(sub_solution)
Note: See TracBrowser for help on using the repository browser.