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

Last change on this file since 2414 was 2414, checked in by jwatson, 9 years ago

Various EF writer changes in PySP, including:
1) Added a --verbose option, and by default am disabling echo of the scenario tree.
2) Changed the default behavior, due to recent experience with lot sizing and wind farm examples, to enable garbage collection.
3) Added a --disable-gc option to disable garbage collection, set to False by default.

File size: 42.6 KB
Line 
1import pyutilib
2import sys
3import os
4import time
5import traceback
6import copy
7
8from coopr.pysp.scenariotree import *
9from coopr.pysp.convergence import *
10from coopr.pysp.ph import *
11
12from coopr.pyomo.base import *
13from coopr.pyomo.io import *
14
15from coopr.pyomo.base.var import _VarValue, _VarBase
16
17#
18# brain-dead utility for determing if there is a binary to write in the
19# composite model - need to know this, because CPLEX doesn't like empty
20# binary blocks in the LP file.
21#
22
23def binaries_present(master_model, scenario_instances):
24
25   # check the master model first.
26   for var in master_model.active_components(Var).values():
27      if isinstance(var.domain, BooleanSet):
28         return True
29
30   # scan the scenario instances next.
31   for scenario_name in scenario_instances.keys():
32      scenario_instance = scenario_instances[scenario_name]
33      for var in scenario_instance.active_components(Var).values():
34         if isinstance(var.domain, BooleanSet):
35            return True
36
37   return False
38
39#
40# brain-dead utility for determing if there is a binary to write in the
41# composite model - need to know this, because CPLEX doesn't like empty
42# integer blocks in the LP file.
43#
44
45def integers_present(master_model, scenario_instances):
46
47   # check the master model first.
48   for var in master_model.active_components(Var).values():
49      if isinstance(var.domain, IntegerSet):
50         return True
51
52   # scan the scenario instances next.
53   for scenario_name in scenario_instances.keys():
54      scenario_instance = scenario_instances[scenario_name]
55      for var in scenario_instance.active_components(Var).values():
56         if isinstance(var.domain, IntegerSet):
57            return True
58
59   return False
60
61#
62# a routine to create the extensive form, given an input scenario tree and instances.
63# IMPT: unlike scenario instances, the extensive form instance is *not* self-contained.
64#       in particular, it has binding constraints that cross the binding instance and
65#       the scenario instances. it is up to the caller to keep track of which scenario
66#       instances are associated with the extensive form. this might be something we
67#       encapsulate at some later time.
68#
69
70def create_ef_instance(scenario_tree, scenario_instances, generate_weighted_cvar, verbose_output):
71
72   binding_instance = Model()
73   binding_instance.name = "MASTER"
74
75   # walk the scenario tree - create variables representing the common values for all scenarios
76   # associated with that node. the constraints will be created later. also create expected-cost
77   # variables for each node, to be computed via constraints/objectives defined in a subsequent pass.
78   # master variables are created for all nodes but those in the last stage. expected cost variables
79   # are, for no particularly good reason other than easy coding, created for nodes in all stages.
80   if verbose_output is True:
81      print "Creating variables for master binding instance"
82
83   for stage in scenario_tree._stages:
84
85      for (stage_variable, index_template, stage_variable_indices) in stage._variables:
86
87         if verbose_output is True:
88            print "Creating master variable and blending constraints for decision variable=", stage_variable, ", indices=", index_template
89
90         for tree_node in stage._tree_nodes:
91
92            if stage != scenario_tree._stages[-1]:     
93
94               master_variable_name = tree_node._name + "_" + stage_variable.name
95
96               # because there may be a single stage variable and multiple indices, check
97               # for the existence of the variable at this node - if you don't, you'll
98               # inadvertently over-write what was there previously!
99               master_variable = None
100               try:
101                  master_variable = getattr(binding_instance, master_variable_name)
102               except:
103                  new_master_variable_index = stage_variable._index
104                  new_master_variable = None
105                  if (len(new_master_variable_index) is 1) and (None in new_master_variable_index):
106                     new_master_variable = Var(name=stage_variable.name)
107                  else:
108                     new_master_variable = Var(new_master_variable_index, name=stage_variable.name)
109                  new_master_variable.construct()
110                  new_master_variable._model = binding_instance
111                  setattr(master_binding_instance, master_variable_name, new_master_variable)
112
113                  master_variable = new_master_variable
114
115               for index in stage_variable_indices:
116
117                  is_used = True # until proven otherwise                     
118                  for scenario in tree_node._scenarios:
119                     instance = scenario_instances[scenario._name]
120                     if getattr(instance,stage_variable.name)[index].status == VarStatus.unused:
121                        is_used = False
122
123                  is_fixed = False # until proven otherwise
124                  for scenario in tree_node._scenarios:
125                     instance = scenario_instances[scenario._name]
126                     if getattr(instance,stage_variable.name)[index].fixed is True:
127                        is_fixed = True
128
129                  if (is_used is True) and (is_fixed is False):
130                           
131                     # the following is necessary, specifically to get the name - deepcopy won't reset these attributes.
132                     # and because presolve/simplification is name-based, the names *have* to be different.
133                     master_variable[index].var = master_variable
134                     master_variable[index].name = tree_node._name + "_" + master_variable[index].name
135
136                     for scenario in tree_node._scenarios:
137
138                        scenario_instance = scenario_instances[scenario._name]
139                        scenario_variable = getattr(scenario_instance, stage_variable.name)
140                        new_constraint_name = scenario._name + "_" + master_variable_name + "_" + str(index)
141                        new_constraint = Constraint(name=new_constraint_name)
142                        new_expr = master_variable[index] - scenario_variable[index]
143                        new_constraint.add(None, (0.0, new_expr, 0.0))
144                        new_constraint._model = master_binding_instance
145                        setattr(master_binding_instance, new_constraint_name, new_constraint)
146
147            # create a variable to represent the expected cost at this node -
148            # the constraint to compute this comes later.
149            expected_cost_variable_name = "EXPECTED_COST_" + tree_node._name
150            expected_cost_variable = Var(name=expected_cost_variable_name)
151            expected_cost_variable._model = master_binding_instance
152            setattr(master_binding_instance, expected_cost_variable_name, expected_cost_variable)
153
154   master_binding_instance.preprocess()
155
156   # ditto above for the (non-expected) cost variable.
157   for stage in scenario_tree._stages:
158
159      (cost_variable,cost_variable_index) = stage._cost_variable
160
161      if verbose_output:
162         print "Creating master variable and blending constraints for cost variable=", cost_variable, ", index=", cost_variable_index     
163
164      for tree_node in stage._tree_nodes:
165
166         new_cost_variable_name = tree_node._name + "_" + cost_variable.name
167
168         # TBD - the following is bad - check to see if it's already there (I suspect some of them are!!!)
169
170         # this is undoubtedly wasteful, in that a cost variable
171         # for each tree node is created with *all* indices.
172         new_cost_variable_name = tree_node._name + "_" + cost_variable.name
173         new_cost_variable_index = cost_variable._index
174         new_cost_variable = None
175         if (len(new_cost_variable_index) is 1) and (None in new_cost_variable_index):
176            new_cost_variable = Var(name=new_cost_variable_name)
177         else:
178            new_cost_variable = Var(new_cost_variable_index, new_cost_variable_name)
179         new_cost_variable.construct()
180         new_cost_variable._model = master_binding_instance
181         setattr(master_binding_instance, new_cost_variable_name, new_cost_variable)                 
182
183         # the following is necessary, specifically to get the name - deepcopy won't reset these attributes.
184         new_cost_variable[cost_variable_index].var = new_cost_variable
185         if cost_variable_index is not None:
186            # if the variable index is None, the variable is derived from a VarValue, so the
187            # name gets updated automagically.
188            new_cost_variable[cost_variable_index].name = tree_node._name + "_" + new_cost_variable[cost_variable_index].name
189
190         for scenario in tree_node._scenarios:
191
192            scenario_instance = scenario_instances[scenario._name]
193            scenario_cost_variable = getattr(scenario_instance, cost_variable.name)
194            new_constraint_name = scenario._name + "_" + new_cost_variable_name + "_" + str(cost_variable_index)
195            new_constraint = Constraint(name=new_constraint_name)
196            new_expr = new_cost_variable[cost_variable_index] - scenario_cost_variable[cost_variable_index]
197            new_constraint.add(None, (0.0, new_expr, 0.0))
198            new_constraint._model = master_binding_instance
199            setattr(master_binding_instance, new_constraint_name, new_constraint)
200
201   # create the constraints for computing the master per-node cost variables,
202   # i.e., the current node cost and the expected cost of the child nodes.
203   # if the root, then the constraint is just the objective.
204
205   for stage in scenario_tree._stages:
206
207      (stage_cost_variable,stage_cost_variable_index) = stage._cost_variable
208
209      for tree_node in stage._tree_nodes:
210
211         node_expected_cost_variable_name = "EXPECTED_COST_" + tree_node._name
212         node_expected_cost_variable = getattr(master_binding_instance, node_expected_cost_variable_name)
213
214         node_cost_variable_name = tree_node._name + "_" + stage_cost_variable.name
215         node_cost_variable = getattr(master_binding_instance, node_cost_variable_name)                       
216           
217         constraint_expr = node_expected_cost_variable - node_cost_variable[stage_cost_variable_index]
218
219         for child_node in tree_node._children:
220
221            child_node_expected_cost_variable_name = "EXPECTED_COST_" + child_node._name
222            child_node_expected_cost_variable = getattr(master_binding_instance, child_node_expected_cost_variable_name)
223            constraint_expr = constraint_expr - (child_node._conditional_probability * child_node_expected_cost_variable)
224
225         new_constraint_name = "COST" + "_" + node_cost_variable_name + "_" + str(cost_variable_index)
226         new_constraint = Constraint(name=new_constraint_name)
227         new_constraint.add(None, (0.0, constraint_expr, 0.0))
228         new_constraint._model = master_binding_instance                     
229         setattr(master_binding_instance, new_constraint_name, new_constraint)
230
231         if tree_node._parent is None:
232
233            an_instance = scenario_instances[scenario_instances.keys()[0]]
234            an_objective = an_instance.active_components(Objective)
235            opt_sense = an_objective[an_objective.keys()[0]].sense
236
237            new_objective = Objective(name="MASTER", sense=opt_sense)
238            new_objective._data[None].expr = node_expected_cost_variable
239            setattr(master_binding_instance, "MASTER", new_objective)
240
241   master_binding_instance.preprocess()
242
243   return master_binding_instance
244
245#
246# the main extensive-form writer routine - including read of scenarios/etc.
247#
248
249def write_ef_from_scratch(model_directory, instance_directory, output_filename,
250                          verbose_output,
251                          generate_weighted_cvar, cvar_weight, risk_alpha):
252
253   start_time = time.time()
254
255   scenario_data_directory_name = instance_directory
256
257   print "Initializing extensive form writer"
258   print ""
259
260   ################################################################################################
261   #### INITIALIZATION ############################################################################
262   ################################################################################################
263
264   #
265   # validate cvar options, if specified.
266   #
267   if generate_weighted_cvar is True:
268      if cvar_weight <= 0.0:
269         raise RuntimeError, "Weight of CVaR term must be >= 0.0 - value supplied="+str(cvar_weight)
270      if (risk_alpha <= 0.0) or (risk_alpha >= 1.0):
271         raise RuntimeError, "CVaR risk alpha must be between 0 and 1, exclusive - value supplied="+str(risk_alpha)
272
273      print "Writing CVaR weighted objective"
274      print "CVaR term weight="+str(cvar_weight)
275      print "CVaR alpha="+str(risk_alpha)
276      print ""
277   
278   #
279   # create and populate the core model
280   #
281   master_scenario_model = None
282   master_scenario_instance = None
283   
284   try:
285     
286      reference_model_filename = model_directory+os.sep+"ReferenceModel.py"   
287      modelimport = pyutilib.misc.import_file(reference_model_filename)
288      if "model" not in dir(modelimport):
289         print ""
290         print "Exiting ef module: No 'model' object created in module "+reference_model_filename
291         sys.exit(0)
292      if modelimport.model is None:
293         print ""
294         print "Exiting ef module: 'model' object equals 'None' in module "+reference_model_filename
295         sys.exit(0)
296   
297      master_scenario_model = modelimport.model
298
299   except IOError:
300     
301      print "***ERROR: Failed to load scenario reference model from file="+reference_model_filename
302      return
303
304   try:
305     
306      reference_scenario_filename = instance_directory+os.sep+"ReferenceModel.dat"
307      master_scenario_instance = master_scenario_model.create(reference_scenario_filename)
308     
309   except IOError:
310     
311      print "***ERROR: Failed to load scenario reference instance data from file="+reference_scenario_filename
312      return           
313
314   #
315   # create and populate the scenario tree model
316   #
317
318   from coopr.pysp.util.scenariomodels import scenario_tree_model
319
320   scenario_tree_instance = scenario_tree_model.create(instance_directory+os.sep+"ScenarioStructure.dat")
321
322   #
323   # construct the scenario tree
324   #
325   scenario_tree = ScenarioTree(scenarioinstance=master_scenario_instance,
326                                scenariotreeinstance=scenario_tree_instance)
327
328   #
329   # print the input tree for validation/information purposes.
330   #
331   if verbose_output is True:
332      scenario_tree.pprint()
333
334   #
335   # validate the tree prior to doing anything serious
336   #
337   print ""
338   if scenario_tree.validate() is False:
339      print "***Scenario tree is invalid****"
340      sys.exit(1)
341   else:
342      print "Scenario tree is valid!"
343   print ""
344
345   #
346   # construct instances for each scenario
347   #
348
349   instances = {}
350   
351   if scenario_tree._scenario_based_data == 1:
352      print "Scenario-based instance initialization enabled"
353   else:
354      print "Node-based instance initialization enabled"
355         
356   for scenario in scenario_tree._scenarios:
357
358      scenario_instance = None
359
360      print "Creating instance for scenario=" + scenario._name
361
362      try:
363         if scenario_tree._scenario_based_data == 1:
364            scenario_data_filename = scenario_data_directory_name + os.sep + scenario._name + ".dat"
365#            print "Data for scenario=" + scenario._name + " loads from file=" + scenario_data_filename
366            scenario_instance = master_scenario_model.create(scenario_data_filename)
367         else:
368            scenario_instance = master_scenario_model.clone()
369            scenario_data = ModelData()
370            current_node = scenario._leaf_node
371            while current_node is not None:
372               node_data_filename = scenario_data_directory_name + os.sep + current_node._name + ".dat"
373#               print "Node data for scenario=" + scenario._name + " partially loading from file=" + node_data_filename
374               scenario_data.add(node_data_filename)
375               current_node = current_node._parent
376            scenario_data.read(model=scenario_instance)
377            scenario_instance._load_model_data(scenario_data)
378            scenario_instance.preprocess()
379      except:
380         print "Encountered exception in model instance creation - traceback:"
381         traceback.print_exc()
382         raise RuntimeError, "Failed to create model instance for scenario=" + scenario._name
383
384      # name each instance with the scenario name, so the prefixes in the EF make sense.
385      scenario_instance.name = scenario._name
386     
387      scenario_instance.preprocess()
388      instances[scenario._name] = scenario_instance
389
390   print ""
391
392   ################################################################################################
393   #### CREATE THE MASTER / BINDING INSTANCE ######################################################
394   ################################################################################################
395
396   master_binding_instance = Model()
397   master_binding_instance.name = "MASTER"
398
399   # walk the scenario tree - create variables representing the common values for all scenarios
400   # associated with that node. the constraints will be created later. also create expected-cost
401   # variables for each node, to be computed via constraints/objectives defined in a subsequent pass.
402   # master variables are created for all nodes but those in the last stage. expected cost variables
403   # are, for no particularly good reason other than easy coding, created for nodes in all stages.
404   print "Creating variables for master binding instance"
405
406   for stage in scenario_tree._stages:
407
408      for (stage_variable, index_template, stage_variable_indices) in stage._variables:
409
410         print "Creating master variable and blending constraints for decision variable="+stage_variable.name+", indices="+str(stage_variable_indices)
411
412         for tree_node in stage._tree_nodes:
413
414            if stage != scenario_tree._stages[-1]:     
415
416               master_variable_name = tree_node._name + "_" + stage_variable.name
417
418               # because there may be a single stage variable and multiple indices, check
419               # for the existence of the variable at this node - if you don't, you'll
420               # inadvertently over-write what was there previously!
421               master_variable = None
422               try:
423                  master_variable = getattr(master_binding_instance, master_variable_name)
424               except:
425                  new_master_variable_index = stage_variable._index
426                  new_master_variable = None
427                  if (len(new_master_variable_index) is 1) and (None in new_master_variable_index):
428                     new_master_variable = Var(name=stage_variable.name)
429                  else:
430                     new_master_variable = Var(new_master_variable_index, name=stage_variable.name)
431                  new_master_variable.construct()
432                  new_master_variable._model = master_binding_instance
433                  setattr(master_binding_instance, master_variable_name, new_master_variable)
434
435                  # TBD - TECHNICALLY, WE NEED TO COPY BOUNDS - BUT WE REALLY DON'T, AS THEY ARE ON THE PER-INSTNACE VARS!
436
437                  master_variable = new_master_variable
438
439               for index in stage_variable_indices:
440
441                  is_used = True # until proven otherwise                     
442                  for scenario in tree_node._scenarios:
443                     instance = instances[scenario._name]
444                     if getattr(instance,stage_variable.name)[index].status == VarStatus.unused:
445                        is_used = False
446
447                  is_fixed = False # until proven otherwise
448                  for scenario in tree_node._scenarios:
449                     instance = instances[scenario._name]
450                     if getattr(instance,stage_variable.name)[index].fixed is True:
451                        is_fixed = True
452
453                  if (is_used is True) and (is_fixed is False):
454                           
455                     # the following is necessary, specifically to get the name - deepcopy won't reset these attributes.
456                     # and because presolve/simplification is name-based, the names *have* to be different.
457                     master_variable[index].var = master_variable
458                     master_variable[index].name = tree_node._name + "_" + master_variable[index].name
459
460                     for scenario in tree_node._scenarios:
461
462                        scenario_instance = instances[scenario._name]
463                        scenario_variable = getattr(scenario_instance, stage_variable.name)
464                        new_constraint_name = scenario._name + "_" + master_variable_name + "_" + str(index)
465                        new_constraint = Constraint(name=new_constraint_name)
466                        new_expr = master_variable[index] - scenario_variable[index]
467                        new_constraint.add(None, (0.0, new_expr, 0.0))
468                        new_constraint._model = master_binding_instance
469                        setattr(master_binding_instance, new_constraint_name, new_constraint)
470
471            # create a variable to represent the expected cost at this node -
472            # the constraint to compute this comes later.
473            expected_cost_variable_name = "EXPECTED_COST_" + tree_node._name
474            expected_cost_variable = Var(name=expected_cost_variable_name)
475            expected_cost_variable._model = master_binding_instance
476            setattr(master_binding_instance, expected_cost_variable_name, expected_cost_variable)
477
478   # if we're generating the weighted CVaR objective term, create the corresponding variable and
479   # the master CVaR eta variable.
480   if generate_weighted_cvar is True:
481      root_node = scenario_tree._stages[0]._tree_nodes[0]
482     
483      cvar_cost_variable_name = "CVAR_COST_" + root_node._name
484      cvar_cost_variable = Var(name=cvar_cost_variable_name)
485      setattr(master_binding_instance, cvar_cost_variable_name, cvar_cost_variable)
486      cvar_cost_variable.construct()
487
488      cvar_eta_variable_name = "CVAR_ETA_" + root_node._name
489      cvar_eta_variable = Var(name=cvar_eta_variable_name)
490      setattr(master_binding_instance, cvar_eta_variable_name, cvar_eta_variable)     
491      cvar_eta_variable.construct()
492
493   master_binding_instance.preprocess()
494
495   # ditto above for the (non-expected) cost variable.
496   for stage in scenario_tree._stages:
497
498      (cost_variable,cost_variable_index) = stage._cost_variable
499
500      print "Creating master variable and blending constraints for cost variable="+cost_variable.name+", index="+str(cost_variable_index)
501
502      for tree_node in stage._tree_nodes:
503
504         # TBD - the following is bad - check to see if it's already there (I suspect some of them are!!!)         
505
506         # this is undoubtedly wasteful, in that a cost variable
507         # for each tree node is created with *all* indices.         
508         new_cost_variable_name = tree_node._name + "_" + cost_variable.name
509         new_cost_variable_index = cost_variable._index
510         new_cost_variable = None
511         if (len(new_cost_variable_index) is 1) and (None in new_cost_variable_index):
512            new_cost_variable = Var(name=new_cost_variable_name)
513         else:
514            new_cost_variable = Var(new_cost_variable_index, name=new_cost_variable_name)
515         new_cost_variable.construct()
516         new_cost_variable._model = master_binding_instance
517         setattr(master_binding_instance, new_cost_variable_name, new_cost_variable)         
518
519         # the following is necessary, specifically to get the name - deepcopy won't reset these attributes.
520         new_cost_variable[cost_variable_index].var = new_cost_variable
521         if cost_variable_index is not None:
522            # if the variable index is None, the variable is derived from a VarValue, so the
523            # name gets updated automagically.
524            new_cost_variable[cost_variable_index].name = tree_node._name + "_" + new_cost_variable[cost_variable_index].name
525
526         for scenario in tree_node._scenarios:
527
528            scenario_instance = instances[scenario._name]
529            scenario_cost_variable = getattr(scenario_instance, cost_variable.name)
530            new_constraint_name = scenario._name + "_" + new_cost_variable_name + "_" + str(cost_variable_index)
531            new_constraint = Constraint(name=new_constraint_name)
532            new_expr = new_cost_variable[cost_variable_index] - scenario_cost_variable[cost_variable_index]
533            new_constraint.add(None, (0.0, new_expr, 0.0))
534            new_constraint._model = master_binding_instance
535            setattr(master_binding_instance, new_constraint_name, new_constraint)
536
537   # create the constraints for computing the master per-node cost variables,
538   # i.e., the current node cost and the expected cost of the child nodes.
539   # if the root, then the constraint is just the objective.
540
541   for stage in scenario_tree._stages:
542
543      (stage_cost_variable,stage_cost_variable_index) = stage._cost_variable
544
545      for tree_node in stage._tree_nodes:
546
547         node_expected_cost_variable_name = "EXPECTED_COST_" + tree_node._name
548         node_expected_cost_variable = getattr(master_binding_instance, node_expected_cost_variable_name)
549
550         node_cost_variable_name = tree_node._name + "_" + stage_cost_variable.name
551         node_cost_variable = getattr(master_binding_instance, node_cost_variable_name)                       
552           
553         constraint_expr = node_expected_cost_variable - node_cost_variable[stage_cost_variable_index]
554
555         for child_node in tree_node._children:
556
557            child_node_expected_cost_variable_name = "EXPECTED_COST_" + child_node._name
558            child_node_expected_cost_variable = getattr(master_binding_instance, child_node_expected_cost_variable_name)
559            constraint_expr = constraint_expr - (child_node._conditional_probability * child_node_expected_cost_variable)
560
561         new_constraint_name = "COST" + "_" + node_cost_variable_name + "_" + str(cost_variable_index)
562         new_constraint = Constraint(name=new_constraint_name)
563         new_constraint.add(None, (0.0, constraint_expr, 0.0))
564         new_constraint._model = master_binding_instance                     
565         setattr(master_binding_instance, new_constraint_name, new_constraint)
566
567         if tree_node._parent is None:
568
569            an_instance = instances[instances.keys()[0]]
570            an_objective = an_instance.active_components(Objective)
571            opt_sense = an_objective[an_objective.keys()[0]].sense
572
573            opt_expression = node_expected_cost_variable
574
575            if generate_weighted_cvar is True:
576               cvar_cost_variable_name = "CVAR_COST_" + tree_node._name
577               cvar_cost_variable = getattr(master_binding_instance, cvar_cost_variable_name)
578               opt_expression += cvar_weight * cvar_cost_variable
579
580            new_objective = Objective(name="MASTER", sense=opt_sense)
581            new_objective._data[None].expr = opt_expression
582            setattr(master_binding_instance, "MASTER", new_objective)
583
584   # CVaR requires the addition of a variable per scenario to represent the cost excess,
585   # and a constraint to compute the cost excess relative to eta. we also replicate (following
586   # what we do for node cost variables) an eta variable for each scenario instance, and
587   # require equality with the master eta variable via constraints.
588   if generate_weighted_cvar is True:
589     
590      root_node = scenario_tree._stages[0]._tree_nodes[0]
591
592      master_cvar_eta_variable_name = "CVAR_ETA_" + root_node._name
593      master_cvar_eta_variable = getattr(master_binding_instance, master_cvar_eta_variable_name)
594     
595      for scenario_name in instances.keys():
596         scenario_instance = instances[scenario_name]
597
598         # unique names are required because the presolve isn't
599         # aware of the "owning" models for variables.
600         cvar_excess_variable_name = "CVAR_EXCESS_"+scenario_name
601         cvar_excess_variable = Var(name=cvar_excess_variable_name, domain=NonNegativeReals)
602         setattr(scenario_instance, cvar_excess_variable_name, cvar_excess_variable)
603         cvar_excess_variable.construct()
604
605         cvar_eta_variable_name = "CVAR_ETA"
606         cvar_eta_variable = Var(name=cvar_eta_variable_name)
607         setattr(scenario_instance, cvar_eta_variable_name, cvar_eta_variable)
608         cvar_eta_variable.construct()
609
610         compute_excess_constraint_name = "COMPUTE_SCENARIO_EXCESS"
611         compute_excess_constraint = Constraint(name=compute_excess_constraint_name)
612         compute_excess_expression = cvar_excess_variable
613         for node in scenario_tree._scenario_map[scenario_name]._node_list:
614            (cost_variable, cost_variable_idx) = node._stage._cost_variable
615            compute_excess_expression -= getattr(scenario_instance, cost_variable.name)[cost_variable_idx]
616         compute_excess_expression += cvar_eta_variable
617         compute_excess_constraint.add(None, (0.0, compute_excess_expression, None))
618         compute_excess_constraint._model = scenario_instance
619         setattr(scenario_instance, compute_excess_constraint_name, compute_excess_constraint)
620
621         eta_equality_constraint_name = "MASTER_ETA_EQUALITY_WITH_" + scenario_instance.name
622         eta_equality_constraint = Constraint(name=eta_equality_constraint_name)
623         eta_equality_expr = master_cvar_eta_variable - cvar_eta_variable
624         eta_equality_constraint.add(None, (0.0, eta_equality_expr, 0.0))
625         eta_equality_constraint._model = master_binding_instance
626         setattr(master_binding_instance, eta_equality_constraint_name, eta_equality_constraint)
627
628      # add the constraint to compute the master CVaR variable value. iterate
629      # over scenario instances to create the expected excess component first.
630      cvar_cost_variable_name = "CVAR_COST_" + root_node._name
631      cvar_cost_variable = getattr(master_binding_instance, cvar_cost_variable_name)
632      cvar_eta_variable_name = "CVAR_ETA_" + root_node._name
633      cvar_eta_variable = getattr(master_binding_instance, cvar_eta_variable_name)
634     
635      cvar_cost_expression = cvar_cost_variable - cvar_eta_variable
636     
637      for scenario_name in instances.keys():
638         scenario_instance = instances[scenario_name]
639         scenario_probability = scenario_tree._scenario_map[scenario_name]._probability
640
641         scenario_excess_variable_name = "CVAR_EXCESS_"+scenario_name
642         scenario_excess_variable = getattr(scenario_instance, scenario_excess_variable_name)
643
644         cvar_cost_expression = cvar_cost_expression - (scenario_probability * scenario_excess_variable) / (1.0 - risk_alpha)
645
646      compute_cvar_cost_constraint_name = "COMPUTE_CVAR_COST"
647      compute_cvar_cost_constraint = Constraint(name=compute_cvar_cost_constraint_name)
648      compute_cvar_cost_constraint.add(None, (0.0, cvar_cost_expression, 0.0))
649      compute_cvar_cost_constraint._model = master_binding_instance
650      setattr(master_binding_instance, compute_cvar_cost_constraint_name, compute_cvar_cost_constraint)
651
652   # after mucking with instances, presolve to collect terms required prior to output.         
653   # IMPT: Do the scenario instances first, as the master depends on variables in the scenarios.
654   for scenario_name in instances.keys():
655      scenario_instance = instances[scenario_name]   
656      scenario_instance.preprocess()         
657
658   master_binding_instance.preprocess()
659
660   ################################################################################################
661   #### WRITE THE COMPOSITE MODEL #################################################################
662   ################################################################################################
663
664   print ""
665   print "Starting to write extensive form"
666
667   # create the output file.
668   problem_writer = cpxlp.ProblemWriter_cpxlp()
669   output_file = open(output_filename,"w")
670
671   problem_writer._output_prefixes = True # we always want prefixes
672
673   ################################################################################################
674   #### WRITE THE MASTER OBJECTIVE ################################################################
675   ################################################################################################
676
677   # write the objective for the master binding instance.
678   problem_writer._output_objectives = True
679   problem_writer._output_constraints = False
680   problem_writer._output_variables = False
681
682   print >>output_file, "\\ Begin objective block for master"
683   problem_writer._print_model_LP(master_binding_instance, output_file)
684   print >>output_file, "\\ End objective block for master"
685   print >>output_file, ""
686
687   ################################################################################################
688   #### WRITE THE CONSTRAINTS FOR THE MASTER MODEL AND ALL SCENARIO MODELS ########################
689   ################################################################################################
690
691   print >>output_file, "s.t."
692   print >>output_file, ""
693   
694   problem_writer._output_objectives = False
695   problem_writer._output_constraints = True
696   problem_writer._output_variables = False
697
698   print >>output_file, "\\ Begin constraint block for master"
699   problem_writer._print_model_LP(master_binding_instance, output_file)
700   print >>output_file, "\\ End constraint block for master",
701   print >>output_file, ""
702
703   for scenario_name in instances.keys():
704      instance = instances[scenario_name]
705      print >>output_file, "\\ Begin constraint block for scenario",scenario_name       
706      problem_writer._print_model_LP(instance, output_file)
707      print >>output_file, "\\ End constraint block for scenario",scenario_name
708      print >>output_file, ""
709
710   ################################################################################################
711   #### WRITE THE VARIABLES FOR THE MASTER MODEL AND ALL SCENARIO MODELS ##########################
712   ################################################################################################
713
714   # write the variables for the master binding instance, and then for each scenario.
715   print >>output_file, "bounds"
716   print >>output_file, ""
717   
718   problem_writer._output_objectives = False
719   problem_writer._output_constraints = False
720   problem_writer._output_variables = True
721
722   # first step: write variable bounds
723
724   problem_writer._output_continuous_variables = True
725   problem_writer._output_integer_variables = False
726   problem_writer._output_binary_variables = False
727
728   print >>output_file, "\\ Begin variable bounds block for master"
729   problem_writer._print_model_LP(master_binding_instance, output_file)
730   print >>output_file, "\\ End variable bounds block for master"
731   print >>output_file, ""
732
733   for scenario_name in instances.keys():
734      instance = instances[scenario_name]
735      print >>output_file, "\\ Begin variable bounds block for scenario",scenario_name
736      problem_writer._print_model_LP(instance, output_file)
737      print >>output_file, "\\ End variable bounds block for scenario",scenario_name
738      print >>output_file, ""
739
740   # second step: write integer indicators.
741
742   problem_writer._output_continuous_variables = False
743   problem_writer._output_integer_variables = True
744
745   if integers_present(master_binding_instance, instances) is True:
746
747      print >>output_file, "integer"
748      print >>output_file, ""
749
750      print >>output_file, "\\ Begin integer variable block for master"
751      problem_writer._print_model_LP(master_binding_instance, output_file)
752      print >>output_file, "\\ End integer variable block for master"
753      print >>output_file, ""
754   
755      for scenario_name in instances.keys():
756         instance = instances[scenario_name]
757         print >>output_file, "\\ Begin integer variable block for scenario",scenario_name
758         problem_writer._print_model_LP(instance, output_file)
759         print >>output_file, "\\ End integer variable block for scenario",scenario_name
760         print >>output_file, ""
761
762   # third step: write binary indicators.
763
764   problem_writer._output_integer_variables = False
765   problem_writer._output_binary_variables = True
766
767   if binaries_present(master_binding_instance, instances) is True:
768
769      print >>output_file, "binary"
770      print >>output_file, ""
771
772      print >>output_file, "\\ Begin binary variable block for master"
773      problem_writer._print_model_LP(master_binding_instance, output_file)
774      print >>output_file, "\\ End binary variable block for master"
775      print >>output_file, ""
776   
777      for scenario_name in instances.keys():
778         instance = instances[scenario_name]
779         print >>output_file, "\\ Begin binary variable block for scenario",scenario_name
780         problem_writer._print_model_LP(instance, output_file)
781         print >>output_file, "\\ End integer binary block for scenario",scenario_name
782         print >>output_file, ""
783
784   # wrap up.
785   print >>output_file, "end"
786
787   # clean up.
788   output_file.close()
789
790   print ""
791   print "Output file written to file=",output_filename
792
793   print ""
794   print "Done..."
795
796   end_time = time.time()
797
798   print ""
799   print "Total execution time=%8.2f seconds" %(end_time - start_time)
800   print ""
801
802def create_and_write_ef(scenario_tree, scenario_instances, output_filename):
803
804   start_time = time.time()
805
806   binding_instance = create_ef_instance(scenario_tree, scenario_instances, False, True)
807
808   ################################################################################################
809   #### WRITE THE COMPOSITE MODEL #################################################################
810   ################################################################################################
811
812   print ""
813   print "Starting to write extensive form"
814
815   # create the output file.
816   problem_writer = cpxlp.ProblemWriter_cpxlp()
817   output_file = open(output_filename,"w")
818
819   problem_writer._output_prefixes = True # we always want prefixes
820
821   ################################################################################################
822   #### WRITE THE MASTER OBJECTIVE ################################################################
823   ################################################################################################
824
825   # write the objective for the master binding instance.
826   problem_writer._output_objectives = True
827   problem_writer._output_constraints = False
828   problem_writer._output_variables = False
829
830   print >>output_file, "\\ Begin objective block for master"
831   problem_writer._print_model_LP(binding_instance, output_file)
832   print >>output_file, "\\ End objective block for master"
833   print >>output_file, ""
834
835   ################################################################################################
836   #### WRITE THE CONSTRAINTS FOR THE MASTER MODEL AND ALL SCENARIO MODELS ########################
837   ################################################################################################
838
839   print >>output_file, "s.t."
840   print >>output_file, ""
841   
842   problem_writer._output_objectives = False
843   problem_writer._output_constraints = True
844   problem_writer._output_variables = False
845
846   print >>output_file, "\\ Begin constraint block for master"
847   problem_writer._print_model_LP(binding_instance, output_file)
848   print >>output_file, "\\ End constraint block for master",
849   print >>output_file, ""
850
851   for scenario_name in scenario_instances.keys():
852      instance = scenario_instances[scenario_name]
853      print >>output_file, "\\ Begin constraint block for scenario",scenario_name       
854      problem_writer._print_model_LP(instance, output_file)
855      print >>output_file, "\\ End constraint block for scenario",scenario_name
856      print >>output_file, ""
857
858   ################################################################################################
859   #### WRITE THE VARIABLES FOR THE MASTER MODEL AND ALL SCENARIO MODELS ##########################
860   ################################################################################################
861
862   # write the variables for the master binding instance, and then for each scenario.
863   print >>output_file, "bounds"
864   print >>output_file, ""
865   
866   problem_writer._output_objectives = False
867   problem_writer._output_constraints = False
868   problem_writer._output_variables = True
869
870   # first step: write variable bounds
871
872   problem_writer._output_continuous_variables = True
873   problem_writer._output_integer_variables = False
874   problem_writer._output_binary_variables = False
875
876   print >>output_file, "\\ Begin variable bounds block for master"
877   problem_writer._print_model_LP(binding_instance, output_file)
878   print >>output_file, "\\ End variable bounds block for master"
879   print >>output_file, ""
880   
881   for scenario_name in scenario_instances.keys():
882      instance = scenario_instances[scenario_name]
883      print >>output_file, "\\ Begin variable bounds block for scenario",scenario_name
884      problem_writer._print_model_LP(instance, output_file)
885      print >>output_file, "\\ End variable bounds block for scenario",scenario_name
886      print >>output_file, ""
887
888   # second step: write integer indicators.
889
890   problem_writer._output_continuous_variables = False
891   problem_writer._output_integer_variables = True
892
893   if integers_present(binding_instance, scenario_instances) is True:
894
895      print >>output_file, "integer"
896      print >>output_file, ""
897
898      print >>output_file, "\\ Begin integer variable block for master"
899      problem_writer._print_model_LP(binding_instance, output_file)
900      print >>output_file, "\\ End integer variable block for master"
901      print >>output_file, ""
902   
903      for scenario_name in scenario_instances.keys():
904         instance = scenario_instances[scenario_name]
905         print >>output_file, "\\ Begin integer variable block for scenario",scenario_name
906         problem_writer._print_model_LP(instance, output_file)
907         print >>output_file, "\\ End integer variable block for scenario",scenario_name
908         print >>output_file, ""
909
910   # third step: write binary indicators.
911
912   problem_writer._output_integer_variables = False
913   problem_writer._output_binary_variables = True
914
915   if binaries_present(binding_instance, scenario_instances) is True:
916
917      print >>output_file, "binary"
918      print >>output_file, ""
919
920      print >>output_file, "\\ Begin binary variable block for master"
921      problem_writer._print_model_LP(binding_instance, output_file)
922      print >>output_file, "\\ End binary variable block for master"
923      print >>output_file, ""
924   
925      for scenario_name in scenario_instances.keys():
926         instance = scenario_instances[scenario_name]
927         print >>output_file, "\\ Begin binary variable block for scenario",scenario_name
928         problem_writer._print_model_LP(instance, output_file)
929         print >>output_file, "\\ End integer binary block for scenario",scenario_name
930         print >>output_file, ""
931
932   # wrap up.
933   print >>output_file, "end"
934
935   # clean up.
936   output_file.close()
937
938   print ""
939   print "Output file written to file=",output_filename
940
941   print ""
942   print "Done..."
943
944   end_time = time.time()
945
946   print ""
947   print "Total execution time=%8.2f seconds" %(end_time - start_time)
948   print ""   
Note: See TracBrowser for help on using the repository browser.