Changeset 2405


Ignore:
Timestamp:
Feb 25, 2010 9:54:36 PM (10 years ago)
Author:
jwatson
Message:

Restructuring of PySP to facilitate full implementation of PH solver servers (penalty objectives in particular).

Location:
coopr.pysp/trunk/coopr/pysp
Files:
1 added
6 edited

Legend:

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

    r2396 r2405  
    2222from phinit import *
    2323from phsolvermanager import *
     24from phobjective import *
    2425
    2526pyutilib.component.core.PluginGlobals.pop_env()
    26 
    2727
    2828try:
  • coopr.pysp/trunk/coopr/pysp/ph.py

    r2404 r2405  
    2727from scenariotree import *
    2828from phutils import *
     29from phobjective import *
    2930
    3031from pyutilib.component.core import ExtensionPoint
     
    3334
    3435class ProgressiveHedging(object):
    35 
    36    #
    37    # routine to compute linearization breakpoints uniformly between the bounds and the mean.
    38    #
    39 
    40    # IMPT: In general, the breakpoint computation codes can return a 2-list even if the lb equals
    41    #       the ub. This case happens quite often in real models (although typically lb=xvag=ub).
    42    #       See the code for constructing the pieces on how this case is handled in the linearization.
    43    
    44    def compute_uniform_breakpoints(self, lb, node_min, xavg, node_max, ub, num_breakpoints_per_side):
    45      
    46       breakpoints = []
    47 
    48       # add the lower bound - the first breakpoint.
    49       breakpoints.append(lb)
    50 
    51       # determine the breakpoints to the left of the mean.
    52       left_step = (xavg - lb) / num_breakpoints_per_side
    53       current_x = lb
    54       for i in range(1,num_breakpoints_per_side+1):
    55          this_lb = current_x
    56          this_ub = current_x+left_step
    57          if (fabs(this_lb-lb) > self._integer_tolerance) and (fabs(this_lb-xavg) > self._integer_tolerance):
    58             breakpoints.append(this_lb)
    59          current_x += left_step           
    60 
    61       # add the mean - it's always a breakpoint. unless!
    62       # the lb or ub and the avg are the same.
    63       if (fabs(lb-xavg) > self._integer_tolerance) and (fabs(ub-xavg) > self._integer_tolerance):
    64          breakpoints.append(xavg)
    65 
    66       # determine the breakpoints to the right of the mean.
    67       right_step = (ub - xavg) / num_breakpoints_per_side
    68       current_x = xavg
    69       for i in range(1,num_breakpoints_per_side+1):
    70          this_lb = current_x
    71          this_ub = current_x+right_step
    72          if (fabs(this_ub-xavg) > self._integer_tolerance) and (fabs(this_ub-ub) > self._integer_tolerance):         
    73             breakpoints.append(this_ub)
    74          current_x += right_step
    75 
    76       # add the upper bound - the last breakpoint.
    77       # the upper bound should always be different than the lower bound (I say with some
    78       # hesitation - it really depends on what plugins are doing to modify the bounds dynamically).
    79       breakpoints.append(ub)
    80 
    81       return breakpoints
    82 
    83    #
    84    # routine to compute linearization breakpoints uniformly between the current node min/max bounds.
    85    #   
    86 
    87    def compute_uniform_between_nodestat_breakpoints(self, lb, node_min, xavg, node_max, ub, num_breakpoints):
    88 
    89       breakpoints = []
    90 
    91       # add the lower bound - the first breakpoint.
    92       breakpoints.append(lb)
    93 
    94       # add the node-min - the second breakpoint. but only if it is different than the lower bound and the mean.
    95       if (fabs(node_min-lb) > self._integer_tolerance) and (fabs(node_min-xavg) > self._integer_tolerance):     
    96          breakpoints.append(node_min)
    97 
    98       step = (node_max - node_min) / num_breakpoints
    99       current_x = node_min
    100       for i in range(1,num_breakpoints+1):
    101          this_lb = current_x
    102          this_ub = current_x+step
    103          if (fabs(this_lb-node_min) > self._integer_tolerance) and (fabs(this_lb-node_max) > self._integer_tolerance) and (fabs(this_lb-xavg) > self._integer_tolerance):
    104             breakpoints.append(this_lb)
    105          current_x += step           
    106 
    107       # add the node-max - the second-to-last breakpoint. but only if it is different than the upper bound and the mean.
    108       if (fabs(node_max-ub) > self._integer_tolerance) and (fabs(node_max-xavg) > self._integer_tolerance):           
    109          breakpoints.append(node_max)     
    110 
    111       # add the upper bound - the last breakpoint.
    112       breakpoints.append(ub)
    113 
    114       # add the mean - it's always a breakpoint. unless! -
    115       # it happens to be equal to (within tolerance) the lower or upper bounds.
    116       # sort to insert it in the correct place.
    117       if (fabs(xavg - lb) > self._integer_tolerance) and (fabs(xavg - ub) > self._integer_tolerance):
    118          breakpoints.append(xavg)
    119       breakpoints.sort()
    120 
    121       return breakpoints
    122 
    123    #
    124    # routine to compute linearization breakpoints using "Woodruff" relaxation of the compute_uniform_between_nodestat_breakpoints.
    125    #   
    126 
    127    def compute_uniform_between_woodruff_breakpoints(self, lb, node_min, xavg, node_max, ub, num_breakpoints):
    128 
    129       breakpoints = []
    130 
    131       # add the lower bound - the first breakpoint.
    132       breakpoints.append(lb)
    133 
    134       # be either three "differences" from the mean, or else "halfway to the bound", whichever is closer to the mean.
    135       left = max(xavg - 3.0 * (xavg - node_min), xavg - 0.5 * (xavg - lb))
    136       right = min(xavg + 3.0 * (node_max - xavg), xavg + 0.5 * (ub - xavg))     
    137 
    138       # add the left bound - the second breakpoint. but only if it is different than the lower bound and the mean.
    139       if (fabs(left-lb) > self._integer_tolerance) and (fabs(left-xavg) > self._integer_tolerance):     
    140          breakpoints.append(left)
    141 
    142       step = (right - left) / num_breakpoints
    143       current_x = left
    144       for i in range(1,num_breakpoints+1):
    145          this_lb = current_x
    146          this_ub = current_x+step
    147          if (fabs(this_lb-left) > self._integer_tolerance) and (fabs(this_lb-right) > self._integer_tolerance) and (fabs(this_lb-xavg) > self._integer_tolerance):
    148             breakpoints.append(this_lb)
    149          current_x += step           
    150 
    151       # add the right bound - the second-to-last breakpoint. but only if it is different than the upper bound and the mean.
    152       if (fabs(right-ub) > self._integer_tolerance) and (fabs(right-xavg) > self._integer_tolerance):           
    153          breakpoints.append(right)     
    154 
    155       # add the upper bound - the last breakpoint.
    156       breakpoints.append(ub)
    157 
    158       # add the mean - it's always a breakpoint.
    159       # sort to insert it in the correct place.
    160       breakpoints.append(xavg)
    161       breakpoints.sort()
    162 
    163       return breakpoints
    164 
    165    #
    166    # routine to compute linearization breakpoints based on an exponential distribution from the mean in each direction.
    167    #   
    168 
    169    def compute_exponential_from_mean_breakpoints(self, lb, node_min, xavg, node_max, ub, num_breakpoints_per_side):
    170 
    171       breakpoints = []
    172 
    173       # add the lower bound - the first breakpoint.
    174       breakpoints.append(lb)
    175 
    176       # determine the breakpoints to the left of the mean.
    177       left_delta = xavg - lb
    178       base = exp(log(left_delta) / num_breakpoints_per_side)
    179       current_offset = base
    180       for i in range(1,num_breakpoints_per_side+1):
    181          current_x = xavg - current_offset
    182          if (fabs(current_x-lb) > self._integer_tolerance) and (fabs(current_x-xavg) > self._integer_tolerance):
    183             breakpoints.append(current_x)
    184          current_offset *= base
    185 
    186       # add the mean - it's always a breakpoint.
    187       breakpoints.append(xavg)
    188 
    189       # determine the breakpoints to the right of the mean.
    190       right_delta = ub - xavg
    191       base = exp(log(right_delta) / num_breakpoints_per_side)
    192       current_offset = base
    193       for i in range(1,num_breakpoints_per_side+1):
    194          current_x = xavg + current_offset
    195          if (fabs(current_x-xavg) > self._integer_tolerance) and (fabs(current_x-ub) > self._integer_tolerance):         
    196             breakpoints.append(current_x)
    197          current_offset *= base
    198 
    199       # add the upper bound - the last breakpoint.
    200       breakpoints.append(ub)         
    201 
    202       return breakpoints     
    20336
    20437   #
     
    378211
    379212      return (num_fixed_discrete_vars, num_fixed_continuous_vars)
    380 
    381    # a utility to create piece-wise linear constraint expressions for a given variable, for
    382    # use in constructing the augmented (penalized) PH objective. lb and ub are the bounds
    383    # on this piece, variable is the actual instance variable, and average is the instance
    384    # parameter specifying the average of this variable across instances sharing passing
    385    # through a common tree node. lb and ub are floats.
    386    # IMPT: There are cases where lb=ub, in which case the slope is 0 and the intercept
    387    #       is simply the penalty at the lower(or upper) bound.
    388    def _create_piecewise_constraint_expression(self, lb, ub, instance_variable, variable_average, quad_variable):
    389 
    390       penalty_at_lb = (lb - variable_average()) * (lb - variable_average())
    391       penalty_at_ub = (ub - variable_average()) * (ub - variable_average())
    392       slope = None
    393       if fabs(ub-lb) > self._integer_tolerance:
    394          slope = (penalty_at_ub - penalty_at_lb) / (ub - lb)
    395       else:
    396          slope = 0.0
    397       intercept = penalty_at_lb - slope * lb
    398       expression = (0.0, quad_variable - slope * instance_variable - intercept, None)
    399 
    400       return expression
    401213
    402214   # when the quadratic penalty terms are approximated via piecewise linear segments,
     
    474286         self._solver_manager.transmit_weights_and_averages(scenario_instance, weights_to_transmit, averages_to_transmit)
    475287
     288   #
    476289   # a utility to transmit - across the PH solver manager - the current rho values
    477290   # for each of my problem instances. mainly after PH iteration 0 is complete,
    478291   # in preparation for the iteration K solves.
     292   #
     293
    479294   def _transmit_rhos(self):
    480295
     
    534349      self._solver_manager_type = "serial" # serial or pyro are the options currently available
    535350     
    536       self._solver = None # will eventually be unnecessary once Bill eliminates the need for a solver in the solver manager constructor.
     351      self._solver = None
    537352      self._solver_manager = None
    538353     
     
    1125940   def form_iteration_k_objectives(self):
    1126941
    1127       # for each blended variable (i.e., those not appearing in the final stage),
    1128       # add the linear and quadratic penalty terms to the objective.
    1129942      for instance_name, instance in self._instances.items():
    1130          
    1131          objective_name = instance.active_components(Objective).keys()[0]         
    1132          objective = instance.active_components(Objective)[objective_name]
    1133          # clone the objective, because we're going to augment (repeatedly) the original objective.
    1134          objective_expression = self._original_objective_expression[instance_name].clone()
    1135          # the quadratic expression is really treated as just a list - eventually should be treated as a full expression.
    1136          quad_expression = 0.0
    1137          
    1138          for stage in self._scenario_tree._stages[:-1]: # skip the last stage, as no blending occurs
    1139 
    1140             variable_tree_node = None
    1141             for node in stage._tree_nodes:
    1142                for scenario in node._scenarios:
    1143                   if scenario._name == instance_name:
    1144                      variable_tree_node = node
    1145                      break
    1146 
    1147             for (reference_variable, index_template, variable_indices) in stage._variables:
    1148 
    1149                variable_name = reference_variable.name
    1150                variable_type = reference_variable.domain
    1151 
    1152                w_parameter_name = "PHWEIGHT_"+variable_name
    1153                w_parameter = instance.active_components(Param)[w_parameter_name]
    1154                  
    1155                average_parameter_name = "PHAVG_"+variable_name
    1156                average_parameter = instance.active_components(Param)[average_parameter_name]
    1157 
    1158                rho_parameter_name = "PHRHO_"+variable_name
    1159                rho_parameter = instance.active_components(Param)[rho_parameter_name]
    1160 
    1161                blend_parameter_name = "PHBLEND_"+variable_name
    1162                blend_parameter = instance.active_components(Param)[blend_parameter_name]
    1163 
    1164                node_min_parameter = variable_tree_node._minimums[variable_name]
    1165                node_max_parameter = variable_tree_node._maximums[variable_name]               
    1166 
    1167                quad_penalty_term_variable = None
    1168                if self._linearize_nonbinary_penalty_terms > 0:
    1169                   quad_penalty_term_variable_name = "PHQUADPENALTY_"+variable_name
    1170                   quad_penalty_term_variable = instance.active_components(Var)[quad_penalty_term_variable_name]
    1171 
    1172                instance_variable = instance.active_components(Var)[variable_name]
    1173 
    1174                for index in variable_indices:
    1175 
    1176                   if (instance_variable[index].status is not VarStatus.unused) and (instance_variable[index].fixed is False):
    1177 
    1178                      # add the linear (w-weighted) term is a consistent fashion, independent of variable type.
    1179                      # if maximizing, here is where you would want "-=" - however, if you do this, the collect/simplify process chokes for reasons currently unknown.
    1180                      objective_expression += (w_parameter[index] * instance_variable[index])
    1181 
    1182                      # there are some cases in which a user may want to avoid the proximal term completely.
    1183                      # it's of course only a good idea when there are at least bounds (both lb and ub) on
    1184                      # the variables to be blended.
    1185                      if self._drop_proximal_terms is False:
    1186 
    1187                         # deal with binaries
    1188                         if isinstance(variable_type, BooleanSet) is True:
    1189 
    1190                            if self._retain_quadratic_binary_terms is False:
    1191                               # this rather ugly form of the linearized quadratic expression term is required
    1192                               # due to a pyomo bug - the form (rho/2) * (x+y+z) chokes in presolve when distributing
    1193                               # over the sum.
    1194                               new_term = (blend_parameter[index] * rho_parameter[index] / 2.0 * instance_variable[index]) - \
    1195                                          (blend_parameter[index] * rho_parameter[index] * average_parameter[index] * instance_variable[index]) + \
    1196                                          (blend_parameter[index] * rho_parameter[index] / 2.0 * average_parameter[index] * average_parameter[index])                             
    1197                               if objective.sense is minimize:
    1198                                  objective_expression += new_term
    1199                               else:
    1200                                  objective_expression -= new_term                                 
    1201                            else:
    1202                               quad_expression += (blend_parameter[index] * rho_parameter[index] * (instance_variable[index] - average_parameter[index]) ** 2)
    1203 
    1204                         # deal with everything else
    1205                         else:
    1206 
    1207                            if self._linearize_nonbinary_penalty_terms > 0:
    1208 
    1209                               # the variables are easy - just a single entry.
    1210                               if objective.sense is minimize:
    1211                                  objective_expression += (rho_parameter[index] / 2.0 * quad_penalty_term_variable[index])
    1212                               else:
    1213                                  objective_expression -= (rho_parameter[index] / 2.0 * quad_penalty_term_variable[index])
    1214                                  
    1215                               # the constraints are somewhat nastier.
    1216 
    1217                               # TBD - DEFINE CONSTRAINTS ON-THE-FLY?? (INDIVIDUALLY NAMED FOR NOW - CREATE INDEX SETS!) - OR A LEAST AN INDEX SET PER "PIECE"
    1218                               xavg = average_parameter[index]
    1219                               x = instance_variable[index]
    1220 
    1221                               lb = None
    1222                               ub = None
    1223 
    1224                               if x.lb is None:
    1225                                  raise ValueError, "No lower bound specified for variable="+variable_name+indexToString(index)+"; required when piece-wise approximating quadratic penalty terms"
    1226                               else:
    1227                                  lb = x.lb()
    1228 
    1229                               if x.ub is None:
    1230                                  raise ValueError, "No upper bound specified for variable="+variable_name+indexToString(index)+"; required when piece-wise approximating quadratic penalty terms"
    1231                               else:
    1232                                  ub = x.ub()
    1233 
    1234                               node_min = node_min_parameter[index]()
    1235                               node_max = node_max_parameter[index]()
    1236 
    1237                               # compute the breakpoint sequence according to the specified strategy.
    1238                               breakpoints = []
    1239                               if self._breakpoint_strategy == 0:
    1240                                  breakpoints = self.compute_uniform_breakpoints(lb, node_min, xavg(), node_max, ub, self._linearize_nonbinary_penalty_terms)
    1241                               elif self._breakpoint_strategy == 1:
    1242                                  breakpoints = self.compute_uniform_between_nodestat_breakpoints(lb, node_min, xavg(), node_max, ub, self._linearize_nonbinary_penalty_terms)
    1243                               elif self._breakpoint_strategy == 2:
    1244                                  breakpoints = self.compute_uniform_between_woodruff_breakpoints(lb, node_min, xavg(), node_max, ub, self._linearize_nonbinary_penalty_terms)
    1245                               elif self._breakpoint_strategy == 3:
    1246                                  breakpoints = self.compute_exponential_from_mean_breakpoints(lb, node_min, xavg(), node_max, ub, self._linearize_nonbinary_penalty_terms)                                                                                                   
    1247                               else:
    1248                                  raise ValueError, "A breakpoint distribution strategy="+str(self._breakpoint_strategy)+" is currently not supported within PH!"
    1249 
    1250                               for i in range(0,len(breakpoints)-1):
    1251 
    1252                                  this_lb = breakpoints[i]
    1253                                  this_ub = breakpoints[i+1]
    1254 
    1255                                  piece_constraint_name = "QUAD_PENALTY_PIECE_"+str(i)+"_"+variable_name+str(index)
    1256                                  if hasattr(instance, piece_constraint_name) is False:
    1257                                     # this is the first time the constraint is being added - add it to the list of PH-specific constraints for this instance.
    1258                                     self._instance_augmented_attributes[instance_name].append(piece_constraint_name)                                                                           
    1259                                  piece_constraint = Constraint(name=piece_constraint_name)
    1260                                  piece_constraint.model = instance
    1261                                  piece_expression = self._create_piecewise_constraint_expression(this_lb, this_ub, x, xavg, quad_penalty_term_variable[index])
    1262                                  piece_constraint.add(None, piece_expression)
    1263                                  setattr(instance, piece_constraint_name, piece_constraint)                                   
    1264 
    1265                            else:
    1266 
    1267                               quad_expression += (blend_parameter[index] * rho_parameter[index] * (instance_variable[index] - average_parameter[index]) ** 2)                           
    1268                    
    1269          # strictly speaking, this probably isn't necessary - parameter coefficients won't get
    1270          # pre-processed out of the expression tree. however, if the under-the-hood should change,
    1271          # we'll be covered.
    1272          objective_expression.simplify(instance)
    1273          instance.active_components(Objective)[objective_name]._data[None].expr = objective_expression
    1274          # if we are linearizing everything, then nothing will appear in the quadratic expression -
    1275          # don't add the empty "0.0" expression to the objective. otherwise, the output file won't
    1276          # be properly generated.
    1277          if quad_expression != 0.0:
    1278            instance.active_components(Objective)[objective_name]._quad_subexpr = quad_expression
     943
     944         new_attrs = form_ph_objective(instance_name, \
     945                                       instance, \
     946                                       self._original_objective_expression[instance_name], \
     947                                       self._scenario_tree, \
     948                                       self._linearize_nonbinary_penalty_terms, \
     949                                       self._drop_proximal_terms, \
     950                                       self._retain_quadratic_binary_terms, \
     951                                       self._breakpoint_strategy, \
     952                                       self._integer_tolerance)
     953         self._instance_augmented_attributes[instance_name].extend(new_attrs)
    1279954
    1280955   def iteration_k_solve(self):
  • coopr.pysp/trunk/coopr/pysp/phinit.py

    r2328 r2405  
    643643   
    644644    return ans
    645 
  • coopr.pysp/trunk/coopr/pysp/phserver.py

    r2402 r2405  
    2424from coopr.pysp.scenariotree import *
    2525from phutils import *
     26from phobjective import *
    2627
    2728# Pyro
     
    4950class PHSolverServer(object):
    5051
    51    def __init__(self, scenario_instances, solver):
    52 
    53       self._scenario_instances = scenario_instances
     52   def __init__(self, scenario_instances, solver, scenario_tree):
     53
     54      # the obvious stuff!
     55      self._instances = scenario_instances
    5456      self._solver = solver
     57      self._scenario_tree = scenario_tree
     58
     59      # the objective functions are modified throughout the course of PH iterations.
     60      # save the original, as a baseline to modify in subsequent iterations. reserve
     61      # the original objectives, for subsequent modification.
     62      self._original_objective_expression = {}
     63      for instance_name, instance in self._instances.items():
     64         objective_name = instance.active_components(Objective).keys()[0]
     65         expr = instance.active_components(Objective)[objective_name]._data[None].expr
     66         if isinstance(expr, Expression) is False:
     67            expr = _IdentityExpression(expr)
     68         self._original_objective_expression[instance_name] = expr
     69
     70      # the server is in one of two modes - solve the baseline instances, or
     71      # those augmented with the PH penalty terms. default is baseline.
     72      # NOTE: This is currently a global flag for all scenarios handled
     73      #       by this server - easy enough to extend if we want.
     74      self._solving_baseline_objective = True
     75
     76   def enable_standard_objective(self):
     77
     78      self._solving_baseline_objective = True
     79
     80   def enable_ph_objective(self):
     81
     82      self._solving_baseline_objective = False
    5583
    5684   def solve(self, scenario_name):
    5785
    58       if scenario_name not in self._scenario_instances:
     86      if scenario_name not in self._instances:
    5987         print "***ERROR: Requested instance to solve not in PH solver server instance collection!"
    6088         return None
    61       scenario_instance = self._scenario_instances[scenario_name]
     89      scenario_instance = self._instances[scenario_name]
     90
     91      # IMPT: You have to re-presolve, as the simple presolver collects the linear terms together. If you
     92      # don't do this, you won't see any chance in the output files as you vary the problem parameters!
     93      # ditto for instance fixing!
     94      scenario_instance.preprocess()
     95
     96      # form the desired objective, depending on the solve mode.
     97      if self._solving_baseline_objective is True:
     98         form_standard_objective(scenario_name, scenario_instance, self._original_objective_expression[scenario_name], self._scenario_tree)         
     99      else:
     100         # TBD - command-line drive the various options (integer tolerance, breakpoint strategies, etc.)
     101         form_ph_objective(scenario_name, scenario_instance, \
     102                           self._original_objective_expression[instance_name], self._scenario_tree, \
     103                           False, False, False, 0, 0.00001)
     104                           
    62105      results = self._solver.solve(scenario_instance)
     106
    63107      print "Successfully solved scenario instance="+scenario_name
     108      print "RESULTS:"
     109      results.write(num=1)
    64110      encoded_results = pickle.dumps(results)
    65111     
     
    70116      print "RECEIVED REQUEST TO UPDATE WEIGHTS AND AVERAGES FOR SCENARIO=",scenario_name
    71117
    72       if scenario_name not in self._scenario_instances:
     118      if scenario_name not in self._instances:
    73119         print "***ERROR: Received request to update weights for instance not in PH solver server instance collection!"
    74120         return None
    75       scenario_instance = self._scenario_instances[scenario_name]
     121      scenario_instance = self._instances[scenario_name]
    76122     
    77123      for weight_update in new_weights:
     
    99145      print "RECEIVED REQUEST TO UPDATE RHOS FOR SCENARIO=",scenario_name
    100146
    101       if scenario_name not in self._scenario_instances:
     147      if scenario_name not in self._instances:
    102148         print "***ERROR: Received request to update weights for instance not in PH solver server instance collection!"
    103149         return None
    104       scenario_instance = self._scenario_instances[scenario_name]     
     150      scenario_instance = self._instances[scenario_name]     
    105151
    106152      for rho_update in new_rhos:
     
    208254                     type="int",
    209255                     default=0)
    210    parser.add_option("--enable-gc",
    211                      help="Enable the python garbage collecter. Default is True.",
    212                      action="store_true",
    213                      dest="enable_gc",
    214                      default=True)
     256   parser.add_option("--disable-gc",
     257                     help="Disable the python garbage collecter. Default is False.",
     258                     action="store_true",
     259                     dest="disable_gc",
     260                     default=False)
    215261
    216262   parser.usage=usage_string
     
    288334#
    289335
    290 def run_server(options, scenario_instances, solver):
     336def run_server(options, scenario_instances, solver, scenario_tree):
    291337
    292338   start_time = time.time()
     
    304350   solver_daemon.useNameServer(ns)
    305351
    306    daemon_object = PHSolverServer(scenario_instances, solver)
     352   daemon_object = PHSolverServer(scenario_instances, solver, scenario_tree)
    307353   delegator_object = Pyro.core.ObjBase()
    308354   delegator_object.delegateTo(daemon_object)
     
    311357   # each scenario processed by this daemon.
    312358   for scenario_name, scenario_instance in scenario_instances.items():
    313       solver_daemon.connect(delegator_object, scenario_name)
     359      try:
     360         solver_daemon.connect(delegator_object, scenario_name)
     361      except Pyro.errors.NamingError:
     362         raise RuntimeError, "Entry in name server already exists for scenario="+scenario_name
    314363
    315364   while True:
     
    387436      solver._report_timing = True
    388437
    389 #   print "SOLVING!"
    390 #   results = solver.solve(scenario_instance)
    391 #   print "TYPE OF RESULTS=",type(results)
    392 #   results.write(num=1)
    393 #   print "RESULTS=",results
    394 #   print "DONE SOLVING!"
    395 
    396438   # spawn the daemon.
    397    run_server(options, scenario_instances, solver)
     439   run_server(options, scenario_instances, solver, scenario_tree)
    398440
    399441def run(args=None):
     
    418460    # much sense - so it is disabled by default. Because: It drops
    419461    # the run-time by a factor of 3-4 on bigger instances.
    420     if options.enable_gc is False:
     462    if options.disable_gc is True:
    421463       gc.disable()
    422464    else:
  • coopr.pysp/trunk/coopr/pysp/phsolvermanager.py

    r2401 r2405  
    4949        self._solver_proxy = {}
    5050
     51    #
     52    # a utility to identify (and create if necessary) the
     53    # appropriate proxy object for a given scenario.
     54    #
     55
     56    def _identify_proxy(self, scenario_name):
     57
     58       proxy = None
     59       if scenario_name in self._solver_proxy.keys():
     60          proxy = self._solver_proxy[scenario_name]
     61       else:
     62          uri = None
     63          try:
     64             uri = self._ns.resolve(scenario_name)
     65          except Pyro.errors.NamingError:
     66             raise RuntimeError, "***ERROR: Failed to locate PH solver server capable of processing scenario="+scenario_name
     67             sys.exit(0)
     68
     69          proxy = Pyro.core.getProxyForURI(uri)
     70          self._solver_proxy[scenario_name] = proxy
     71       return proxy
     72
    5173    def clear(self):
    5274        """
     
    78100            raise ActionManagerError, "Undefined solver"
    79101
    80         # identify the appropriate PH solver server, grabbing a handle if necessary.
    81         proxy = None
    82         if scenario_name in self._solver_proxy.keys():
    83            proxy = self._solver_proxy[scenario_name]
    84         else:
    85            uri = None
    86            try:
    87               uri = self._ns.resolve(scenario_name)
    88            except Pyro.errors.NamingError:
    89               raise RuntimeError, "***ERROR: Failed to locate PH solver server capable of processing scenario="+scenario_name
    90               sys.exit(0)
    91 
    92            proxy = Pyro.core.getProxyForURI(uri)
    93            self._solver_proxy[scenario_name] = proxy
    94 
    95102        # TBD - we don't actually transfer any solver options
    96103        #       at the moment, but we will soon!
    97104
     105        proxy = self._identify_proxy(scenario_name)
    98106        encoded_result = proxy.solve(scenario_name)
    99        
    100107        self.results[ah.id] = pickle.loads(encoded_result)
    101108        ah.status = ActionStatus.done
     
    115122        return ActionHandle(error=True, explanation="No queued evaluations available in the PH solver manager, which only executes solvers synchronously")
    116123
     124    def enable_ph_objective(self, scenario_instance):
     125
     126       proxy = self._identify_proxy(scenario_instance.name)
     127       proxy.enable_ph_objective()
     128
    117129    def transmit_weights_and_averages(self, scenario_instance, new_weights, new_averages):
    118130
    119        scenario_name = scenario_instance.name
    120 
    121        # identify the appropriate PH solver server, grabbing a handle if necessary.
    122        proxy = None
    123        if scenario_name in self._solver_proxy.keys():
    124           proxy = self._solver_proxy[scenario_name]
    125        else:
    126           uri = None
    127           try:
    128              uri = self._ns.resolve(scenario_name)
    129           except Pyro.errors.NamingError:
    130              raise RuntimeError, "***ERROR: Failed to locate PH solver server capable of processing scenario="+scenario_name
    131              sys.exit(0)
    132              
    133           proxy = Pyro.core.getProxyForURI(uri)
    134           self._solver_proxy[scenario_name] = proxy
    135 
    136        # execute the RMI.
    137        proxy.update_weights_and_averages(scenario_name, new_weights, new_averages)
     131       proxy = self._identify_proxy(scenario_instance.name)
     132       proxy.update_weights_and_averages(scenario_instance.name, new_weights, new_averages)
    138133
    139134    def transmit_rhos(self, scenario_instance, new_rhos):
    140135
    141        scenario_name = scenario_instance.name
    142 
    143        # identify the appropriate PH solver server, grabbing a handle if necessary.
    144        # TBD: The code block for extracting the proxy should be made common within the module.
    145        proxy = None
    146        if scenario_name in self._solver_proxy.keys():
    147           proxy = self._solver_proxy[scenario_name]
    148        else:
    149           uri = None
    150           try:
    151              uri = self._ns.resolve(scenario_name)
    152           except Pyro.errors.NamingError:
    153              raise RuntimeError, "***ERROR: Failed to locate PH solver server capable of processing scenario="+scenario_name
    154              sys.exit(0)
    155              
    156           proxy = Pyro.core.getProxyForURI(uri)
    157           self._solver_proxy[scenario_name] = proxy
    158 
    159        # execute the RMI.
    160        proxy.update_rhos(scenario_name, new_rhos)
     136       proxy = self._identify_proxy(scenario_instance.name)
     137       proxy.update_rhos(scenario_instance.name, new_rhos)
    161138
    162139SolverManagerRegistration("ph", PHSolverManager)
  • coopr.pysp/trunk/coopr/pysp/scenariotree.py

    r2404 r2405  
    4747   # a utility to compute the cost of the current node plus the expected costs of child nodes.
    4848   #
     49
    4950   def computeExpectedNodeCost(self, scenario_instance_map):
    5051
     
    347348   # is the indicated scenario in the tree?
    348349   #
     350
    349351   def contains_scenario(self, name):
    350352      return name in self._scenario_map.keys()
     
    353355   # get the scenario object from the tree.
    354356   #
     357
    355358   def get_scenario(self, name):
    356359      return self._scenario_map[name]
     
    360363   # the sum of all stage cost variables.
    361364   #
     365
    362366   def compute_scenario_cost(self, instance):
    363367      aggregate_cost = 0.0
     
    374378   # of the scenario tree structure.
    375379   #
     380
    376381   def compress(self, scenario_bundle_list):
    377382
     
    461466   # returns the root node of the scenario tree
    462467   #
     468
    463469   def findRootNode(self):
    464470
     
    472478   # the maximal length of identifiers in various categories.
    473479   #
     480
    474481   def computeIdentifierMaxLengths(self):
    475482
     
    482489   # a utility function to (partially, at the moment) validate a scenario tree
    483490   #
     491
    484492   def validate(self):
    485493
Note: See TracChangeset for help on using the changeset viewer.