source: coopr.pysp/trunk/coopr/pysp/scenariotree.py @ 2375

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

Fix to output message in scenario tree validation.

File size: 28.4 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           scenarioinstance             - the reference scenario instance.
93           scenariotreeinstance - the pyomo model specifying all scenario tree (text) data.
94           scenariobundlelist   - a list of scenario names to retain, i.e., cull the rest to create a reduced tree!
95   """ 
96   def __init__(self, *args, **kwds):
97      self._name = None # TBD - some arbitrary identifier
98      self._reference_instance = None # TBD - the reference (deterministic) base model
99
100      # the core objects defining the scenario tree.
101      self._tree_nodes = [] # collection of ScenarioTreeNodes
102      self._stages = [] # collection of Stages - assumed to be in time-order. the set (provided by the user) itself *must* be ordered.
103      self._scenarios = [] # collection of Scenarios
104
105      # dictionaries for the above.
106      self._tree_node_map = {}
107      self._stage_map = {}
108      self._scenario_map = {}
109
110      # mapping of stages to sets of variables which belong in the corresponding stage.
111      self._stage_variables = {}
112
113      # a boolean indicating how data for scenario instances is specified.
114      # possibly belongs elsewhere, e.g., in the PH algorithm.
115      self._scenario_based_data = None
116
117      # every stage has a cost variable - this is a variable/index pair.
118      self._cost_variable = None
119
120      scenario_tree_instance = None
121      scenario_bundle_list = None
122
123      # process the keyword options
124      for key in kwds.keys():
125         if key == "scenarioinstance":
126            self._reference_instance = kwds[key]
127         elif key == "scenariotreeinstance":
128            scenario_tree_instance = kwds[key]           
129         elif key == "scenariobundlelist":
130            scenario_bundle_list = kwds[key]           
131         else:
132            print "Unknown option=" + key + " specified in call to ScenarioTree constructor"
133
134      if self._reference_instance is None:
135         raise ValueError, "A reference scenario instance must be supplied in the ScenarioTree constructor"
136
137      if scenario_tree_instance is None:
138         raise ValueError, "A scenario tree instance must be supplied in the ScenarioTree constructor"
139
140      node_ids = scenario_tree_instance.Nodes
141      node_child_ids = scenario_tree_instance.Children
142      node_stage_ids = scenario_tree_instance.NodeStage
143      node_probability_map = scenario_tree_instance.ConditionalProbability
144      stage_ids = scenario_tree_instance.Stages
145      stage_variable_ids = scenario_tree_instance.StageVariables
146      stage_cost_variable_ids = scenario_tree_instance.StageCostVariable
147      scenario_ids = scenario_tree_instance.Scenarios
148      scenario_leaf_ids = scenario_tree_instance.ScenarioLeafNode
149      scenario_based_data = scenario_tree_instance.ScenarioBasedData
150
151      # save the method for instance data storage.
152      self._scenario_based_data = scenario_based_data()
153
154      # the input stages must be ordered, for both output purposes and knowledge of the final stage.
155      if stage_ids.ordered is False:
156         raise ValueError, "An ordered set of stage IDs must be supplied in the ScenarioTree constructor"
157
158      #     
159      # construct the actual tree objects
160      #
161
162      # construct the stage objects w/o any linkages first; link them up
163      # with tree nodes after these have been fully constructed.
164      for stage_name in stage_ids:
165         new_stage = Stage()
166         new_stage._name = stage_name
167         self._stages.append(new_stage)
168         self._stage_map[stage_name] = new_stage
169
170      # construct the tree node objects themselves in a first pass,
171      # and then link them up in a second pass to form the tree.
172      # can't do a single pass because the objects may not exist.
173      for tree_node_name in node_ids:
174         new_tree_node = ScenarioTreeNode()
175         new_tree_node._name = tree_node_name
176
177         self._tree_nodes.append(new_tree_node)
178         self._tree_node_map[tree_node_name] = new_tree_node
179
180         new_tree_node._conditional_probability = node_probability_map[tree_node_name].value
181
182         if tree_node_name not in node_stage_ids:
183            raise ValueError, "No stage is assigned to tree node=" + tree_node._name
184         else:
185            stage_name = node_stage_ids[new_tree_node._name].value
186            if stage_name not in self._stage_map.keys():
187               raise ValueError, "Unknown stage=" + stage_name + " assigned to tree node=" + tree_node._name
188            else:
189               new_tree_node._stage = self._stage_map[stage_name]
190               self._stage_map[stage_name]._tree_nodes.append(new_tree_node)
191
192      # link up the tree nodes objects based on the child id sets.
193      for this_node in self._tree_nodes:
194         this_node._children = []
195         if this_node._name in node_child_ids: # otherwise, you're at a leaf and all is well.
196            child_ids = node_child_ids[this_node._name]
197            for child_id in child_ids:
198               if child_id in self._tree_node_map.keys():
199                  child_node = self._tree_node_map[child_id]
200                  this_node._children.append(self._tree_node_map[child_id])
201                  if child_node._parent is None:
202                     child_node._parent = this_node
203                  else:
204                     raise ValueError, "Multiple parents specified for tree node="+child_id+"; existing parent node="+child_node._parent._name+"; conflicting parent node="+this_node._name
205               else:
206                  raise ValueError, "Unknown child tree node=" + child_id + " specified for tree node=" + this_node._name
207
208      # at this point, the scenario tree nodes and the stages are set - no
209      # two-pass logic necessary when constructing scenarios.
210      for scenario_name in scenario_ids:
211         new_scenario = Scenario()
212         new_scenario._name=scenario_name
213
214         if scenario_name not in scenario_leaf_ids.keys():
215            raise ValueError, "No leaf tree node specified for scenario=" + scenario_name
216         else:
217            scenario_leaf_node_name = scenario_leaf_ids[scenario_name].value
218            if scenario_leaf_node_name not in self._tree_node_map.keys():
219               raise ValueError, "Uknown tree node=" + scenario_leaf_node_name + " specified as leaf of scenario=" + scenario_name
220            else:
221               new_scenario._leaf_node = self._tree_node_map[scenario_leaf_node_name]
222
223         current_node = new_scenario._leaf_node
224         probability = 1.0
225         while current_node is not None:
226            new_scenario._node_list.append(current_node)
227            current_node._scenarios.append(new_scenario) # links the scenarios to the nodes to enforce necessary non-anticipativity
228            probability *= current_node._conditional_probability
229            current_node = current_node._parent
230         new_scenario._node_list.reverse()
231         new_scenario._probability = probability
232
233         self._scenarios.append(new_scenario)
234         self._scenario_map[scenario_name] = new_scenario
235
236      # map the variables to the stages - these are references, not copies.
237      for stage_id in stage_variable_ids.keys():
238
239         if stage_id not in self._stage_map.keys():
240            raise ValueError, "Unknown stage=" + stage_id + " specified in scenario tree constructor (stage->variable map)"
241
242         stage = self._stage_map[stage_id]
243         variable_ids = stage_variable_ids[stage_id]
244
245         for variable_string in variable_ids:
246
247            if isVariableNameIndexed(variable_string) is True:
248
249               variable_name, index_template = extractVariableNameAndIndex(variable_string)
250
251               # validate that the variable exists and extract the reference.
252               if variable_name not in self._reference_instance.active_components(Var):
253                  raise ValueError, "Variable=" + variable_name + " associated with stage=" + stage_id + " is not present in model=" + self._reference_instance.name
254               variable = self._reference_instance.active_components(Var)[variable_name]               
255
256               # extract all "real", i.e., fully specified, indices matching the index template.
257               match_indices = extractVariableIndices(variable, index_template)               
258
259               # there is a possibility that no indices match the input template.
260               # if so, let the user know about it.
261               if len(match_indices) == 0:
262                  raise RuntimeError, "No indices match template="+str(index_template)+" for variable="+variable_name+" ; encountered in scenario tree specification for model="+self._reference_instance.name
263                 
264               stage._variables.append((variable, index_template, match_indices))
265
266            else:
267
268               # verify that the variable exists.
269               if variable_string not in self._reference_instance.active_components(Var).keys():
270                  raise RuntimeError, "Unknown variable=" + variable_string + " associated with stage=" + stage_id + " is not present in model=" + self._reference_instance.name
271
272               variable = self._reference_instance.active_components(Var)[variable_string]
273
274               # 9/14/2009 - now forcing the user to explicit specify the full
275               # match template (e.g., "foo[*,*]") instead of just the variable
276               # name (e.g., "foo") to represent the set of all indices.
277               
278               # if the variable is a singleton - that is, non-indexed - no brackets is fine.
279               # we'll just tag the var[None] variable value with the (suffix,value) pair.
280               if None not in variable._index:
281                  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                 
282
283               match_indices = []
284               match_indices.append(None)
285
286               stage._variables.append((variable, "", match_indices))
287
288      for stage_id in stage_cost_variable_ids.keys():
289
290         if stage_id not in self._stage_map.keys():
291            raise ValueError, "Unknown stage=" + stage_id + " specified in scenario tree constructor (stage->cost variable map)"
292         stage = self._stage_map[stage_id]
293         
294         cost_variable_string = stage_cost_variable_ids[stage_id].value # de-reference is required to access the parameter value
295
296         # to be extracted from the string.
297         cost_variable_name = None
298         cost_variable = None
299         cost_variable_index = None
300
301         # do the extraction.
302         if isVariableNameIndexed(cost_variable_string) is True:
303
304            cost_variable_name, index_template = extractVariableNameAndIndex(cost_variable_string)
305
306            # validate that the variable exists and extract the reference.
307            if cost_variable_name not in self._reference_instance.active_components(Var):
308               raise ValueError, "Variable=" + cost_variable_name + " associated with stage=" + stage_id + " is not present in model=" + self._reference_instance.name
309            cost_variable = self._reference_instance.active_components(Var)[cost_variable_name]               
310
311            # extract all "real", i.e., fully specified, indices matching the index template.
312            match_indices = extractVariableIndices(cost_variable, index_template)
313
314            # only one index can be supplied for a stage cost variable.
315            if len(match_indices) != 1:
316               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
317
318            cost_variable_index = match_indices[0]
319
320         else:
321
322            cost_variable_name = cost_variable_string
323
324            # validate that the variable exists and extract the reference
325            if cost_variable_name not in self._reference_instance.active_components(Var):
326               raise ValueError, "Cost variable=" + cost_variable_name + " associated with stage=" + stage_id + " is not present in model=" + self._reference_instance.name
327            cost_variable = self._reference_instance.active_components(Var)[cost_variable_name]
328           
329         # store the validated info.
330         stage._cost_variable = (cost_variable, cost_variable_index)
331
332      # for output purposes, it is useful to known the maximal length of identifiers
333      # in the scenario tree for any particular category. I'm building these up
334      # incrementally, as they are needed. 0 indicates unassigned.
335      self._max_scenario_id_length = 0
336
337      # does the actual traversal to populate the members.
338      self.computeIdentifierMaxLengths()
339
340      # if a sub-bundle of scenarios has been specified, mark the
341      # active scenario tree components and compress the tree.
342      if scenario_bundle_list is not None:
343         print "Compressing scenario tree!"
344         self.compress(scenario_bundle_list)
345
346   #
347   # is the indicated scenario in the tree?
348   #
349   def contains_scenario(self, name):
350      return name in self._scenario_map.keys()
351
352   #
353   # get the scenario object from the tree.
354   #
355   def get_scenario(self, name):
356      return self._scenario_map[name]
357
358   #
359   # utility for compressing or culling a scenario tree based on
360   # a provided list of scenarios (specified by name) to retain -
361   # all non-referenced components are eliminated. this particular
362   # method compresses *in-place*, i.e., via direct modification
363   # of the scenario tree structure.
364   #
365   def compress(self, scenario_bundle_list):
366
367      # scan for and mark all referenced scenarios and
368      # tree nodes in the bundle list - all stages will
369      # obviously remain.
370      for scenario_name in scenario_bundle_list:
371         if scenario_name not in self._scenario_map:
372            raise ValueError, "Scenario="+scenario_name+" selected for bundling not present in scenario tree"
373         scenario = self._scenario_map[scenario_name]
374         scenario.retain = True
375
376         # chase all nodes comprising this scenario,
377         # marking them for retention.
378         for node in scenario._node_list:
379            node.retain = True
380
381      # scan for any non-retained scenarios and tree nodes.
382      scenarios_to_delete = []
383      tree_nodes_to_delete = []
384      for scenario in self._scenarios:
385         if hasattr(scenario, "retain") is True:
386            delattr(scenario, "retain")
387            pass
388         else:
389            scenarios_to_delete.append(scenario)             
390            del self._scenario_map[scenario._name]
391
392      for tree_node in self._tree_nodes:
393         if hasattr(tree_node, "retain") is True:
394            delattr(tree_node, "retain")
395            pass
396         else:
397            tree_nodes_to_delete.append(tree_node)
398            del self._tree_node_map[tree_node._name]
399
400      # JPW does not claim the following routines are
401      # the most efficient. rather, they get the job
402      # done while avoiding serious issues with
403      # attempting to remove elements from a list that
404      # you are iterating over.
405
406      # delete all references to unmarked scenarios
407      # and child tree nodes in the scenario tree node
408      # structures.
409      for tree_node in self._tree_nodes:
410         for scenario in scenarios_to_delete:
411            if scenario in tree_node._scenarios:
412               tree_node._scenarios.remove(scenario)
413         for node_to_delete in tree_nodes_to_delete:
414            if node_to_delete in tree_node._children:
415               tree_node._children.remove(node_to_delete)
416
417      # delete all references to unmarked tree nodes
418      # in the scenario tree stage structures.
419      for stage in self._stages:
420         for tree_node in tree_nodes_to_delete:
421            if tree_node in stage._tree_nodes:
422               stage._tree_nodes.remove(tree_node)
423
424      # delete all unreferenced entries from the core scenario
425      # tree data structures.
426      for scenario in scenarios_to_delete:
427         self._scenarios.remove(scenario)
428      for tree_node in tree_nodes_to_delete:
429         self._tree_nodes.remove(tree_node)
430
431
432      # re-normalize the conditional probabilities of the
433      # children at each tree node.
434      for tree_node in self._tree_nodes:
435         sum_child_probabilities = 0.0
436         for child_node in tree_node._children:
437            sum_child_probabilities += child_node._conditional_probability
438         for child_node in tree_node._children:
439            child_node._conditional_probability = child_node._conditional_probability / sum_child_probabilities
440
441      # re-compute the absolute scenario probabilities based
442      # on the re-normalized conditional node probabilities.
443      for scenario in self._scenarios:
444         probability = 1.0
445         for tree_node in scenario._node_list:
446            probability = probability * tree_node._conditional_probability
447         scenario._probability = probability
448
449   #
450   # returns the root node of the scenario tree
451   #
452   def findRootNode(self):
453
454      for tree_node in self._tree_nodes:
455         if tree_node._parent is None:
456            return tree_node
457      return None
458
459   #
460   # a utility function to compute, based on the current scenario tree content,
461   # the maximal length of identifiers in various categories.
462   #
463   def computeIdentifierMaxLengths(self):
464
465      self._max_scenario_id_length = 0
466      for scenario in self._scenarios:
467         if len(scenario._name) > self._max_scenario_id_length:
468            self._max_scenario_id_length = len(scenario._name)
469
470   #
471   # a utility function to (partially, at the moment) validate a scenario tree
472   #
473   def validate(self):
474
475      # for any node, the sum of conditional probabilities of the children should sum to 1.
476      for tree_node in self._tree_nodes:
477         sum_probabilities = 0.0
478         if len(tree_node._children) > 0:
479            for child in tree_node._children:
480               sum_probabilities += child._conditional_probability
481            if abs(1.0 - sum_probabilities) > 0.000001:
482               print "The child conditional probabilities for tree node=" + tree_node._name + " sum to " + `sum_probabilities`
483               return False
484
485      # ensure that there is only one root node in the tree
486      num_roots = 0
487      root_ids = []
488      for tree_node in self._tree_nodes:
489         if tree_node._parent is None:
490            num_roots += 1
491            root_ids.append(tree_node._name)
492
493      if num_roots != 1:
494         print "Illegal set of root nodes detected: " + `root_ids`
495         return False
496
497      # there must be at least one scenario passing through each tree node.
498      for tree_node in self._tree_nodes:
499         if len(tree_node._scenarios) == 0:
500            print "There are no scenarios associated with tree node=" + tree_node._name
501            return False
502     
503      return True
504
505   #
506   # a utility function to pretty-print the static/non-cost information associated with a scenario tree
507   #
508
509   def pprint(self):
510
511      print "Scenario Tree Detail"
512
513      print "----------------------------------------------------"
514      if self._reference_instance is not None:
515         print "Model=" + self._reference_instance.name
516      else:
517         print "Model=" + "Unassigned"
518      print "----------------------------------------------------"         
519      print "Tree Nodes:"
520      print ""
521      for tree_node_name in sorted(self._tree_node_map.keys()):
522         tree_node = self._tree_node_map[tree_node_name]
523         print "\tName=" + tree_node_name
524         if tree_node._stage is not None:
525            print "\tStage=" + tree_node._stage._name
526         else:
527            print "\t Stage=None"
528         if tree_node._parent is not None:
529            print "\tParent=" + tree_node._parent._name
530         else:
531            print "\tParent=" + "None"
532         if tree_node._conditional_probability is not None:
533            print "\tConditional probability=%4.4f" % tree_node._conditional_probability
534         else:
535            print "\tConditional probability=" + "***Undefined***"
536         print "\tChildren:"
537         if len(tree_node._children) > 0:
538            for child_node in sorted(tree_node._children, cmp=lambda x,y: cmp(x._name, y._name)):
539               print "\t\t" + child_node._name
540         else:
541            print "\t\tNone"
542         print "\tScenarios:"
543         if len(tree_node._scenarios) == 0:
544            print "\t\tNone"
545         else:
546            for scenario in sorted(tree_node._scenarios, cmp=lambda x,y: cmp(x._name, y._name)):
547               print "\t\t" + scenario._name
548         print ""
549      print "----------------------------------------------------"
550      print "Stages:"
551      for stage_name in sorted(self._stage_map.keys()):
552         stage = self._stage_map[stage_name]
553         print "\tName=" + stage_name
554         print "\tTree Nodes: "
555         for tree_node in sorted(stage._tree_nodes, cmp=lambda x,y: cmp(x._name, y._name)):
556            print "\t\t" + tree_node._name
557         print "\tVariables: "
558         for (variable, index_template, indices) in stage._variables:
559            if (len(indices) == 1) and (indices[0] == None):
560               print "\t\t" + variable.name
561            else:
562               print "\t\t",variable.name,":",index_template
563         print "\tCost Variable: "           
564         if stage._cost_variable[1] is None:
565            print "\t\t" + stage._cost_variable[0].name
566         else:
567            print "\t\t" + stage._cost_variable[0].name + indexToString(stage._cost_variable[1])
568         print ""           
569      print "----------------------------------------------------"
570      print "Scenarios:"
571      for scenario_name in sorted(self._scenario_map.keys()):
572         scenario = self._scenario_map[scenario_name]
573         print "\tName=" + scenario_name
574         print "\tProbability=%4.4f" % scenario._probability
575         if scenario._leaf_node is None:
576            print "\tLeaf node=None"
577         else:
578            print "\tLeaf node=" + scenario._leaf_node._name
579         print "\tTree node sequence:"
580         for tree_node in scenario._node_list:
581            print "\t\t" + tree_node._name
582         print ""                       
583      print "----------------------------------------------------"
584
585   #
586   # a utility function to pretty-print the cost information associated with a scenario tree
587   #
588
589   def pprintCosts(self, scenario_instance_map):
590
591      print "Scenario Tree Costs"
592      print "***WARNING***: Assumes full (or nearly so) convergence of scenario solutions at each node in the scenario tree - computed costs are invalid otherwise"
593
594      print "----------------------------------------------------"
595      if self._reference_instance is not None:
596         print "Model=" + self._reference_instance.name
597      else:
598         print "Model=" + "Unassigned"
599
600      print "----------------------------------------------------"     
601      print "Tree Nodes:"
602      print ""
603      for tree_node_name in sorted(self._tree_node_map.keys()):
604         tree_node = self._tree_node_map[tree_node_name]
605         print "\tName=" + tree_node_name
606         if tree_node._stage is not None:
607            print "\tStage=" + tree_node._stage._name
608         else:
609            print "\t Stage=None"
610         if tree_node._parent is not None:
611            print "\tParent=" + tree_node._parent._name
612         else:
613            print "\tParent=" + "None"
614         if tree_node._conditional_probability is not None:
615            print "\tConditional probability=%4.4f" % tree_node._conditional_probability
616         else:
617            print "\tConditional probability=" + "***Undefined***"
618         print "\tChildren:"
619         if len(tree_node._children) > 0:
620            for child_node in sorted(tree_node._children, cmp=lambda x,y: cmp(x._name, y._name)):
621               print "\t\t" + child_node._name
622         else:
623            print "\t\tNone"
624         print "\tScenarios:"
625         if len(tree_node._scenarios) == 0:
626            print "\t\tNone"
627         else:
628            for scenario in sorted(tree_node._scenarios, cmp=lambda x,y: cmp(x._name, y._name)):
629               print "\t\t" + scenario._name
630         print "\tExpected node cost=%10.4f" % tree_node.computeExpectedNodeCost(scenario_instance_map)
631         print ""
632
633      print "----------------------------------------------------"
634      print "Scenarios:"
635      print ""
636      for scenario_name, scenario in sorted(self._scenario_map.iteritems()):
637         instance = scenario_instance_map[scenario_name]
638
639         print "\tName=" + scenario_name
640         print "\tProbability=%4.4f" % scenario._probability
641
642         if scenario._leaf_node is None:
643            print "\tLeaf Node=None"
644         else:
645            print "\tLeaf Node=" + scenario._leaf_node._name
646
647         print "\tTree node sequence:"
648         for tree_node in scenario._node_list:
649            print "\t\t" + tree_node._name
650
651         aggregate_cost = 0.0
652         for stage in self._stages:
653            instance_cost_variable = instance.active_components(Var)[stage._cost_variable[0].name][stage._cost_variable[1]]()
654            print "\tStage=%20s     Cost=%10.4f" % (stage._name, instance_cost_variable)
655            aggregate_cost += instance_cost_variable
656         print "\tTotal scenario cost=%10.4f" % aggregate_cost
657         print ""
658      print "----------------------------------------------------"     
Note: See TracBrowser for help on using the repository browser.