source: coopr.pysp/stable/2.1/coopr/pysp/scenariotree.py @ 2068

Last change on this file since 2068 was 2068, checked in by wehart, 10 years ago

Merged revisions 1952-2067 via svnmerge from
https://software.sandia.gov/svn/public/coopr/coopr.pysp/trunk

........

r1956 | jwatson | 2009-12-02 17:56:53 -0700 (Wed, 02 Dec 2009) | 3 lines


Added --scenario-solver-options and --ef-solver-options options to the "runph" script.

........

r1957 | dlwoodr | 2009-12-03 14:17:35 -0700 (Thu, 03 Dec 2009) | 2 lines


Documentation updates for pysp

........

r1974 | wehart | 2009-12-06 17:20:56 -0700 (Sun, 06 Dec 2009) | 2 lines


Updating PyPI categories

........

r1978 | jwatson | 2009-12-10 21:29:33 -0700 (Thu, 10 Dec 2009) | 3 lines


Eliminated exception-handling logic when loading user-defined extension modules in PH. The range of exceptions is too large, and for debugging purposes, it is more useful to see the raw trace output.

........

r1979 | jwatson | 2009-12-10 22:23:17 -0700 (Thu, 10 Dec 2009) | 5 lines


Biggest enhancement: The efwriter command-line script now has the option to output a CVaR-weighted objective term. Not extensively tested, but behaves sane on a number of small test cases.


Improved exception handling and error diagnostics in both the runph and efwriter scripts.

........

r1985 | jwatson | 2009-12-12 10:45:17 -0700 (Sat, 12 Dec 2009) | 3 lines


Modified PH to only use warm-starts if a solver has the capability!

........

r1998 | jwatson | 2009-12-13 15:17:58 -0700 (Sun, 13 Dec 2009) | 3 lines


Changed references to _component to active_component.

........

r2026 | wehart | 2009-12-21 23:27:06 -0700 (Mon, 21 Dec 2009) | 2 lines


Attempting to update PH. I'm not sure if this works, since I don't know how to test PH.

........

r2029 | jwatson | 2009-12-22 09:52:21 -0700 (Tue, 22 Dec 2009) | 3 lines


Some fixes to the ef writer based on Bill's recent changes to _initialize_constraint.

........

r2035 | jwatson | 2009-12-22 21:10:32 -0700 (Tue, 22 Dec 2009) | 3 lines


Added --scenario-mipgap option to PH script. Added _mipgap attribute to PH object. This attribute is mirrored to the solver plugin at the initiation of each iteration, after any PH extensions have the opportunity to provide a new value to the attribute. It is currently made use of by the WW PH extension.

........

r2037 | dlwoodr | 2009-12-23 14:38:43 -0700 (Wed, 23 Dec 2009) | 2 lines


add this example from Fernando

........

r2038 | dlwoodr | 2009-12-23 14:46:56 -0700 (Wed, 23 Dec 2009) | 3 lines


finish the job: we can now duplicate Fernando's example

........

r2039 | jwatson | 2009-12-23 15:13:54 -0700 (Wed, 23 Dec 2009) | 3 lines


Missed fix with new constraint initialization syntax in PH linearization.

........

r2058 | jwatson | 2009-12-29 10:57:58 -0700 (Tue, 29 Dec 2009) | 3 lines


Missed some _initialize_constraint function calls within the PySP EF writer during the recent switch to the corresponding "add" method.

........

r2059 | jwatson | 2009-12-29 10:58:34 -0700 (Tue, 29 Dec 2009) | 3 lines


Enabling garbage collection by default in PH.

........

r2060 | jwatson | 2009-12-29 10:59:05 -0700 (Tue, 29 Dec 2009) | 3 lines


Elimnating some debug output.

........

r2061 | jwatson | 2009-12-29 11:07:47 -0700 (Tue, 29 Dec 2009) | 3 lines


Fixing some option documentation in PH.

........

r2062 | jwatson | 2009-12-29 12:00:37 -0700 (Tue, 29 Dec 2009) | 3 lines


Added ef-mipgap option to PH scripts.

........

File size: 26.9 KB
Line 
1#  _________________________________________________________________________
2#
3#  Coopr: A COmmon Optimization Python Repository
4#  Copyright (c) 2008 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
11import sys
12import types
13from coopr.pyomo import *
14import copy
15import os.path
16import traceback
17
18from phutils import *
19
20class ScenarioTreeNode(object):
21
22   """ Constructor
23
24   """
25   def __init__(self, *args, **kwds):
26
27      self._name = ""
28      self._stage = None
29      self._parent = None
30      self._children = [] # a collection of ScenarioTreeNodes
31      self._conditional_probability = None # conditional on parent
32      self._scenarios = [] # a collection of all Scenarios passing through this node in the tree
33
34      # general use statistics for the variables at each node.
35      # each attribute is a map between the variable name and a
36      # parameter (over the same index set) encoding the corresponding
37      # statistic computed over all scenarios for that node. the
38      # parameters are named as the source variable name suffixed
39      # by one of: "NODEMIN", "NODEAVG", and "NODEMAX".
40      # NOTE: the averages are probability_weighted - the min/max
41      #       values are not.
42      self._averages = {} 
43      self._minimums = {}
44      self._maximums = {}
45
46   #
47   # a utility to compute the cost of the current node plus the expected costs of child nodes.
48   #
49   def computeExpectedNodeCost(self, scenario_instance_map):
50
51      # IMPT: This implicitly assumes convergence across the scenarios - if not, garbage results.
52      instance = scenario_instance_map[self._scenarios[0]._name]
53      my_cost = instance.active_components(Var)[self._stage._cost_variable[0].name][self._stage._cost_variable[1]]()
54      child_cost = 0.0
55      for child in self._children:
56         child_cost += (child._conditional_probability * child.computeExpectedNodeCost(scenario_instance_map))
57      return my_cost + child_cost
58
59
60class Stage(object):
61
62   """ Constructor
63
64   """
65   def __init__(self, *args, **kwds):
66      self._name = ""
67      self._tree_nodes = []      # a collection of ScenarioTreeNodes
68      # a collection of pairs consisting of (1) references to pyomo model Vars, (2) the original match template string (for output purposes),
69      # and (3) a *list* of the corresponding indices.
70      # the variables are references to those objects belonging to the instance in the parent ScenarioTree.
71      # NOTE: if the variable index is none, it is assumed that the entire variable is blended.
72      self._variables = []
73      # a tuple consisting of (1) a reference to a pyomo model Var that computes the stage-specific cost and (2) the corresponding index.
74      # the index *is* the sole index in the cost variable, as the cost variable refers to a single variable index.
75      self._cost_variable = (None, None)
76
77class Scenario(object):
78
79   """ Constructor
80
81   """
82   def __init__(self, *args, **kwds):
83      self._name = None
84      self._leaf_node = None  # allows for construction of node list
85      self._node_list = []    # sequence from parent to leaf of ScenarioTreeNodes
86      self._probability = 0.0 # the unconditional probability for this scenario, computed from the node list
87
88class ScenarioTree(object):
89
90   """ Constructor
91       Arguments:
92           model                         the (deterministic) model associated with this scenario tree
93           nodes              (type-TBD) set of tree node IDs
94           nodechildren       (type-TBD) map of node ID to a set of IDs indicating child nodes.
95           nodestages         (type-TBD) map of tree node ID to stage ID
96           nodeprobabilities  (type-TBD) map of node ID to corresponding conditional probability
97           stages             (type-TBD) ordered set of stage IDs
98           scenarios          (type-TBD) set of scenario node IDs
99           scenarioleafs      (type-TBD) map of scenario ID to leaf tree node ID
100           stagevariables     (type-TBD) map of stage ID to the names of the variables in the corresponding stage.
101           stagecostvariables (type-TBD) map of stage ID to the name of the cost variable for the corresponding stage.
102           nodedata           (type-TBD) map of node ID to the name of the corresponding data file (prefix only - no .dat suffix).
103           scenariobaseddata  (type-TBD) parameter indicating whether instance data comes from scenario-based or node-based .dat files.
104   """
105   def __init__(self, *args, **kwds):
106      self._name = None # TBD - some arbitrary identifier
107      self._reference_instance = None # TBD - the reference (deterministic) base model
108
109      # the core objects defining the scenario tree.
110      self._tree_nodes = [] # collection of ScenarioTreeNodes
111      self._stages = [] # collection of Stages - assumed to be in time-order. the set (provided by the user) itself *must* be ordered.
112      self._scenarios = [] # collection of Scenarios
113
114      # dictionaries for the above.
115      self._tree_node_map = {}
116      self._stage_map = {}
117      self._scenario_map = {}
118
119      # mapping of stages to sets of variables which belong in the corresponding stage.
120      self._stage_variables = {}
121
122      # a boolean indicating how data for scenario instances is specified.
123      # possibly belongs elsewhere, e.g., in the PH algorithm.
124      self._scenario_based_data = None
125
126      # every stage has a cost variable - this is a variable/index pair.
127      self._cost_variable = None
128
129      # working copies based on the input arguments - only used
130      # for constructing the tree, and subsequently discarded.
131      node_ids = None
132      node_child_ids = None
133      node_stage_ids = None
134      node_probability_map = None
135      stage_ids = None
136      stage_variable_ids = None
137      stage_cost_variable_ids = None
138      scenario_ids = None
139      scenario_leaf_ids = None
140      scenario_based_data = None
141
142      # process the keyword options
143      for key in kwds.keys():
144         if key == "model":
145            self._reference_instance = kwds[key]
146         elif key == "nodes":
147            node_ids = kwds[key]
148         elif key == "nodechildren":
149            node_child_ids = kwds[key]           
150         elif key == "nodestages":
151            node_stage_ids = kwds[key]
152         elif key == "nodeprobabilities":
153            node_probability_map = kwds[key]           
154         elif key == "stages":
155            stage_ids = kwds[key]
156         elif key == "stagevariables":
157            stage_variable_ids = kwds[key]
158         elif key == "stagecostvariables":
159            stage_cost_variable_ids = kwds[key]                                                           
160         elif key == "scenarios":
161            scenario_ids = kwds[key]
162         elif key == "scenarioleafs":
163            scenario_leaf_ids = kwds[key]
164         elif key == "scenariobaseddata":
165            scenario_based_data = kwds[key]           
166         else:
167            print "Unknown option=" + key + " specified in call to ScenarioTree constructor"
168
169      # TBD - verify type of keyword arguments match expectation, and that they're all supplied.
170      if self._reference_instance is None:
171         raise ValueError, "A reference model must be supplied in the ScenarioTree constructor"         
172      if node_ids is None:
173         raise ValueError, "A set of node IDs must be supplied in the ScenarioTree constructor"
174      if node_child_ids is None:
175         raise ValueError, "A map from node IDs to the set of child node ID must be supplied in the ScenarioTree constructor"           
176      if node_stage_ids is None:
177         raise ValueError, "A map from node ID to stage ID must be supplied in the ScenarioTree constructor"
178      if node_probability_map is None:
179         raise ValueError, "A map from node ID to the corresponding conditional probability must be supplied in the ScenarioTree constructor"     
180      if stage_ids is None:
181         raise ValueError, "A set of stage IDs must be supplied in the ScenarioTree constructor"
182      if stage_variable_ids is None:
183         raise ValueError, "A map from stage ID to the corresponding set of variable names must be supplied in the ScenarioTree constructor"
184      if stage_cost_variable_ids is None:
185         raise ValueError, "A map from stage ID to the corresponding stage cost variable name must be supplied in the ScenarioTree constructor"                 
186      if scenario_ids is None:
187         raise ValueError, "A set of scenario IDs must be supplied in the Scenario Tree constructor"
188      if scenario_leaf_ids is None:
189         raise ValueError, "A map from scenario ID to the leaf tree node ID must be supplied in the ScenarioTree constructor"
190      if scenario_leaf_ids is None:
191         raise ValueError, "A map from scenario ID to the leaf tree node ID must be supplied in the ScenarioTree constructor"
192      if scenario_based_data is None:
193         raise ValueError, "A boolean indicating whether the instance data is obtained from per-scenario or per-node sources must be supplied in the ScenarioTree constructor"
194
195      # save the method for instance data storage.
196      self._scenario_based_data = scenario_based_data()
197
198      # the input stages must be ordered, for both output purposes and knowledge of the final stage.
199      if stage_ids.ordered is False:
200         raise ValueError, "An ordered set of stage IDs must be supplied in the ScenarioTree constructor"
201
202      #     
203      # construct the actual tree objects
204      #
205
206      # construct the stage objects w/o any linkages first; link them up
207      # with tree nodes after these have been fully constructed.
208      for stage_name in stage_ids:
209         new_stage = Stage()
210         new_stage._name = stage_name
211         self._stages.append(new_stage)
212         self._stage_map[stage_name] = new_stage
213
214      # construct the tree node objects themselves in a first pass,
215      # and then link them up in a second pass to form the tree.
216      # can't do a single pass because the objects may not exist.
217      for tree_node_name in node_ids:
218         new_tree_node = ScenarioTreeNode()
219         new_tree_node._name = tree_node_name
220
221         self._tree_nodes.append(new_tree_node)
222         self._tree_node_map[tree_node_name] = new_tree_node
223
224         new_tree_node._conditional_probability = node_probability_map[tree_node_name].value
225
226         if tree_node_name not in node_stage_ids:
227            raise ValueError, "No stage is assigned to tree node=" + tree_node._name
228         else:
229            stage_name = node_stage_ids[new_tree_node._name].value
230            if stage_name not in self._stage_map.keys():
231               raise ValueError, "Unknown stage=" + stage_name + " assigned to tree node=" + tree_node._name
232            else:
233               new_tree_node._stage = self._stage_map[stage_name]
234               self._stage_map[stage_name]._tree_nodes.append(new_tree_node)
235
236      # link up the tree nodes objects based on the child id sets.
237      for this_node in self._tree_nodes:
238         this_node._children = []
239         if this_node._name in node_child_ids: # otherwise, you're at a leaf and all is well.
240            child_ids = node_child_ids[this_node._name]
241            for child_id in child_ids:
242               if child_id in self._tree_node_map.keys():
243                  child_node = self._tree_node_map[child_id]
244                  this_node._children.append(self._tree_node_map[child_id])
245                  if child_node._parent is None:
246                     child_node._parent = this_node
247                  else:
248                     raise ValueError, "Multiple parents specified for tree node="+child_id+"; existing parent node="+child_node._parent._name+"; conflicting parent node="+this_node._name
249               else:
250                  raise ValueError, "Unknown child tree node=" + child_id + " specified for tree node=" + this_node._name
251
252      # at this point, the scenario tree nodes and the stages are set - no
253      # two-pass logic necessary when constructing scenarios.
254      for scenario_name in scenario_ids:
255         new_scenario = Scenario()
256         new_scenario._name=scenario_name
257
258         if scenario_name not in scenario_leaf_ids.keys():
259            raise ValueError, "No leaf tree node specified for scenario=" + scenario_name
260         else:
261            scenario_leaf_node_name = scenario_leaf_ids[scenario_name].value
262            if scenario_leaf_node_name not in self._tree_node_map.keys():
263               raise ValueError, "Uknown tree node=" + scenario_leaf_node_name + " specified as leaf of scenario=" + scenario_name
264            else:
265               new_scenario._leaf_node = self._tree_node_map[scenario_leaf_node_name]
266
267         current_node = new_scenario._leaf_node
268         probability = 1.0
269         while current_node is not None:
270            new_scenario._node_list.append(current_node)
271            current_node._scenarios.append(new_scenario) # links the scenarios to the nodes to enforce necessary non-anticipativity
272            probability *= current_node._conditional_probability
273            current_node = current_node._parent
274         new_scenario._node_list.reverse()
275         new_scenario._probability = probability
276
277         self._scenarios.append(new_scenario)
278         self._scenario_map[scenario_name] = new_scenario
279
280      # map the variables to the stages - these are references, not copies.
281      for stage_id in stage_variable_ids.keys():
282
283         if stage_id not in self._stage_map.keys():
284            raise ValueError, "Unknown stage=" + stage_id + " specified in scenario tree constructor (stage->variable map)"
285
286         stage = self._stage_map[stage_id]
287         variable_ids = stage_variable_ids[stage_id]
288
289         for variable_string in variable_ids:
290
291            if isVariableNameIndexed(variable_string) is True:
292
293               variable_name, index_template = extractVariableNameAndIndex(variable_string)
294
295               # validate that the variable exists and extract the reference.
296               if variable_name not in self._reference_instance.active_components(Var):
297                  raise ValueError, "Variable=" + variable_name + " associated with stage=" + stage_id + " is not present in model=" + self._reference_instance.name
298               variable = self._reference_instance.active_components(Var)[variable_name]               
299
300               # extract all "real", i.e., fully specified, indices matching the index template.
301               match_indices = extractVariableIndices(variable, index_template)               
302
303               # there is a possibility that no indices match the input template.
304               # if so, let the user know about it.
305               if len(match_indices) == 0:
306                  raise RuntimeError, "No indices match template="+str(index_template)+" for variable="+variable_name+" ; encountered in scenario tree specification for model="+self._reference_instance.name
307                 
308               stage._variables.append((variable, index_template, match_indices))
309
310            else:
311
312               # verify that the variable exists.
313               if variable_string not in self._reference_instance.active_components(Var).keys():
314                  raise RuntimeError, "Unknown variable=" + variable_string + " associated with stage=" + stage_id + " is not present in model=" + self._reference_instance.name
315
316               variable = self._reference_instance.active_components(Var)[variable_string]
317
318               # 9/14/2009 - now forcing the user to explicit specify the full
319               # match template (e.g., "foo[*,*]") instead of just the variable
320               # name (e.g., "foo") to represent the set of all indices.
321               
322               # if the variable is a singleton - that is, non-indexed - no brackets is fine.
323               # we'll just tag the var[None] variable value with the (suffix,value) pair.
324               if None not in variable._index:
325                  raise RuntimeError, "Variable="+variable_string+" is an indexed variable, and templates must specify an index match; encountered in scenario tree specification for model="+self._reference_instance.name                 
326
327               match_indices = []
328               match_indices.append(None)
329
330               stage._variables.append((variable, "", match_indices))
331
332      for stage_id in stage_cost_variable_ids.keys():
333
334         if stage_id not in self._stage_map.keys():
335            raise ValueError, "Unknown stage=" + stage_id + " specified in scenario tree constructor (stage->cost variable map)"
336         stage = self._stage_map[stage_id]
337         
338         cost_variable_string = stage_cost_variable_ids[stage_id].value # de-reference is required to access the parameter value
339
340         # to be extracted from the string.
341         cost_variable_name = None
342         cost_variable = None
343         cost_variable_index = None
344
345         # do the extraction.
346         if isVariableNameIndexed(cost_variable_string) is True:
347
348            cost_variable_name, index_template = extractVariableNameAndIndex(cost_variable_string)
349
350            # validate that the variable exists and extract the reference.
351            if cost_variable_name not in self._reference_instance.active_components(Var):
352               raise ValueError, "Variable=" + cost_variable_name + " associated with stage=" + stage_id + " is not present in model=" + self._reference_instance.name
353            cost_variable = self._reference_instance.active_components(Var)[cost_variable_name]               
354
355            # extract all "real", i.e., fully specified, indices matching the index template.
356            match_indices = extractVariableIndices(cost_variable, index_template)
357
358            # only one index can be supplied for a stage cost variable.
359            if len(match_indices) != 1:
360               raise RuntimeError, "Only one index can be specified for a stage cost variable - "+str(len(match_indices))+"match template="+index_template+" for variable="+cost_variable_name+" ; encountered in scenario tree specification for model="+self._reference_instance.name
361
362            cost_variable_index = match_indices[0]
363
364         else:
365
366            cost_variable_name = cost_variable_string
367
368            # validate that the variable exists and extract the reference
369            if cost_variable_name not in self._reference_instance.active_components(Var):
370               raise ValueError, "Cost variable=" + cost_variable_name + " associated with stage=" + stage_id + " is not present in model=" + self._reference_instance.name
371            cost_variable = self._reference_instance.active_components(Var)[cost_variable_name]
372           
373         # store the validated info.
374         stage._cost_variable = (cost_variable, cost_variable_index)
375
376      # for output purposes, it is useful to known the maximal length of identifiers
377      # in the scenario tree for any particular category. I'm building these up
378      # incrementally, as they are needed. 0 indicates unassigned.
379      self._max_scenario_id_length = 0
380
381      # does the actual traversal to populate the members.
382      self.computeIdentifierMaxLengths()
383
384   #
385   # returns the root node of the scenario tree
386   #
387   def findRootNode(self):
388
389      for tree_node in self._tree_nodes:
390         if tree_node._parent is None:
391            return tree_node
392      return None
393
394   #
395   # a utility function to compute, based on the current scenario tree content,
396   # the maximal length of identifiers in various categories.
397   #
398   def computeIdentifierMaxLengths(self):
399
400      self._max_scenario_id_length = 0
401      for scenario in self._scenarios:
402         if len(scenario._name) > self._max_scenario_id_length:
403            self._max_scenario_id_length = len(scenario._name)
404
405   #
406   # a utility function to (partially, at the moment) validate a scenario tree
407   #
408   def validate(self):
409
410      # for any node, the sum of conditional probabilities of the children should sum to 1.
411      for tree_node in self._tree_nodes:
412         sum_probabilities = 0.0
413         if len(tree_node._children) > 0:
414            for child in tree_node._children:
415               sum_probabilities += child._conditional_probability
416            if abs(1.0 - sum_probabilities) > 0.000001:
417               print "The child conditional probabilities for tree node=" + child._name + " sums to " + `sum_probabilities`
418               return False
419
420      # ensure that there is only one root node in the tree
421      num_roots = 0
422      root_ids = []
423      for tree_node in self._tree_nodes:
424         if tree_node._parent is None:
425            num_roots += 1
426            root_ids.append(tree_node._name)
427
428      if num_roots != 1:
429         print "Illegal set of root nodes detected: " + `root_ids`
430         return False
431
432      # there must be at least one scenario passing through each tree node.
433      for tree_node in self._tree_nodes:
434         if len(tree_node._scenarios) == 0:
435            print "There are no scenarios associated with tree node=" + tree_node._name
436            return False
437     
438      return True
439
440   #
441   # a utility function to pretty-print the static/non-cost information associated with a scenario tree
442   #
443
444   def pprint(self):
445
446      print "Scenario Tree Detail"
447
448      print "----------------------------------------------------"
449      if self._reference_instance is not None:
450         print "Model=" + self._reference_instance.name
451      else:
452         print "Model=" + "Unassigned"
453      print "----------------------------------------------------"         
454      print "Tree Nodes:"
455      print ""
456      for tree_node_name, tree_node in sorted(self._tree_node_map.iteritems()):
457         print "\tName=" + tree_node_name
458         if tree_node._stage is not None:
459            print "\tStage=" + tree_node._stage._name
460         else:
461            print "\t Stage=None"
462         if tree_node._parent is not None:
463            print "\tParent=" + tree_node._parent._name
464         else:
465            print "\tParent=" + "None"
466         if tree_node._conditional_probability is not None:
467            print "\tConditional probability=%4.4f" % tree_node._conditional_probability
468         else:
469            print "\tConditional probability=" + "***Undefined***"
470         print "\tChildren:"
471         if len(tree_node._children) > 0:
472            for child_node in sorted(tree_node._children):
473               print "\t\t" + child_node._name
474         else:
475            print "\t\tNone"
476         print "\tScenarios:"
477         if len(tree_node._scenarios) == 0:
478            print "\t\tNone"
479         else:
480            for scenario in tree_node._scenarios:
481               print "\t\t" + scenario._name
482         print ""
483      print "----------------------------------------------------"
484      print "Stages:"
485      for stage_name, stage in sorted(self._stage_map.iteritems()):
486         print "\tName=" + stage_name
487         print "\tTree Nodes: "
488         for tree_node in stage._tree_nodes:
489            print "\t\t" + tree_node._name
490         print "\tVariables: "
491         for (variable, index_template, indices) in stage._variables:
492            if (len(indices) == 1) and (indices[0] == None):
493               print "\t\t" + variable.name
494            else:
495               print "\t\t",variable.name,":",index_template
496         print "\tCost Variable: "           
497         if stage._cost_variable[1] is None:
498            print "\t\t" + stage._cost_variable[0].name
499         else:
500            print "\t\t" + stage._cost_variable[0].name + indexToString(stage._cost_variable[1])
501         print ""           
502      print "----------------------------------------------------"
503      print "Scenarios:"
504      for scenario_name, scenario in sorted(self._scenario_map.iteritems()):
505         print "\tName=" + scenario_name
506         print "\tProbability=%4.4f" % scenario._probability
507         if scenario._leaf_node is None:
508            print "\tLeaf node=None"
509         else:
510            print "\tLeaf node=" + scenario._leaf_node._name
511         print "\tTree node sequence:"
512         for tree_node in scenario._node_list:
513            print "\t\t" + tree_node._name
514         print ""                       
515      print "----------------------------------------------------"
516
517   #
518   # a utility function to pretty-print the cost information associated with a scenario tree
519   #
520
521   def pprintCosts(self, scenario_instance_map):
522
523      print "Scenario Tree Costs"
524      print "***WARNING***: Assumes full (or nearly so) convergence of scenario solutions at each node in the scenario tree - computed costs are invalid otherwise"
525
526      print "----------------------------------------------------"
527      if self._reference_instance is not None:
528         print "Model=" + self._reference_instance.name
529      else:
530         print "Model=" + "Unassigned"
531
532      print "----------------------------------------------------"     
533      print "Tree Nodes:"
534      print ""
535      for tree_node_name, tree_node in sorted(self._tree_node_map.iteritems()):
536         print "\tName=" + tree_node_name
537         if tree_node._stage is not None:
538            print "\tStage=" + tree_node._stage._name
539         else:
540            print "\t Stage=None"
541         if tree_node._parent is not None:
542            print "\tParent=" + tree_node._parent._name
543         else:
544            print "\tParent=" + "None"
545         if tree_node._conditional_probability is not None:
546            print "\tConditional probability=%4.4f" % tree_node._conditional_probability
547         else:
548            print "\tConditional probability=" + "***Undefined***"
549         print "\tChildren:"
550         if len(tree_node._children) > 0:
551            for child_node in sorted(tree_node._children):
552               print "\t\t" + child_node._name
553         else:
554            print "\t\tNone"
555         print "\tScenarios:"
556         if len(tree_node._scenarios) == 0:
557            print "\t\tNone"
558         else:
559            for scenario in tree_node._scenarios:
560               print "\t\t" + scenario._name
561         print "\tExpected node cost=%10.4f" % tree_node.computeExpectedNodeCost(scenario_instance_map)
562         print ""
563
564      print "----------------------------------------------------"
565      print "Scenarios:"
566      print ""
567      for scenario_name, scenario in sorted(self._scenario_map.iteritems()):
568         instance = scenario_instance_map[scenario_name]
569
570         print "\tName=" + scenario_name
571         print "\tProbability=%4.4f" % scenario._probability
572
573         if scenario._leaf_node is None:
574            print "\tLeaf Node=None"
575         else:
576            print "\tLeaf Node=" + scenario._leaf_node._name
577
578         print "\tTree node sequence:"
579         for tree_node in scenario._node_list:
580            print "\t\t" + tree_node._name
581
582         aggregate_cost = 0.0
583         for stage in self._stages:
584            instance_cost_variable = instance.active_components(Var)[stage._cost_variable[0].name][stage._cost_variable[1]]()
585            print "\tStage=%20s     Cost=%10.4f" % (stage._name, instance_cost_variable)
586            aggregate_cost += instance_cost_variable
587         print "\tTotal scenario cost=%10.4f" % aggregate_cost
588         print ""
589      print "----------------------------------------------------"     
Note: See TracBrowser for help on using the repository browser.