Changeset 2398


Ignore:
Timestamp:
Feb 24, 2010 1:34:47 PM (10 years ago)
Author:
jwatson
Message:

Re-factoring the PH instance augmentation routines, moving them from ph.py to phutils.py so they can be used by the PH solver server.

Location:
coopr.pysp/trunk/coopr/pysp
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • coopr.pysp/trunk/coopr/pysp/ph.py

    r2373 r2398  
    424424      for (instance_name, instance) in self._instances.items():
    425425
    426          # first, gather all unique variables referenced in any stage
    427          # other than the last, independent of specific indices. this
    428          # "gather" step is currently required because we're being lazy
    429          # in terms of index management in the scenario tree - which
    430          # should really be done in terms of sets of indices.
    431          # NOTE: technically, the "instance variables" aren't really references
    432          # to the variable in the instance - instead, the reference model. this
    433          # isn't an issue now, but it could easily become one (esp. in avoiding deep copies).
    434          instance_variables = {}
    435          
    436          for stage in self._scenario_tree._stages[:-1]:
    437            
    438             for (reference_variable, index_template, reference_indices) in stage._variables:
    439                  
    440                if reference_variable.name not in instance_variables.keys():
    441                      
    442                   instance_variables[reference_variable.name] = reference_variable
    443 
    444          # for each blended variable, create a corresponding ph weight and average parameter in the instance.
    445          # this is a bit wasteful, in terms of indices that might appear in the last stage, but that is minor
    446          # in the grand scheme of things.
    447 
    448          for (variable_name, reference_variable) in instance_variables.items():
    449 
    450             # PH WEIGHT
    451 
    452             new_w_index = reference_variable._index
    453             new_w_parameter_name = "PHWEIGHT_"+reference_variable.name
    454             # this bit of ugliness is due to Pyomo not correctly handling the Param construction
    455             # case when the supplied index set consists strictly of None, i.e., the source variable
    456             # is a singleton. this case be cleaned up when the source issue in Pyomo is fixed.
    457             new_w_parameter = None
    458             if (len(new_w_index) is 1) and (None in new_w_index):
    459                new_w_parameter = Param(name=new_w_parameter_name)
    460             else:
    461                new_w_parameter = Param(new_w_index,name=new_w_parameter_name)
    462             setattr(instance,new_w_parameter_name,new_w_parameter)
    463 
    464             # if you don't explicitly assign values to each index, the entry isn't created - instead, when you reference
    465             # the parameter that hasn't been explicitly assigned, you just get the default value as a constant. I'm not
    466             # sure if this has to do with the model output, or the function of the model, but I'm doing this to avoid the
    467             # issue in any case for now. NOTE: I'm not sure this is still the case in Pyomo.
    468             for index in new_w_index:
    469                new_w_parameter[index] = 0.0
    470 
    471             # PH AVG
    472 
    473             new_avg_index = reference_variable._index
    474             new_avg_parameter_name = "PHAVG_"+reference_variable.name
    475             new_avg_parameter = None
    476             if (len(new_avg_index) is 1) and (None in new_avg_index):
    477                new_avg_parameter = Param(name=new_avg_parameter_name)
    478             else:
    479                new_avg_parameter = Param(new_avg_index,name=new_avg_parameter_name)
    480             setattr(instance,new_avg_parameter_name,new_avg_parameter)
    481 
    482             for index in new_avg_index:
    483                new_avg_parameter[index] = 0.0
    484 
    485             # PH RHO
    486 
    487             new_rho_index = reference_variable._index
    488             new_rho_parameter_name = "PHRHO_"+reference_variable.name
    489             new_rho_parameter = None
    490             if (len(new_avg_index) is 1) and (None in new_avg_index):
    491                new_rho_parameter = Param(name=new_rho_parameter_name)
    492             else:
    493                new_rho_parameter = Param(new_rho_index,name=new_rho_parameter_name)
    494             setattr(instance,new_rho_parameter_name,new_rho_parameter)
    495 
    496             for index in new_rho_index:
    497                new_rho_parameter[index] = self._rho
    498 
    499             # PH LINEARIZED PENALTY TERM
    500 
    501             if self._linearize_nonbinary_penalty_terms > 0:
    502 
    503                new_penalty_term_variable_index = reference_variable._index
    504                new_penalty_term_variable_name = "PHQUADPENALTY_"+reference_variable.name
    505                # this is a quadratic penalty term - the lower bound is 0!
    506                new_penalty_term_variable = None
    507                if (len(new_avg_index) is 1) and (None in new_avg_index):
    508                   new_penalty_term_variable = Var(name=new_penalty_term_variable_name, bounds=(0.0,None))                 
    509                else:
    510                   new_penalty_term_variable = Var(new_penalty_term_variable_index, name=new_penalty_term_variable_name, bounds=(0.0,None))
    511                new_penalty_term_variable.construct()
    512                setattr(instance, new_penalty_term_variable_name, new_penalty_term_variable)
    513                self._instance_augmented_attributes[instance_name].append(new_penalty_term_variable_name)
    514 
    515             # BINARY INDICATOR PARAMETER FOR WHETHER SPECIFIC VARIABLES ARE BLENDED. FOR ADVANCED USERS ONLY.
    516 
    517             # also controls whether weight updates proceed at any iteration.
    518 
    519             new_blend_index = reference_variable._index
    520             new_blend_parameter_name = "PHBLEND_"+reference_variable.name
    521             new_blend_parameter = None
    522             if (len(new_avg_index) is 1) and (None in new_avg_index):
    523                new_blend_parameter = Param(name=new_blend_parameter_name, within=Binary)
    524             else:
    525                new_blend_parameter = Param(new_blend_index, name=new_blend_parameter_name, within=Binary)
    526             setattr(instance,new_blend_parameter_name,new_blend_parameter)
    527 
    528             for index in new_blend_index:
    529                new_blend_parameter[index] = 1
     426         new_penalty_variable_names = create_ph_parameters(instance, self._scenario_tree, self._rho, self._linearize_nonbinary_penalty_terms)
     427         self._instance_augmented_attributes[instance_name].extend(new_penalty_variable_names)
    530428
    531429   # a simple utility to extract the first-stage cost statistics, e.g., min, average, and max.
     
    554452               minimum_value = this_value
    555453      return minimum_value, sum_values/num_values, maximum_value
    556      
     454
     455   # a utility to transmit - across the PH solver manager - the current weights
     456   # for each of my problem instances. used to set up iteration K solves.
     457   def _transmit_weights(self):
     458
     459      for scenario_name, scenario_instance in self._instances.items():
     460
     461         weights_to_transmit = []
     462
     463         for stage in self._scenario_tree._stages[:-1]: # no blending over the final stage, so no weights to worry about.
     464
     465            for (variable, index_template, variable_indices) in stage._variables:
     466
     467               variable_name = variable.name
     468               weight_parameter_name = "PHWEIGHT_"+variable_name
     469               weights_to_transmit.append(getattr(scenario_instance, weight_parameter_name))
     470
     471         self._solver_manager.transmit_weights(scenario_instance, weights_to_transmit)
     472
     473   # a utility to transmit - across the PH solver manager - the current rho values
     474   # for each of my problem instances. mainly after PH iteration 0 is complete,
     475   # in preparation for the iteration K solves.
     476   def _transmit_rhos(self):
     477
     478      for scenario_name, scenario_instance in self._instances.items():
     479
     480         rhos_to_transmit = []
     481
     482         for stage in self._scenario_tree._stages[:-1]: # no blending over the final stage, so no rhos to worry about.
     483
     484            for (variable, index_template, variable_indices) in stage._variables:
     485
     486               variable_name = variable.name
     487               rho_parameter_name = "PHRHO_"+variable_name
     488               rhos_to_transmit.append(getattr(scenario_instance, rho_parameter_name))
     489
     490         self._solver_manager.transmit_rhos(scenario_instance, rhos_to_transmit)         
     491         
    557492
    558493   """ Constructor
     
    13511286      solve_start_time = time.time()
    13521287
     1288      # STEP -1: if using a PH solver manager, propagate current weights to the appropriate solver servers.
     1289      # NOTE: We aren't currently propagating rhos, as they generally don't change - we need to
     1290      #       have a flag, though, indicating whether the rhos have changed, so they can be
     1291      #       transmitted if needed.
     1292      if self._solver_manager_type == "ph":
     1293         self._transmit_weights()
     1294
    13531295      # STEP 0: set up all global solver options.
    13541296      self._solver.mipgap = self._mipgap     
     
    14891431      for plugin in self._ph_plugins:     
    14901432         plugin.post_iteration_0(self)
     1433
     1434      # if using a PH solver server, trasnsmit the rhos prior to the iteration
     1435      # k solve sequence. for now, we are assuming that the rhos don't change
     1436      # on a per-iteration basis, but that assumption can be easily relaxed.
     1437      # it is important to do this after the plugins have a chance to do their
     1438      # computation.
     1439      if self._solver_manager_type == "ph":
     1440         self._transmit_rhos()
    14911441
    14921442      # checkpoint if it's time - which it always is after iteration 0,
  • coopr.pysp/trunk/coopr/pysp/phserver.py

    r2396 r2398  
    66#  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
    77#  the U.S. Government retains certain rights in this software.
    8 #  For more information, see the Coopr README.txt file.
     8#  Fr more information, see the Coopr README.txt file.
    99#  _________________________________________________________________________
    1010
     
    2323from coopr.opt.base import SolverFactory
    2424from coopr.pysp.scenariotree import *
     25from phutils import *
    2526
    2627# Pyro
     
    6465     
    6566      return encoded_results
     67
     68   def update_weights(self, scenario_name, new_weights):
     69
     70      print "RECEIVED REQUEST TO UPDATE WEIGHTS FOR SCENARIO=",scenario_name
     71      print "WEIGHT LIST=",new_weights
     72      for update_weight in new_weights:
     73         update_weight.pprint()
     74      # TBD - FIND THE RIGHT SCENARIO INSTANCE, APPLY THE UPDATES, ETC!
     75
     76   def update_rhos(self, scenario_name, new_rhos):
     77
     78      print "RECEIVED REQUEST TO UPDATE RHOS FOR SCENARIO=",scenario_name
     79      print "RHO LIST=",new_rhos
     80      for update_rho in new_rho:
     81         update_rho.pprint()
     82      # TBD - FIND THE RIGHT SCENARIO INSTANCE, APPLY THE UPDATES, ETC!     
    6683
    6784#
     
    185202         print ""
    186203         print "***ERROR: Exiting test driver: No 'model' object created in module "+reference_model_filename
    187          return None, None, None
     204         return None, None, None, None
    188205
    189206      if model_import.model is None:
    190207         print ""
    191208         print "***ERROR: Exiting test driver: 'model' object equals 'None' in module "+reference_model_filename
    192          return None, None, None
     209         return None, None, None, None
    193210 
    194211      reference_model = model_import.model
    195212   except IOError:
    196213      print "***ERROR: Failed to load scenario reference model from file="+reference_model_filename
    197       return None, None, None
     214      return None, None, None, None
    198215
    199216   try:
     
    204221   except IOError:
    205222      print "***ERROR: Failed to load scenario reference instance data from file="+reference_instance_filename
    206       return None, None, None
     223      return None, None, None, None
    207224
    208225   #
     
    238255   start_time = time.time()
    239256
    240    print "RUNNING SERVER NOW!"
    241257   Pyro.core.initServer(banner=1)   
    242258
    243    print "NUMBER OF SCENARIO INSTANCES="+str(len(scenario_instances))
    244 
    245    print "ATTEMPTING TO LOCATE NAME SERVER"
    246259   locator = Pyro.naming.NameServerLocator()
    247    ns = locator.getNS()
    248    print "FOUND NAME SERVER"
     260   ns = None
     261   try:
     262      ns = locator.getNS()
     263   except Pyro.errors.NamingError:
     264      raise RuntimeError, "PH solver server failed to locate Pyro name server"     
    249265
    250266   solver_daemon = Pyro.core.Daemon()
     
    276292   # can't hurt too much until proven otherwise, that is) to construct
    277293   # the scenarios that this server is responsible for.
    278    print "LOADING REFERENCE AND SCENARIO MODELS/INSTANCES"
    279294   reference_model, reference_instance, scenario_tree, scenario_tree_instance = load_reference_and_scenario_models(options)
    280295   if reference_model is None or reference_instance is None or scenario_tree is None:
     
    294309         return
    295310
     311      # create the baseline scenario instance
    296312      scenario_instance = construct_scenario_instance(scenario_tree,
    297313                                                      options.instance_directory,
     
    301317      if scenario_instance is None:
    302318         print "***Unable to launch PH solver server - failed to create instance for scenario="+scenario_name
     319         return
     320
     321      # augment the instance with PH-specific parameters (weights, rhos, etc).
     322      # TBD: The default rho of 1.0 is kind of bogus. Need to somehow propagate
     323      #      this value and the linearization parameter as a command-line argument.
     324      new_penalty_variable_names = create_ph_parameters(scenario_instance, scenario_tree, 1.0, False)
     325
    303326      scenario_instances[scenario_name] = scenario_instance
    304327
  • coopr.pysp/trunk/coopr/pysp/phsolvermanager.py

    r2396 r2398  
    115115        return ActionHandle(error=True, explanation="No queued evaluations available in the PH solver manager, which only executes solvers synchronously")
    116116
     117    def transmit_weights(self, scenario_instance, new_weights):
     118
     119       if isinstance(scenario_instance, Model) is False:
     120          raise RuntimeError, "Argument supplied to _perform_queue method of class PHSolverManager must be a Model instance - type supplied="+str(type(scenario_instance))
     121       scenario_name = scenario_instance.name
     122
     123       # identify the appropriate PH solver server, grabbing a handle if necessary.
     124       proxy = None
     125       if scenario_name in self._solver_proxy.keys():
     126          proxy = self._solver_proxy[scenario_name]
     127       else:
     128          uri = None
     129          try:
     130             uri = self._ns.resolve(scenario_name)
     131          except Pyro.errors.NamingError:
     132             raise RuntimeError, "***ERROR: Failed to locate PH solver server capable of processing scenario="+scenario_name
     133             sys.exit(0)
     134             
     135          proxy = Pyro.core.getProxyForURI(uri)
     136          self._solver_proxy[scenario_name] = proxy
     137
     138       # execute the RMI.
     139       proxy.update_weights(scenario_name, new_weights)
     140
    117141SolverManagerRegistration("ph", PHSolverManager)
  • coopr.pysp/trunk/coopr/pysp/phutils.py

    r2371 r2398  
    224224
    225225   return scenario_instance
     226
     227
     228# creates all PH parameters for a problem instance, given a scenario tree
     229# (to identify the relevant variables), a default rho (simply for initialization),
     230# and a boolean indicating if quadratic penalty terms are to be linearized.
     231# returns a list of any created variables, specifically when linearizing -
     232# this is useful to clean-up reporting.
     233
     234def create_ph_parameters(instance, scenario_tree, default_rho, linearizing_penalty_terms):
     235
     236   new_penalty_variable_names = []   
     237   
     238   # first, gather all unique variables referenced in any stage
     239   # other than the last, independent of specific indices. this
     240   # "gather" step is currently required because we're being lazy
     241   # in terms of index management in the scenario tree - which
     242   # should really be done in terms of sets of indices.
     243   # NOTE: technically, the "instance variables" aren't really references
     244   # to the variable in the instance - instead, the reference model. this
     245   # isn't an issue now, but it could easily become one (esp. in avoiding deep copies).
     246   instance_variables = {}
     247   
     248   for stage in scenario_tree._stages[:-1]:
     249     
     250      for (reference_variable, index_template, reference_indices) in stage._variables:
     251           
     252         if reference_variable.name not in instance_variables.keys():
     253               
     254            instance_variables[reference_variable.name] = reference_variable
     255
     256   # for each blended variable, create a corresponding ph weight and average parameter in the instance.
     257   # this is a bit wasteful, in terms of indices that might appear in the last stage, but that is minor
     258   # in the grand scheme of things.
     259
     260   for (variable_name, reference_variable) in instance_variables.items():
     261
     262      # PH WEIGHT
     263
     264      new_w_index = reference_variable._index
     265      new_w_parameter_name = "PHWEIGHT_"+reference_variable.name
     266      # this bit of ugliness is due to Pyomo not correctly handling the Param construction
     267      # case when the supplied index set consists strictly of None, i.e., the source variable
     268      # is a singleton. this case be cleaned up when the source issue in Pyomo is fixed.
     269      new_w_parameter = None
     270      if (len(new_w_index) is 1) and (None in new_w_index):
     271         new_w_parameter = Param(name=new_w_parameter_name)
     272      else:
     273         new_w_parameter = Param(new_w_index,name=new_w_parameter_name)
     274      setattr(instance,new_w_parameter_name,new_w_parameter)
     275
     276      # if you don't explicitly assign values to each index, the entry isn't created - instead, when you reference
     277      # the parameter that hasn't been explicitly assigned, you just get the default value as a constant. I'm not
     278      # sure if this has to do with the model output, or the function of the model, but I'm doing this to avoid the
     279      # issue in any case for now. NOTE: I'm not sure this is still the case in Pyomo.
     280      for index in new_w_index:
     281         new_w_parameter[index] = 0.0
     282
     283      # PH AVG
     284
     285      new_avg_index = reference_variable._index
     286      new_avg_parameter_name = "PHAVG_"+reference_variable.name
     287      new_avg_parameter = None
     288      if (len(new_avg_index) is 1) and (None in new_avg_index):
     289         new_avg_parameter = Param(name=new_avg_parameter_name)
     290      else:
     291         new_avg_parameter = Param(new_avg_index,name=new_avg_parameter_name)
     292      setattr(instance,new_avg_parameter_name,new_avg_parameter)
     293
     294      for index in new_avg_index:
     295         new_avg_parameter[index] = 0.0
     296
     297      # PH RHO
     298
     299      new_rho_index = reference_variable._index
     300      new_rho_parameter_name = "PHRHO_"+reference_variable.name
     301      new_rho_parameter = None
     302      if (len(new_avg_index) is 1) and (None in new_avg_index):
     303         new_rho_parameter = Param(name=new_rho_parameter_name)
     304      else:
     305         new_rho_parameter = Param(new_rho_index,name=new_rho_parameter_name)
     306      setattr(instance,new_rho_parameter_name,new_rho_parameter)
     307
     308      for index in new_rho_index:
     309         new_rho_parameter[index] = default_rho
     310
     311      # PH LINEARIZED PENALTY TERM
     312
     313      if linearizing_penalty_terms > 0:
     314
     315         new_penalty_term_variable_index = reference_variable._index
     316         new_penalty_term_variable_name = "PHQUADPENALTY_"+reference_variable.name
     317         # this is a quadratic penalty term - the lower bound is 0!
     318         new_penalty_term_variable = None
     319         if (len(new_avg_index) is 1) and (None in new_avg_index):
     320            new_penalty_term_variable = Var(name=new_penalty_term_variable_name, bounds=(0.0,None))                 
     321         else:
     322            new_penalty_term_variable = Var(new_penalty_term_variable_index, name=new_penalty_term_variable_name, bounds=(0.0,None))
     323         new_penalty_term_variable.construct()
     324         setattr(instance, new_penalty_term_variable_name, new_penalty_term_variable)
     325         new_penalty_variable_names.append(new_penalty_term_variable_name)
     326
     327      # BINARY INDICATOR PARAMETER FOR WHETHER SPECIFIC VARIABLES ARE BLENDED. FOR ADVANCED USERS ONLY.
     328
     329      # also controls whether weight updates proceed at any iteration.
     330
     331      new_blend_index = reference_variable._index
     332      new_blend_parameter_name = "PHBLEND_"+reference_variable.name
     333      new_blend_parameter = None
     334      if (len(new_avg_index) is 1) and (None in new_avg_index):
     335         new_blend_parameter = Param(name=new_blend_parameter_name, within=Binary)
     336      else:
     337         new_blend_parameter = Param(new_blend_index, name=new_blend_parameter_name, within=Binary)
     338      setattr(instance,new_blend_parameter_name,new_blend_parameter)
     339
     340      for index in new_blend_index:
     341         new_blend_parameter[index] = 1
     342
     343   return new_penalty_variable_names     
Note: See TracChangeset for help on using the changeset viewer.