def update_bounds_lists(var_name): var_lb = None var_ub = None if var_data.fixed and self._output_fixed_variable_bounds: var_lb = var_ub = var_data.value elif var_data.fixed: # if we've been directed to not deal with fixed # variables, then skip - they should have been # compiled out of any description of the constraints return else: if not var_data.has_lb(): var_lb = -CPLEXDirect._cplex_module.infinity else: var_lb = value(var_data.lb) if not var_data.has_ub(): var_ub = CPLEXDirect._cplex_module.infinity else: var_ub= value(var_data.ub) var_cplex_id = self._cplex_variable_ids[var_name] new_lower_bounds.append((var_cplex_id, var_lb)) new_upper_bounds.append((var_cplex_id, var_ub))
def cycle_ics_noisy(self, sigma_bar=0.0001): """Patches the initial conditions with the last result from the simulation with noise. Args: sigma_bar (float): The variance. Return None""" print("-" * 120) print("I[[cycle_ics]] Cycling initial state -- NOISY.") print("-" * 120) s = np.random.normal(0, sigma_bar) for x in self.states: x_ic = getattr(self.d1, x + "_ic") v_tgt = getattr(self.d1, x) for ks in x_ic.keys(): if type(ks) != tuple: ks = (ks, ) x_ic[ks].value = value(v_tgt[(1, self.ncp_t) + ks]) sigma = value(v_tgt[(1, self.ncp_t) + ks]) * s self.curr_state_noise[(x, ks)] = sigma tst_val = value(v_tgt[(1, self.ncp_t) + ks]) + sigma if tst_val < 0: print("error", tst_val, x, ks) x_ic[ks].value = value(v_tgt[(1, self.ncp_t) + ks]) + sigma self._c_it += 1
def instance2dat(instance, output_filename): output_file = open(output_filename, "w") for set_name, set_object in instance.component_map(Set, active=True).items(): if (set_object.initialize is not None) and (type(set_object.initialize) is types.FunctionType): continue if (set_name.find("_index_set") == -1) and (set_name.find("_domain") == -1): if set_object.dim() == 0: if len(set_object) == 0: continue output_file.write("set " + set_name + " := \n") for element in set_object: output_file.write(element, ) output_file.write(";\n") elif set_object.dim() == 1: for index in set_object: output_file.write("set " + set_name + "[\"" + str(index) + "\"]" + " :=") for element in set_object[index]: output_file.write(element, ) output_file.write(";\n") else: output_file.write( "***MULTIPLY INDEXED SETS NOT IMPLEMENTED!!!\n") pass output_file.write("\n") for param_name, param_object in instance.component_map( Param, active=True).items(): if (param_object._initialize is not None) and (type( param_object._initialize) is types.FunctionType): continue elif len(param_object) == 0: continue if None in param_object: output_file.write("param " + param_name + " := " + str(value(param_object[None])) + " ;\n") output_file.write("\n") else: output_file.write("param " + param_name + " := \n") if param_object.dim() == 1: for index in param_object: output_file.write( str(index) + str(value(param_object[index])) + "\n") else: for index in param_object: for i in index: output_file.write(i, ) output_file.write(str(value(param_object[index])) + "\n") output_file.write(";\n") output_file.write("\n") output_file.close()
def update_bounds_lists(var_name): var_lb = None var_ub = None if var_data.fixed and self._output_fixed_variable_bounds: var_lb = var_ub = var_data.value elif var_data.fixed: # if we've been directed to not deal with fixed # variables, then skip - they should have been # compiled out of any description of the constraints return else: if var_data.lb is None: var_lb = -cplex.infinity else: var_lb = value(var_data.lb) if var_data.ub is None: var_ub = cplex.infinity else: var_ub= value(var_data.ub) var_cplex_id = self._cplex_variable_ids[var_name] new_lower_bounds.append((var_cplex_id, var_lb)) new_upper_bounds.append((var_cplex_id, var_ub))
def _simulate_with_casadi_no_inputs(self, initcon, tsim, integrator, integrator_options): # Old way (10 times faster, but can't incorporate time # varying parameters/controls) xalltemp = [self._templatemap[i] for i in self._diffvars] xall = casadi.vertcat(*xalltemp) odealltemp = [value(self._rhsdict[i]) for i in self._derivlist] odeall = casadi.vertcat(*odealltemp) dae = {'x': xall, 'ode': odeall} if len(self._algvars) != 0: zalltemp = [self._templatemap[i] for i in self._simalgvars] zall = casadi.vertcat(*zalltemp) algalltemp = [value(i) for i in self._alglist] algall = casadi.vertcat(*algalltemp) dae['z'] = zall dae['alg'] = algall integrator_options['grid'] = tsim integrator_options['output_t0'] = True F = casadi.integrator('F', integrator, dae, integrator_options) sol = F(x0=initcon) profile = sol['xf'].full().T if len(self._algvars) != 0: algprofile = sol['zf'].full().T profile = np.concatenate((profile, algprofile), axis=1) return [tsim, profile]
def patch_meas_mhe(self, t, **kwargs): """Mechanism to assign a value of y0 to the current mhe from the dynamic model Args: t (int): int The current collocation point Returns: meas_dict (dict): A dictionary containing the measurements list by meas_var """ src = kwargs.pop("src", None) skip_update = kwargs.pop("skip_update", False) noisy = kwargs.pop("noisy", True) meas_dic = dict.fromkeys(self.y) l = [] for i in self.y: lm = [] var = getattr(src, i) for j in self.y_vars[i]: lm.append(value(var[(1, self.ncp_t,) + j])) l.append(value(var[(1, self.ncp_t,) + j])) meas_dic[i] = lm if not skip_update: #: Update the mhe model self.journalizer("I", self._c_it, "patch_meas_mhe", "Measurement patched to " + str(t)) y0dest = getattr(self.lsmhe, "yk0_mhe") # print("there is an update", file=sys.stderr) for i in self.y: for j in self.y_vars[i]: k = self.yk_key[(i, j)] #: Adding noise to the mhe measurement y0dest[t, k].value = l[k] + self.curr_m_noise[(i, j)] if noisy else l[k] return meas_dic
def instance2dat(instance, output_filename): output_file = open(output_filename, "w") for set_name, set_object in iteritems( instance.component_map(Set, active=True)): if (set_object.initialize is not None) and (type(set_object.initialize) is types.FunctionType): continue if (set_name.find("_index") == -1) and (set_name.find("_domain") == -1): if set_object.dim() == 0: if len(set_object) == 0: continue print >> output_file, "set " + set_name + " := " for element in set_object: print >> output_file, element print >> output_file, ";" elif set_object.dim() == 1: for index in set_object: print >> output_file, "set " + set_name + "[\"" + str( index) + "\"]" + " := ", for element in set_object[index]: print >> output_file, element, print >> output_file, ";" else: print >> output_file, "***MULTIPLY INDEXED SETS NOT IMPLEMENTED!!!" pass print >> output_file, "" for param_name, param_object in iteritems( instance.component_map(Param, active=True)): if (param_object._initialize is not None) and (type( param_object._initialize) is types.FunctionType): continue elif len(param_object) == 0: continue if None in param_object: print >> output_file, "param " + param_name + " := " + str( value(param_object[None])) + " ;" print >> output_file, "" else: print >> output_file, "param " + param_name + " := " if param_object.dim() == 1: for index in param_object: print >> output_file, index, str(value( param_object[index])) else: for index in param_object: for i in index: print >> output_file, i, print >> output_file, str(value(param_object[index])) print >> output_file, ";" print >> output_file, "" output_file.close()
def adjust_nu0_mhe(self): """Adjust the initial guess for the nu variable""" for t in self.lsmhe.fe_t: k = 0 for i in self.y: for j in self.y_vars[i]: target = value(self.lsmhe.yk0_mhe[t, k]) - value(self.yk_l[t][k]) self.lsmhe.nuk_mhe[t, k].set_value(target) k += 1
def shift_measurement_input_mhe(self): """Shifts current measurements for the mhe problem""" y0 = getattr(self.lsmhe, "yk0_mhe") for i in range(2, self.nfe_t + 1): for j in self.lsmhe.yk0_mhe.keys(): y0[i-1, j[1:]].value = value(y0[i, j[1:]]) for u in self.u: umhe = getattr(self.lsmhe, u) umhe[i-1] = value(umhe[i]) self.adjust_nu0_mhe()
def compute_y_offset(self, noisy=True): mhe_y = getattr(self.lsmhe, "yk0_mhe") for y in self.y: plant_y = getattr(self.d1, y) for j in self.y_vars[y]: k = self.yk_key[(y, j)] mhe_yval = value(mhe_y[self.nfe_t, k]) plant_yval = value(plant_y[(1, self.ncp_t) + j]) y_noise = self.curr_m_noise[(y, j)] if noisy else 0.0 self.curr_y_offset[(y, j)] = mhe_yval - plant_yval - y_noise
def get_result(self, name, index, state, start_time): obj = self.block.find_component(name) result = [] if obj is None: raise Exception( '{} is not a valid parameter or variable of {}'.format( name, self.name)) time = self.get_time_axis(state) if isinstance(obj, IndexedVar) and self.repr_days is None: if index is None: for i in obj: result.append(value(obj[i])) resname = self.name + '.' + name else: for i in time: result.append(obj[(index, i)].value) resname = self.name + '.' + name + '.' + index elif isinstance(obj, IndexedVar) and self.repr_days is not None: for d in self.DAYS_OF_YEAR: for t in time: result.append(value(obj[t, self.repr_days[d]])) resname = self.name + '.' + name elif isinstance(obj, IndexedParam): resname = self.name + '.' + name if self.repr_days is None: result = [] for t in obj: result.append(value(obj[t])) else: for d in self.DAYS_OF_YEAR: for t in time: result.append(value(obj[t, self.repr_days[d]])) else: self.logger.warning( '{}.{} was a different type of variable/parameter than what has been implemented: ' '{}'.format(self.name, name, type(obj))) return None timeindex = pd.DatetimeIndex(start=start_time, freq=str(self.params['time_step'].v()) + 'S', periods=len(result)) return pd.Series(data=result, index=timeindex, name=resname)
def _collect_linear_pow(exp, idMap, multiplier, coef, varmap, compute_values): if exp.is_fixed(): if compute_values: coef[None] += multiplier * value(exp) else: coef[None] += multiplier * exp elif value(exp._args[1]) == 1: arg = exp._args[0] _linear_collectors[arg.__class__](arg, idMap, multiplier, coef, varmap, compute_values) else: raise TypeError( "Unsupported power expression: "+str(exp._args) )
def equalize_u(self, direction="u_to_r"): """set current controls to the values of their respective dummies""" if direction == "u_to_r": for i in iterkeys(self.Rec): self.Rec[i].set_value(value(self.u1[i])) for i in iterkeys(self.Rec): self.Qr[i].set_value(value(self.u2[i])) elif direction == "r_to_u": for i in iterkeys(self.u1): self.u1[i].value = value(self.Rec[i]) for i in iterkeys(self.u2): self.u2[i].value = value(self.Qr[i])
def adjust_w_mhe(self): for i in range(1, self.nfe_t): j = 0 for x in self.x_noisy: x_var = getattr(self.lsmhe, x) for k in self.x_vars[x]: x1pvar_val = value(x_var[(i+1, 0), k]) x1var_val = value(x_var[(i, self.ncp_t), k]) if self.IgnoreProcessNoise: pass else: self.lsmhe.wk_mhe[i, j].set_value(x1pvar_val - x1var_val) j += 1
def instance2dat(instance, output_filename): output_file = open(output_filename,"w") for set_name, set_object in iteritems(instance.component_map(Set, active=True)): if (set_object.initialize is not None) and (type(set_object.initialize) is types.FunctionType): continue if (set_name.find("_index") == -1) and (set_name.find("_domain") == -1): if set_object.dim() == 0: if len(set_object) == 0: continue print >>output_file, "set "+set_name+" := " for element in set_object: print >>output_file, element print >>output_file, ";" elif set_object.dim() == 1: for index in set_object: print >>output_file, "set "+set_name+"[\""+str(index)+"\"]"+" := ", for element in set_object[index]: print >>output_file, element, print >>output_file, ";" else: print >>output_file, "***MULTIPLY INDEXED SETS NOT IMPLEMENTED!!!" pass print >>output_file, "" for param_name, param_object in iteritems(instance.component_map(Param, active=True)): if (param_object._initialize is not None) and (type(param_object._initialize) is types.FunctionType): continue elif len(param_object) == 0: continue if None in param_object: print >>output_file, "param "+param_name+" := "+str(value(param_object[None]))+" ;" print >>output_file, "" else: print >>output_file, "param "+param_name+" := " if param_object.dim() == 1: for index in param_object: print >>output_file, index, str(value(param_object[index])) else: for index in param_object: for i in index: print >>output_file, i, print >>output_file, str(value(param_object[index])) print >>output_file, ";" print >>output_file, "" output_file.close()
def solve_ss(self): """Solves steady state model Args: None Return: None""" # self.k_aug.solve(self.ss, tee=True, symbolic_solver_labels=True) with open("ipopt.opt", "w") as f: f.write("max_iter 100\n") f.write("mu_init 1e-08\n") f.write("bound_push 1e-08\n") f.write("print_info_string yes\n") f.close() ip = SolverFactory("ipopt") # ip.options["halt_on_ampl_error"] = "yes" ip.options["print_user_options"] = "yes" ip.options["linear_solver"] = "ma57" results = ip.solve(self.ss, tee=True, symbolic_solver_labels=True, report_timing=True) self.ss.solutions.load_from(results) for x in self.states: self.state_vars[x] = [] try: xv = getattr(self.ss, x) except AttributeError: # delete this continue for j in xv.keys(): if xv[j].stale: continue if type(j[2:]) == tuple: self.state_vars[x].append(j[2:]) else: self.state_vars[x].append((j[2:], )) for x in self.states: try: xvar = getattr(self.ss, x) except AttributeError: # delete this continue for j in self.state_vars[x]: self.curr_state_offset[(x, j)] = 0.0 self.curr_state_noise[(x, j)] = 0.0 self.curr_estate[(x, j)] = value(xvar[1, 1, j]) self.curr_rstate[(x, j)] = value(xvar[1, 1, j]) self.curr_state_target[(x, j)] = value(xvar[1, 1, j]) for u in self.u: uvar = getattr(self.ss, u) self.curr_u_target[u] = value(uvar[1]) self.curr_u[u] = value(uvar[1])
def load_input_mhe(self, src_kind, **kwargs): """Loads inputs into the mhe model""" src = kwargs.pop("src", self.d1) fe = kwargs.pop("fe", 1) # src_kind = kwargs.pop("src_kind", "mod") if src_kind == "mod": for u in self.u: usrc = getattr(src, u) utrg = getattr(self.lsmhe, u) utrg[fe].value = value(usrc[1]) elif src_kind == "self.dict": for u in self.u: utrg = getattr(self.lsmhe, u) utrg[fe].value = value(self.curr_u[u])
def __iadd__(self, other): _type = other.__class__ if _type in native_numeric_types: self.constant += other elif _type is CompiledLinearCanonicalRepn: self.constant += other.constant for v in other.variables: _id = id(v) if _id in self.linear: self.linear[_id] += other.linear[_id] else: self.variables.append(v) self.linear[_id] = other.linear[_id] CompiledLinearCanonicalRepn_Pool.append(other) elif other.is_fixed(): self.constant += value(other) else: assert isinstance(other, _VarData) _id = id(other) if _id in self.linear: self.linear[_id] += 1. else: self.variables.append(other) self.linear[_id] = 1. return self
def update_state_mhe(self, as_nmpc_mhe_strategy=False): # Improvised strategy if as_nmpc_mhe_strategy: self.journalizer("I", self._c_it, "update_state_mhe", "offset ready for asnmpcmhe") for x in self.states: xvar = getattr(self.lsmhe, x) x0 = getattr(self.olnmpc, x + "_ic") for j in self.state_vars[x]: # self.curr_state_offset[(x, j)] = self.curr_estate[(x, j)] - value(xvar[self.nfe_t, self.ncp_t, j]) self.curr_state_offset[(x, j)] = value(x0[j] )- value(xvar[self.nfe_t, self.ncp_t, j]) print("state !", self.curr_state_offset[(x, j)]) for x in self.states: xvar = getattr(self.lsmhe, x) for j in self.state_vars[x]: self.curr_estate[(x, j)] = value(xvar[self.nfe_t, self.ncp_t, j])
def get_objective(self, objtype=None, get_value=True): """ Return value of objective function. With no argument supplied, the active objective is returned. Otherwise, the objective specified in the argument is returned. :param objtype: Name of the objective to be returned. Default None: returns the active objective. :param value: True if value of objective should be returned. If false, the objective object instance is returned. :return: """ if objtype is None: # Find active objective if self.act_objective is not None: obj = self.act_objective else: raise ValueError('No active objective found.') else: assert objtype in self.objectives.keys( ), 'Requested objective does not exist. Please choose from {}'.format( self.objectives.keys()) obj = self.objectives[objtype] if get_value: return value(obj) else: return obj
def __imul__(self, other): _type = other.__class__ if _type in native_numeric_types: pass elif _type is CompiledLinearCanonicalRepn: if other.variables: self, other = other, self assert(not other.variables) CompiledLinearCanonicalRepn_Pool.append(other) other = other.constant elif other.is_fixed(): other = value(other) else: assert isinstance(other, _VarData) assert not self.variables self.variables.append(other) self.linear[id(other)] = self.constant self.constant = 0. return self if other: for _id in self.linear: self.linear[_id] *= other else: self.linear = {} self.variables = [] self.constant *= other return self
def check_expr(expr, name, solver_io): # Check if GAMS will encounter domain violations in presolver # operations at current values, which are None (0) by default # Used to handle log and log10 violations, for example try: value(expr) except ValueError: logger.warning("While evaluating model.%s's expression, GAMS solver " "encountered an error.\nGAMS requires that all " "equations and expressions evaluate at initial values.\n" "Ensure variable values do not violate any domains, " "and use the warmstart=True keyword to solve()." % name) if solver_io == 'shell': # For shell, there is no previous exception to worry about # overwriting, so raise the ValueError. # But for direct, the GamsExceptionExecution will be raised. raise
def propagate_solution(self, scaled_model, original_model): """ This method takes the solution in scaled_model and maps it back to the original model. It will also transform duals and reduced costs if the suffixes 'dual' and/or 'rc' are present. The :code:`scaled_model` argument must be a model that was already scaled using this transformation as it expects data from the transformation to perform the back mapping. Parameters ---------- scaled_model : Pyomo Model The model that was previously scaled with this transformation original_model : Pyomo Model The original unscaled source model """ if not hasattr(scaled_model, 'component_scaling_factor_map'): raise AttributeError('ScaleModel:propagate_solution called with scaled_model that does not ' 'have a component_scaling_factor_map. It is possible this method was called ' 'using a model that was not scaled with the ScaleModel transformation') if not hasattr(scaled_model, 'scaled_component_to_original_name_map'): raise AttributeError('ScaleModel:propagate_solution called with scaled_model that does not ' 'have a scaled_component_to_original_name_map. It is possible this method was called ' 'using a model that was not scaled with the ScaleModel transformation') component_scaling_factor_map = scaled_model.component_scaling_factor_map scaled_component_to_original_name_map = scaled_model.scaled_component_to_original_name_map # get the objective scaling factor scaled_objectives = list(scaled_model.component_data_objects(ctype=Objective, active=True, descend_into=True)) if len(scaled_objectives) != 1: raise NotImplementedError( 'ScaleModel.propagate_solution requires a single active objective function, but %d objectives found.' % ( len(objectives))) objective_scaling_factor = component_scaling_factor_map[scaled_objectives[0]] # transfer the variable values and reduced costs check_reduced_costs = type(scaled_model.component('rc')) is Suffix for scaled_v in scaled_model.component_objects(ctype=Var, descend_into=True): # get the unscaled_v from the original model original_v_path = scaled_component_to_original_name_map[scaled_v] original_v = original_model.find_component(original_v_path) for k in scaled_v: original_v[k].value = value(scaled_v[k]) / component_scaling_factor_map[scaled_v[k]] if check_reduced_costs and scaled_v[k] in scaled_model.rc: original_model.rc[original_v[k]] = scaled_model.rc[scaled_v[k]] * component_scaling_factor_map[ scaled_v[k]] / objective_scaling_factor # transfer the duals if type(scaled_model.component('dual')) is Suffix and type(original_model.component('dual')) is Suffix: for scaled_c in scaled_model.component_objects(ctype=Constraint, descend_into=True): original_c = original_model.find_component(scaled_component_to_original_name_map[scaled_c]) for k in scaled_c: original_model.dual[original_c[k]] = scaled_model.dual[scaled_c[k]] * component_scaling_factor_map[ scaled_c[k]] / objective_scaling_factor
def _collect_linear_intrinsic(exp, idMap, multiplier, coef, varmap, compute_values): if exp.is_fixed(): if compute_values: coef[None] += multiplier * value(exp) else: coef[None] += multiplier * exp else: raise TypeError( "Unsupported intrinsic expression: %s: %s" % (exp, str(exp._args)) )
def _collect_identity(exp, idMap, multiplier, coef, varmap, compute_values): exp = exp.expr if exp.is_fixed(): if compute_values: coef[None] += multiplier * value(exp) else: coef[None] += multiplier * exp else: _linear_collectors[exp.__class__](exp, idMap, multiplier, coef, varmap, compute_values)
def visiting_potential_leaf(self, node): """ Visiting a potential leaf. Return True if the node is not expanded. """ if node.__class__ in native_numeric_types: return True, node if node.__class__ is casadi.SX: return True, node if node.is_variable_type(): return True, value(node) if not node.is_expression_type(): return True, value(node) return False, None
def _temp_bal_incoming(b, t, l): incoming_comps = collections.defaultdict(list) incoming_pipes = collections.defaultdict(list) for name, comp in c.items(): if value(comp.get_mflo(t)) >= 0: incoming_comps['supply'].append(name) else: incoming_comps['return'].append(name) for name, pipe in p.items(): if value(pipe.get_edge_mflo(self.name, t)) >= 0: incoming_pipes['supply'].append(name) else: incoming_pipes['return'].append(name) # Zero mass flow rate: if value( sum(c[comp].get_mflo(t) for comp in incoming_comps[l]) + \ sum(p[pipe].get_edge_mflo(self.name, t) for pipe in incoming_pipes[l])) == 0: # mixed temperature is average of all joined pipes, actual value should not matter, # because packages in pipes of this time step will have zero size and components do not take over # mixed temperature in case there is no mass flow return b.mix_temp[ t, l] == (sum(c[comp].get_temperature(t, l) for comp in c) + sum(p[pipe].get_temperature(self.name, t, l) for pipe in p)) / (len(p) + len(c)) else: # mass flow rate through the node return (sum( c[comp].get_mflo(t) for comp in incoming_comps[l]) + sum(p[pipe].get_edge_mflo(self.name, t) for pipe in incoming_pipes[l])) * b.mix_temp[t, l] == \ sum(c[comp].get_mflo(t) * c[comp].get_temperature(t, l) for comp in incoming_comps[l]) + \ sum(p[pipe].get_edge_mflo(self.name, t) * p[ pipe].get_edge_temperature(self.name, t, l) for pipe in incoming_pipes[l])
def _collect_linear_prod(exp, idMap, multiplier, coef, varmap, compute_values): multiplier *= exp._coef _coef = {None: 0} _varmap = {} for subexp in exp._denominator: if compute_values: x = value( subexp) # only have constants/fixed terms in the denominator. if x == 0: logger.error( "Divide-by-zero: offending sub-expression:\n %s" % str(subexp)) raise ZeroDivisionError multiplier /= x else: multiplier /= subexp for subexp in exp._numerator: if _varmap: if compute_values: multiplier *= value(subexp) else: multiplier *= subexp else: _get_linear_collector(subexp, idMap, 1, _coef, _varmap, compute_values) if not _varmap: multiplier *= _coef[None] _coef[None] = 0 if _varmap: for key, val in iteritems(_coef): if key in coef: coef[key] += multiplier * val else: coef[key] = multiplier * val varmap.update(_varmap) else: # constant expression; i.e. 1/x coef[None] += multiplier
def cycle_ics(self, plant_step=False): """Patches the initial conditions with the last result from the simulation Args: None Return None""" print("-" * 120) print("I[[cycle_ics]] Cycling initial state.") print("-" * 120) for x in self.states: x_ic = getattr(self.d1, x + "_ic") v_tgt = getattr(self.d1, x) for ks in x_ic.keys(): if type(ks) != tuple: ks = (ks, ) x_ic[ks].value = value(v_tgt[(1, self.ncp_t) + ks]) v_tgt[(1, 0) + ks].set_value(value(v_tgt[(1, self.ncp_t) + ks])) if plant_step: self._c_it += 1
def update_noise_meas(self, mod, cov_dict): self.journalizer("I", self._c_it, "introduce_noise_meas", "Noise introduction") # f = open("m0.txt", "w") # f1 = open("m1.txt", "w") for y in self.y: vy = getattr(mod, y) # vy.display(ostream=f) for j in self.y_vars[y]: vv = value(vy[(1, self.ncp_t) + j]) sigma = cov_dict[(y, j), (y, j), 1] self.curr_m_noise[(y, j)] = np.random.normal(0, sigma)
def initialize_xreal(self, ref): """Wanted to keep the states in a horizon-like window, this should be done in the main dyngen class""" dum = self.d_mod(1, self.ncp_t, _t=self.hi_t) dum.name = "Dummy [xreal]" self.load_d_d(ref, dum, 1) for fe in range(1, self._window_keep): for i in self.states: pn = i + "_ic" p = getattr(dum, pn) vs = getattr(dum, i) for ks in p.iterkeys(): p[ks].value = value(vs[(1, self.ncp_t) + (ks,)]) #: Solve self.solve_d(dum, o_tee=False) for i in self.states: self.xreal_W[(i, fe)] = [] xs = getattr(dum, i) for k in xs.keys(): if k[1] == self.ncp_t: print(i) self.xreal_W[(i, fe)].append(value(xs[k]))
def _collect_linear_prod(exp, idMap, multiplier, coef, varmap, compute_values): multiplier *= exp._coef _coef = { None : 0 } _varmap = {} for subexp in exp._denominator: if compute_values: x = value(subexp) # only have constants/fixed terms in the denominator. if x == 0: buf = StringIO() subexp.pprint(buf) logger.error("Divide-by-zero: offending sub-expression:\n " + buf) raise ZeroDivisionError multiplier /= x else: multiplier /= subexp for subexp in exp._numerator: if _varmap: if compute_values: multiplier *= value(subexp) else: multiplier *= subexp else: _linear_collectors[subexp.__class__](subexp, idMap, 1, _coef, _varmap, compute_values) if not _varmap: multiplier *= _coef[None] _coef[None] = 0 if _varmap: for key, val in iteritems(_coef): if key in coef: coef[key] += multiplier * val else: coef[key] = multiplier * val varmap.update(_varmap) else: # constant expression; i.e. 1/x coef[None] += multiplier
def coopr3_generate_canonical_repn(exp, idMap=None, compute_values=True): if exp is None: return CompiledLinearCanonicalRepn() degree = exp.polynomial_degree() if idMap is None: idMap = {} idMap.setdefault(None, {}) if degree == 0: ans = CompiledLinearCanonicalRepn() ans.constant = value(exp) return ans elif degree == 1: # varmap is a map from the variable id() to a _VarData. # coef is a map from the variable id() to its coefficient. coef, varmap = collect_linear_canonical_repn(exp, idMap, compute_values) ans = CompiledLinearCanonicalRepn() if None in coef: val = coef.pop(None) if type(val) not in [int, float] or val != 0.0: ans.constant = val # the six module is inefficient in terms of wrapping iterkeys # and itervalues, in the context of Python 2.7. use the native # dictionary methods where possible. if using_py3: ans.linear = tuple(itervalues(coef)) ans.variables = tuple(varmap[var_hash] for var_hash in iterkeys(coef)) else: ans.linear = tuple(coef.itervalues()) ans.variables = tuple(varmap[var_hash] for var_hash in coef.iterkeys()) return ans # **Py3k: degree > 1 comparision will error if degree is None elif degree and degree > 1: ans = collect_general_canonical_repn(exp, idMap, compute_values) if 1 in ans: linear_terms = {} for key, coef in iteritems(ans[1]): linear_terms[list(key.keys())[0]] = coef ans[1] = linear_terms return GeneralCanonicalRepn(ans) else: return GeneralCanonicalRepn({ None: exp, -1: collect_variables(exp, idMap) })
def get_table(self): tmp = [] if not self.options.columns is None: tmp.append(self.options.columns) if not self.options.set is None: # Create column names if self.options.columns is None: cols = [] for i in xrange(self.options.set.dimen): cols.append(self.options.set.local_name+str(i)) tmp.append(cols) # Get rows if not self.options.sort is None: for data in sorted(self.options.set): if self.options.set.dimen > 1: tmp.append(list(data)) else: tmp.append([data]) else: for data in self.options.set: if self.options.set.dimen > 1: tmp.append(list(data)) else: tmp.append([data]) elif not self.options.param is None: if type(self.options.param) in (list,tuple): _param = self.options.param else: _param = [self.options.param] tmp = [] # Collect data for index in _param[0]: if index is None: row = [] elif type(index) in (list,tuple): row = list(index) else: row = [index] for param in _param: row.append(value(param[index])) tmp.append(row) # Create column names if self.options.columns is None: cols = [] for i in xrange(len(tmp[0])-len(_param)): cols.append('I'+str(i)) for param in _param: cols.append(param) tmp = [cols] + tmp return tmp
def subproblem_solve(gdp, solver, config): subproblem = gdp.clone() TransformationFactory('gdp.fix_disjuncts').apply_to(subproblem) result = solver.solve(subproblem, **config.solver_args) main_obj = next(subproblem.component_data_objects(Objective, active=True)) obj_sign = 1 if main_obj.sense == minimize else -1 if (result.solver.status is SolverStatus.ok and result.solver.termination_condition is tc.optimal): return value(main_obj.expr), result, subproblem.GDPbb_utils.variable_list elif result.solver.termination_condition is tc.unbounded: return obj_sign * float('-inf'), result, subproblem.GDPbb_utils.variable_list else: return obj_sign * float('inf'), result, subproblem.GDPbb_utils.variable_list
def coopr3_generate_canonical_repn(exp, idMap=None, compute_values=True): if exp is None: return CompiledLinearCanonicalRepn() degree = exp.polynomial_degree() if idMap is None: idMap = {} idMap.setdefault(None, {}) if degree == 0: ans = CompiledLinearCanonicalRepn() ans.constant = value(exp) return ans elif degree == 1: # varmap is a map from the variable id() to a _VarData. # coef is a map from the variable id() to its coefficient. coef, varmap = collect_linear_canonical_repn(exp, idMap, compute_values) ans = CompiledLinearCanonicalRepn() if None in coef: val = coef.pop(None) if type(val) not in [int,float] or val != 0.0: ans.constant = val # the six module is inefficient in terms of wrapping iterkeys # and itervalues, in the context of Python 2.7. use the native # dictionary methods where possible. if using_py3: ans.linear = tuple( itervalues(coef) ) ans.variables = tuple(varmap[var_hash] for var_hash in iterkeys(coef) ) else: ans.linear = tuple( coef.itervalues() ) ans.variables = tuple(varmap[var_hash] for var_hash in coef.iterkeys() ) return ans # **Py3k: degree > 1 comparision will error if degree is None elif degree and degree > 1: ans = collect_general_canonical_repn(exp, idMap, compute_values) if 1 in ans: linear_terms = {} for key, coef in iteritems(ans[1]): linear_terms[list(key.keys())[0]] = coef ans[1] = linear_terms return GeneralCanonicalRepn(ans) else: return GeneralCanonicalRepn( { None: exp, -1 : collect_variables(exp, idMap) } )
def _collect_linear_var(exp, idMap, multiplier, coef, varmap, compute_values): if exp.is_fixed(): if compute_values: coef[None] += multiplier * value(exp) else: coef[None] += multiplier * exp else: id_ = id(exp) if id_ in idMap[None]: key = idMap[None][id_] else: key = len(idMap) - 1 idMap[None][id_] = key idMap[key] = exp # if key in coef: coef[key] += multiplier else: coef[key] = multiplier varmap[key] = exp
def compile_instance(self, pyomo_instance, symbolic_solver_labels=False, output_fixed_variable_bounds=False, skip_trivial_constraints=False): from pyomo.core.base import Var, Constraint, SOSConstraint from pyomo.repn import canonical_is_constant, LinearCanonicalRepn, canonical_degree self._symbolic_solver_labels = symbolic_solver_labels self._output_fixed_variable_bounds = output_fixed_variable_bounds self._skip_trivial_constraints = skip_trivial_constraints self._has_quadratic_constraints = False self._has_quadratic_objective = False used_sos_constraints = False self._active_cplex_instance = cplex.Cplex() if self._symbolic_solver_labels: labeler = self._labeler = TextLabeler() else: labeler = self._labeler = NumericLabeler('x') self._symbol_map = SymbolMap() self._instance = pyomo_instance pyomo_instance.solutions.add_symbol_map(self._symbol_map) self._smap_id = id(self._symbol_map) # we use this when iterating over the constraints because it # will have a much smaller hash table, we also use this for # the warm start code after it is cleaned to only contain # variables referenced in the constraints self._variable_symbol_map = SymbolMap() # cplex wants the caller to set the problem type, which is (for # current purposes) strictly based on variable type counts. num_binary_variables = 0 num_integer_variables = 0 num_continuous_variables = 0 ############################################# # populate the variables in the cplex model # ############################################# var_names = [] var_lbs = [] var_ubs = [] var_types = [] self._referenced_variable_ids.clear() # maps pyomo var data labels to the corresponding CPLEX variable id. self._cplex_variable_ids.clear() # cached in the loop below - used to update the symbol map # immediately following loop termination. var_label_pairs = [] for var_data in pyomo_instance.component_data_objects(Var, active=True): if var_data.fixed and not self._output_fixed_variable_bounds: # if a variable is fixed, and we're preprocessing # fixed variables (as in not outputting them), there # is no need to add them to the compiled model. continue var_name = self._symbol_map.getSymbol(var_data, labeler) var_names.append(var_name) var_label_pairs.append((var_data, var_name)) self._cplex_variable_ids[var_name] = len(self._cplex_variable_ids) if (var_data.lb is None) or (var_data.lb == -infinity): var_lbs.append(-cplex.infinity) else: var_lbs.append(value(var_data.lb)) if (var_data.ub is None) or (var_data.ub == infinity): var_ubs.append(cplex.infinity) else: var_ubs.append(value(var_data.ub)) if var_data.is_integer(): var_types.append(self._active_cplex_instance.variables.type.integer) num_integer_variables += 1 elif var_data.is_binary(): var_types.append(self._active_cplex_instance.variables.type.binary) num_binary_variables += 1 elif var_data.is_continuous(): var_types.append(self._active_cplex_instance.variables.type.continuous) num_continuous_variables += 1 else: raise TypeError("Invalid domain type for variable with name '%s'. " "Variable is not continuous, integer, or binary.") self._active_cplex_instance.variables.add(names=var_names, lb=var_lbs, ub=var_ubs, types=var_types) self._active_cplex_instance.variables.add(lb=[1], ub=[1], names=["ONE_VAR_CONSTANT"]) self._cplex_variable_ids["ONE_VAR_CONSTANT"] = len(self._cplex_variable_ids) self._variable_symbol_map.addSymbols(var_label_pairs) self._cplex_variable_names = self._active_cplex_instance.variables.get_names() ######################################################## # populate the standard constraints in the cplex model # ######################################################## expressions = [] senses = [] rhss = [] range_values = [] names = [] qexpressions = [] qlinears = [] qsenses = [] qrhss = [] qnames = [] for block in pyomo_instance.block_data_objects(active=True): gen_con_canonical_repn = \ getattr(block, "_gen_con_canonical_repn", True) # Get/Create the ComponentMap for the repn if not hasattr(block,'_canonical_repn'): block._canonical_repn = ComponentMap() block_canonical_repn = block._canonical_repn for con in block.component_data_objects(Constraint, active=True, descend_into=False): if (con.lower is None) and \ (con.upper is None): continue # not binding at all, don't bother con_repn = None if isinstance(con, LinearCanonicalRepn): con_repn = con else: if gen_con_canonical_repn: con_repn = generate_canonical_repn(con.body) block_canonical_repn[con] = con_repn else: con_repn = block_canonical_repn[con] # There are conditions, e.g., when fixing variables, under which # a constraint block might be empty. Ignore these, for both # practical reasons and the fact that the CPLEX LP format # requires a variable in the constraint body. It is also # possible that the body of the constraint consists of only a # constant, in which case the "variable" of if isinstance(con_repn, LinearCanonicalRepn): if (con_repn.linear is None) and \ self._skip_trivial_constraints: continue else: # we shouldn't come across a constant canonical repn # that is not LinearCanonicalRepn assert not canonical_is_constant(con_repn) name = self._symbol_map.getSymbol(con, labeler) expr = None qexpr = None quadratic = False if isinstance(con_repn, LinearCanonicalRepn): expr, offset = \ self._encode_constraint_body_linear_specialized(con_repn, labeler, use_variable_names=False, cplex_variable_name_index_map=self._cplex_variable_ids) else: degree = canonical_degree(con_repn) if degree == 2: quadratic = True elif (degree != 0) or (degree != 1): raise ValueError( "CPLEXPersistent plugin does not support general nonlinear " "constraint expression (only linear or quadratic).\n" "Constraint: %s" % (con.cname(True))) expr, offset = self._encode_constraint_body_linear(con_repn, labeler) if quadratic: if expr is None: expr = cplex.SparsePair(ind=[0],val=[0.0]) self._has_quadratic_constraints = True qexpr = self._encode_constraint_body_quadratic(con_repn,labeler) qnames.append(name) if con.equality: # equality constraint. qsenses.append('E') qrhss.append(self._get_bound(con.lower) - offset) elif (con.lower is not None) and (con.upper is not None): raise RuntimeError( "The CPLEXDirect plugin can not translate range " "constraints containing quadratic expressions.") elif con.lower is not None: assert con.upper is None qsenses.append('G') qrhss.append(self._get_bound(con.lower) - offset) else: qsenses.append('L') qrhss.append(self._get_bound(con.upper) - offset) qlinears.append(expr) qexpressions.append(qexpr) else: names.append(name) expressions.append(expr) if con.equality: # equality constraint. senses.append('E') rhss.append(self._get_bound(con.lower) - offset) range_values.append(0.0) elif (con.lower is not None) and (con.upper is not None): # ranged constraint. senses.append('R') lower_bound = self._get_bound(con.lower) - offset upper_bound = self._get_bound(con.upper) - offset rhss.append(lower_bound) range_values.append(upper_bound - lower_bound) elif con.lower is not None: senses.append('G') rhss.append(self._get_bound(con.lower) - offset) range_values.append(0.0) else: senses.append('L') rhss.append(self._get_bound(con.upper) - offset) range_values.append(0.0) ################################################### # populate the SOS constraints in the cplex model # ################################################### # SOS constraints - largely taken from cpxlp.py so updates there, # should be applied here # TODO: Allow users to specify the variables coefficients for custom # branching/set orders - refer to cpxlp.py sosn = self._capabilities.sosn sos1 = self._capabilities.sos1 sos2 = self._capabilities.sos2 modelSOS = ModelSOS() for soscondata in pyomo_instance.component_data_objects(SOSConstraint, active=True): level = soscondata.level if (level == 1 and not sos1) or \ (level == 2 and not sos2) or \ (level > 2 and not sosn): raise Exception("Solver does not support SOS level %s constraints" % (level,)) modelSOS.count_constraint(self._symbol_map, labeler, self._variable_symbol_map, soscondata) if modelSOS.sosType: for key in modelSOS.sosType: self._active_cplex_instance.SOS.add(type = modelSOS.sosType[key], name = modelSOS.sosName[key], SOS = [modelSOS.varnames[key], modelSOS.weights[key]]) self._referenced_variable_ids.update(modelSOS.varids[key]) used_sos_constraints = True self._active_cplex_instance.linear_constraints.add( lin_expr=expressions, senses=senses, rhs=rhss, range_values=range_values, names=names) for index in xrange(len(qexpressions)): self._active_cplex_instance.quadratic_constraints.add( lin_expr=qlinears[index], quad_expr=qexpressions[index], sense=qsenses[index], rhs=qrhss[index], name=qnames[index]) ############################################# # populate the objective in the cplex model # ############################################# self.compile_objective(pyomo_instance) ################################################ # populate the problem type in the cplex model # ################################################ # This gets rid of the annoying "Freeing MIP data." message. def _filter_freeing_mip_data(val): if val.strip() == 'Freeing MIP data.': return "" return val self._active_cplex_instance.set_warning_stream(sys.stderr, fn=_filter_freeing_mip_data) if (self._has_quadratic_objective is True) or \ (self._has_quadratic_constraints is True): if (num_integer_variables > 0) or \ (num_binary_variables > 0) or \ (used_sos_constraints): if self._has_quadratic_constraints is True: self._active_cplex_instance.set_problem_type( self._active_cplex_instance.problem_type.MIQCP) else: self._active_cplex_instance.set_problem_type( self._active_cplex_instance.problem_type.MIQP) else: if self._has_quadratic_constraints is True: self._active_cplex_instance.set_problem_type( self._active_cplex_instance.problem_type.QCP) else: self._active_cplex_instance.set_problem_type( self._active_cplex_instance.problem_type.QP) elif (num_integer_variables > 0) or \ (num_binary_variables > 0) or \ (used_sos_constraints): self._active_cplex_instance.set_problem_type( self._active_cplex_instance.problem_type.MILP) else: self._active_cplex_instance.set_problem_type( self._active_cplex_instance.problem_type.LP) # restore the warning stream without our filter function self._active_cplex_instance.set_warning_stream(sys.stderr)
def simulate(self, numpoints=None, tstep=None, integrator=None, varying_inputs=None, initcon=None, integrator_options=None): """ Simulate the model. Integrator-specific options may be specified as keyword arguments and will be passed on to the integrator. Parameters ---------- numpoints : int The number of points for the profiles returned by the simulator. Default is 100 tstep : int or float The time step to use in the profiles returned by the simulator. This is not the time step used internally by the integrators. This is an optional parameter that may be specified in place of 'numpoints'. integrator : string The string name of the integrator to use for simulation. The default is 'lsoda' when using Scipy and 'idas' when using CasADi varying_inputs : ``pyomo.environ.Suffix`` A :py:class:`Suffix<pyomo.environ.Suffix>` object containing the piecewise constant profiles to be used for certain time-varying algebraic variables. initcon : list of floats The initial conditions for the the differential variables. This is an optional argument. If not specified then the simulator will use the current value of the differential variables at the lower bound of the ContinuousSet for the initial condition. integrator_options : dict Dictionary containing options that should be passed to the integrator. See the documentation for a specific integrator for a list of valid options. Returns ------- numpy array, numpy array The first return value is a 1D array of time points corresponding to the second return value which is a 2D array of the profiles for the simulated differential and algebraic variables. """ if not numpy_available: raise ValueError("The numpy module is not available. " "Cannot simulate the model.") if integrator_options is None: integrator_options = {} if self._intpackage == 'scipy': # Specify the scipy integrator to use for simulation valid_integrators = ['vode', 'zvode', 'lsoda', 'dopri5', 'dop853'] if integrator is None: integrator = 'lsoda' elif integrator is 'odeint': integrator = 'lsoda' else: # Specify the casadi integrator to use for simulation. # Only a subset of these integrators may be used for # DAE simulation. We defer this check to CasADi. valid_integrators = ['cvodes', 'idas', 'collocation', 'rk'] if integrator is None: integrator = 'idas' if integrator not in valid_integrators: raise DAE_Error("Unrecognized %s integrator \'%s\'. Please select" " an integrator from %s" % (self._intpackage, integrator, valid_integrators)) # Set the time step or the number of points for the lists # returned by the integrator if tstep is not None and \ tstep > (self._contset.last() - self._contset.first()): raise ValueError( "The step size %6.2f is larger than the span of the " "ContinuousSet %s" % (tstep, self._contset.name())) if tstep is not None and numpoints is not None: raise ValueError( "Cannot specify both the step size and the number of " "points for the simulator") if tstep is None and numpoints is None: # Use 100 points by default numpoints = 100 if tstep is None: tsim = np.linspace( self._contset.first(), self._contset.last(), num=numpoints) # Consider adding an option for log spaced time points. Can be # important for simulating stiff systems. # tsim = np.logspace(-4,6, num=100) # np.log10(self._contset.first()),np.log10( # self._contset.last()),num=1000, endpoint=True) else: tsim = np.arange( self._contset.first(), self._contset.last(), tstep) switchpts = [] self._siminputvars = {} self._simalgvars = [] if varying_inputs is not None: if type(varying_inputs) is not Suffix: raise TypeError( "Varying input values must be specified using a " "Suffix. Please refer to the simulator documentation.") for alg in self._algvars: if alg._base in varying_inputs: # Find all the switching points switchpts += varying_inputs[alg._base].keys() # Add to dictionary of siminputvars self._siminputvars[alg._base] = alg else: self._simalgvars.append(alg) if self._intpackage is 'scipy' and len(self._simalgvars) != 0: raise DAE_Error("When simulating with Scipy you must " "provide values for all parameters " "and algebraic variables that are indexed " "by the ContinuoutSet using the " "'varying_inputs' keyword argument. " "Please refer to the simulator documentation " "for more information.") # Get the set of unique points switchpts = list(set(switchpts)) switchpts.sort() # Make sure all the switchpts are within the bounds of # the ContinuousSet if switchpts[0] < self._contset.first() or \ switchpts[-1] > self._contset.last(): raise ValueError("Found a switching point for one or more of " "the time-varying inputs that is not within " "the bounds of the ContinuousSet.") # Update tsim to include input switching points # This numpy function returns the unique, sorted points tsim = np.union1d(tsim, switchpts) else: self._simalgvars = self._algvars # Check if initial conditions were provided, otherwise obtain # them from the current variable values if initcon is not None: if len(initcon) > len(self._diffvars): raise ValueError( "Too many initial conditions were specified. The " "simulator was expecting a list with %i values." % len(self._diffvars)) if len(initcon) < len(self._diffvars): raise ValueError( "Too few initial conditions were specified. The " "simulator was expecting a list with %i values." % len(self._diffvars)) else: initcon = [] for v in self._diffvars: for idx, i in enumerate(v._args): if type(i) is IndexTemplate: break initpoint = self._contset.first() vidx = tuple(v._args[0:idx]) + (initpoint,) + \ tuple(v._args[idx + 1:]) # This line will raise an error if no value was set initcon.append(value(v._base[vidx])) # Call the integrator if self._intpackage is 'scipy': if not scipy_available: raise ValueError("The scipy module is not available. " "Cannot simulate the model.") tsim, profile = self._simulate_with_scipy(initcon, tsim, switchpts, varying_inputs, integrator, integrator_options) else: if len(switchpts) != 0: tsim, profile = \ self._simulate_with_casadi_with_inputs(initcon, tsim, varying_inputs, integrator, integrator_options) else: tsim, profile = \ self._simulate_with_casadi_no_inputs(initcon, tsim, integrator, integrator_options) self._tsim = tsim self._simsolution = profile return [tsim, profile]
def _apply_to(self, model, **kwds): # create a map of component to scaling factor component_scaling_factor_map = ComponentMap() # if the scaling_method is 'user', get the scaling parameters from the suffixes if self._scaling_method == 'user': # perform some checks to make sure we have the necessary suffixes if type(model.component('scaling_factor')) is not Suffix: raise ValueError("ScaleModel transformation called with scaling_method='user'" ", but cannot find the suffix 'scaling_factor' on the model") # get the scaling factors for c in model.component_data_objects(ctype=(Var, Constraint, Objective), descend_into=True): component_scaling_factor_map[c] = self._get_float_scaling_factor(model, c) else: raise ValueError("ScaleModel transformation: unknown scaling_method found" "-- supported values: 'user' ") # rename all the Vars, Constraints, and Objectives from foo to scaled_foo scaled_component_to_original_name_map = \ rename_components(model=model, component_list=list(model.component_objects(ctype=[Var, Constraint, Objective])), prefix='scaled_') # scale the variable bounds and values and build the variable substitution map # for scaling vars in constraints variable_substitution_map = ComponentMap() for variable in [var for var in model.component_objects(ctype=Var, descend_into=True)]: # set the bounds/value for the scaled variable for k in variable: v = variable[k] scaling_factor = component_scaling_factor_map[v] variable_substitution_map[v] = v / scaling_factor if v.lb is not None: v.setlb(v.lb * scaling_factor) if v.ub is not None: v.setub(v.ub * scaling_factor) if scaling_factor < 0: temp = v.lb v.setlb(v.ub) v.setub(temp) if v.value is not None: v.value = value(v) * scaling_factor # scale the objectives/constraints and perform the scaled variable substitution scale_constraint_dual = False if type(model.component('dual')) is Suffix: scale_constraint_dual = True # translate the variable_substitution_map (ComponentMap) # to variable_substition_dict (key: id() of component) # ToDo: We should change replace_expressions to accept a ComponentMap as well variable_substitution_dict = dict() for k in variable_substitution_map: variable_substitution_dict[id(k)] = variable_substitution_map[k] for component in model.component_objects(ctype=(Constraint, Objective), descend_into=True): for k in component: c = component[k] # perform the constraint/objective scaling and variable sub scaling_factor = component_scaling_factor_map[c] if isinstance(c, _ConstraintData): body = scaling_factor * \ replace_expressions(expr=c.body, substitution_map=variable_substitution_dict, descend_into_named_expressions=True, remove_named_expressions=True) # scale the rhs if c._lower is not None: c._lower = c._lower * scaling_factor if c._upper is not None: c._upper = c._upper * scaling_factor if scaling_factor < 0: c._lower, c._upper = c._upper, c._lower if scale_constraint_dual and c in model.dual: dual_value = model.dual[c] if dual_value is not None: model.dual[c] = dual_value / scaling_factor c.set_value((c._lower, body, c._upper)) elif isinstance(c, _ObjectiveData): c.expr = scaling_factor * \ replace_expressions(expr=c.expr, substitution_map=variable_substitution_dict, descend_into_named_expressions=True, remove_named_expressions=True) else: raise NotImplementedError( 'Unknown object type found when applying scaling factors in ScaleModel transformation - Internal Error') model.component_scaling_factor_map = component_scaling_factor_map model.scaled_component_to_original_name_map = scaled_component_to_original_name_map return model
def pyomo4_generate_canonical_repn(exp, idMap=None, compute_values=True): # A **very** special case if TreeWalkerHelper.typeList.get(exp.__class__,0) == 4: # _LinearExpression: ans = CompiledLinearCanonicalRepn() # old format ans.constant = exp._const ans.variables = list( exp._args ) _l = exp._coef ans.linear = [_l[id(v)] for v in exp._args] if idMap: if None not in idMap: idMap[None] = {} _test = idMap[None] _key = len(idMap) - 1 for v in exp._args: if id(v) not in _test: _test[id(v)] = _key idMap[_key] = v _key += 1 return ans else: degree = exp.polynomial_degree() if degree == 1: _typeList = TreeWalkerHelper.typeList _stackMax = len(_stack) _stackIdx = 0 _stackPtr = _stack[0] _stackPtr[0] = exp try: _stackPtr[1] = exp._args except AttributeError: ans = CompiledLinearCanonicalRepn() ans.variables.append(exp) # until we can redefine CompiledLinearCanonicalRepn, restore # old format #ans.linear[id(exp)] = 1. ans.linear = [1.] return ans try: _stackPtr[2] = _type = _typeList[exp.__class__] if _stackPtr[2] == 2: _stackPtr[5].constant = 1. except KeyError: _stackPtr[2] = _type = 0 _stackPtr[3] = len(_stackPtr[1]) _stackPtr[4] = 0 #_stackPtr[5] = CompiledLinearCanonicalRepn() if _type == 4: # _LinearExpression _stackPtr[4] = _stackPtr[3] _stackPtr[5].constant = exp._const _stackPtr[5].linear = dict(exp._coef) _stackPtr[5].variables = list(exp._args) while 1: # Note: 1 is faster than True for Python 2.x if _stackPtr[4] < _stackPtr[3]: _sub = _stackPtr[1][_stackPtr[4]] _stackPtr[4] += 1 _test = _sub.__class__ in native_numeric_types if _test or not _sub.is_expression(): if not _test and _sub.is_fixed(): _sub = value(_sub) _test = 1 # True if _test: if _type == 2: _stackPtr[5].constant *= _sub _l = _stackPtr[5].linear if _l: for _id in _l: _l[_id] *= _sub elif _type == 1: _stackPtr[5].constant += _sub elif _type == 3: _stackPtr[5].constant = -1. * _sub else: raise RuntimeError("HELP") else: _id = id(_sub) if _type == 2: _lcr = _stackPtr[5] _lcr.variables.append(_sub) _lcr.linear[_id] = _lcr.constant _lcr.constant = 0 elif _type == 1: if _id in _stackPtr[5].linear: _stackPtr[5].linear[_id] += 1. else: _stackPtr[5].variables.append(_sub) _stackPtr[5].linear[_id] = 1. elif _type == 3: _lcr = _stackPtr[5] _lcr.variables.append(_sub) _lcr.linear[_id] = -1. else: raise RuntimeError("HELP") else: _stackIdx += 1 if _stackMax == _stackIdx: _stackMax += 1 _stack.append([0,0,0,0,0, CompiledLinearCanonicalRepn()]) _stackPtr = _stack[_stackIdx] _stackPtr[0] = _sub _stackPtr[1] = _sub._args #_stackPtr[2] = _type = _typeList.get(_sub.__class__, 0) #if _type == 2: # _stackPtr[5].constant = 1. try: _stackPtr[2] = _type = _typeList[_sub.__class__] if _type == 2: _stackPtr[5].constant = 1. except KeyError: _stackPtr[2] = _type = 0 _stackPtr[3] = len(_stackPtr[1]) _stackPtr[4] = 0 #_stackPtr[5] = CompiledLinearCanonicalRepn() if _type == 4: # _LinearExpression _stackPtr[4] = _stackPtr[3] _stackPtr[5].constant = _sub._const _stackPtr[5].linear = dict(_sub._coef) _stackPtr[5].variables = list(_sub._args) else: old = _stackPtr[5] if not _type: old.constant = _stackPtr[0]._apply_operation(old.variables) old.variables = [] if _stackIdx == 0: ans = CompiledLinearCanonicalRepn() ans.variables, old.variables = old.variables, ans.variables ans.linear, old.linear = old.linear, ans.linear ans.constant, old.constant = old.constant, ans.constant # until we can redefine CompiledLinearCanonicalRepn, restore # old format ans.linear = [ans.linear[id(v)] for v in ans.variables] if idMap: if None not in idMap: idMap[None] = {} _test = idMap[None] _key = len(idMap) - 1 for v in ans.variables: if id(v) not in _test: _test[id(v)] = _key idMap[_key] = v _key += 1 return ans _stackIdx -= 1 _stackPtr = _stack[_stackIdx] new = _stackPtr[5] _type = _stackPtr[2] if _type == 1: new.constant += old.constant _nl = new.linear # Note: append the variables in the order that they # were originally added to the CompiledLinearCanonicalRepn. # This keeps things deterministic. for v in old.variables: _id = id(v) if _id in _nl: _nl[_id] += old.linear[_id] else: new.variables.append(v) _nl[_id] = old.linear[_id] old.constant = 0. old.variables = [] old.linear = {} elif _type == 2: if old.variables: old.variables, new.variables = new.variables, old.variables old.linear, new.linear = new.linear, old.linear old.constant, new.constant = new.constant, old.constant _c = old.constant new.constant *= _c _nl = new.linear for _id in _nl: _nl[_id] *= _c old.constant = 0. elif _type == 3: old.variables, new.variables = new.variables, old.variables old.linear, new.linear = new.linear, old.linear new.constant = -1 * old.constant old.constant = 0. _nl = new.linear for _id in _nl: _nl[_id] *= -1 else: raise RuntimeError("HELP") elif degree == 0: if CompiledLinearCanonicalRepn_Pool: ans = CompiledLinearCanonicalRepn_Pool.pop() ans.__init__() else: ans = CompiledLinearCanonicalRepn() ans.constant = value(exp) return ans # **Py3k: degree > 1 comparision will error if degree is None elif degree and degree > 1: ans = collect_general_canonical_repn(exp, idMap, compute_values) if 1 in ans: linear_terms = {} for key, coef in iteritems(ans[1]): linear_terms[list(key.keys())[0]] = coef ans[1] = linear_terms return GeneralCanonicalRepn(ans) else: return GeneralCanonicalRepn( { None: exp, -1 : collect_variables(exp, idMap) } )
def _print_model_LP(self, model, output_file, solver_capability, labeler, output_fixed_variable_bounds=False, file_determinism=1, row_order=None, column_order=None, skip_trivial_constraints=False, force_objective_constant=False, include_all_variable_bounds=False): symbol_map = SymbolMap() variable_symbol_map = SymbolMap() # NOTE: we use createSymbol instead of getSymbol because we # know whether or not the symbol exists, and don't want # to the overhead of error/duplicate checking. # cache frequently called functions create_symbol_func = SymbolMap.createSymbol create_symbols_func = SymbolMap.createSymbols alias_symbol_func = SymbolMap.alias variable_label_pairs = [] # populate the symbol map in a single pass. #objective_list, constraint_list, sosconstraint_list, variable_list \ # = self._populate_symbol_map(model, # symbol_map, # labeler, # variable_symbol_map, # file_determinism=file_determinism) sortOrder = SortComponents.unsorted if file_determinism >= 1: sortOrder = sortOrder | SortComponents.indices if file_determinism >= 2: sortOrder = sortOrder | SortComponents.alphabetical # # Create variable symbols (and cache the block list) # all_blocks = [] variable_list = [] for block in model.block_data_objects(active=True, sort=sortOrder): all_blocks.append(block) for vardata in block.component_data_objects( Var, active=True, sort=sortOrder, descend_into=False): variable_list.append(vardata) variable_label_pairs.append( (vardata,create_symbol_func(symbol_map, vardata, labeler))) variable_symbol_map.addSymbols(variable_label_pairs) # and extract the information we'll need for rapid labeling. object_symbol_dictionary = symbol_map.byObject variable_symbol_dictionary = variable_symbol_map.byObject # cache - these are called all the time. print_expr_canonical = self._print_expr_canonical # print the model name and the source, so we know roughly where # it came from. # # NOTE: this *must* use the "\* ... *\" comment format: the GLPK # LP parser does not correctly handle other formats (notably, "%"). output_file.write( "\\* Source Pyomo model name=%s *\\\n\n" % (model.name,) ) # # Objective # supports_quadratic_objective = \ solver_capability('quadratic_objective') numObj = 0 onames = [] for block in all_blocks: gen_obj_canonical_repn = \ getattr(block, "_gen_obj_canonical_repn", True) # Get/Create the ComponentMap for the repn if not hasattr(block,'_canonical_repn'): block._canonical_repn = ComponentMap() block_canonical_repn = block._canonical_repn for objective_data in block.component_data_objects( Objective, active=True, sort=sortOrder, descend_into=False): numObj += 1 onames.append(objective_data.name) if numObj > 1: raise ValueError( "More than one active objective defined for input " "model '%s'; Cannot write legal LP file\n" "Objectives: %s" % (model.name, ' '.join(onames))) create_symbol_func(symbol_map, objective_data, labeler) symbol_map.alias(objective_data, '__default_objective__') if objective_data.is_minimizing(): output_file.write("min \n") else: output_file.write("max \n") if gen_obj_canonical_repn: canonical_repn = \ generate_canonical_repn(objective_data.expr) block_canonical_repn[objective_data] = canonical_repn else: canonical_repn = block_canonical_repn[objective_data] degree = canonical_degree(canonical_repn) if degree == 0: logger.warning("Constant objective detected, replacing " "with a placeholder to prevent solver failure.") force_objective_constant = True elif degree == 2: if not supports_quadratic_objective: raise RuntimeError( "Selected solver is unable to handle " "objective functions with quadratic terms. " "Objective at issue: %s." % objective_data.name) elif degree != 1: raise RuntimeError( "Cannot write legal LP file. Objective '%s' " "has nonlinear terms that are not quadratic." % objective_data.name) output_file.write( object_symbol_dictionary[id(objective_data)]+':\n') offset = print_expr_canonical( canonical_repn, output_file, object_symbol_dictionary, variable_symbol_dictionary, True, column_order, force_objective_constant=force_objective_constant) if numObj == 0: raise ValueError( "ERROR: No objectives defined for input model '%s'; " " cannot write legal LP file" % str(model.name)) # Constraints # # If there are no non-trivial constraints, you'll end up with an empty # constraint block. CPLEX is OK with this, but GLPK isn't. And # eliminating the constraint block (i.e., the "s.t." line) causes GLPK # to whine elsewhere. Output a warning if the constraint block is empty, # so users can quickly determine the cause of the solve failure. output_file.write("\n") output_file.write("s.t.\n") output_file.write("\n") have_nontrivial = False supports_quadratic_constraint = solver_capability('quadratic_constraint') def constraint_generator(): for block in all_blocks: gen_con_canonical_repn = \ getattr(block, "_gen_con_canonical_repn", True) # Get/Create the ComponentMap for the repn if not hasattr(block,'_canonical_repn'): block._canonical_repn = ComponentMap() block_canonical_repn = block._canonical_repn for constraint_data in block.component_data_objects( Constraint, active=True, sort=sortOrder, descend_into=False): if isinstance(constraint_data, LinearCanonicalRepn): canonical_repn = constraint_data else: if gen_con_canonical_repn: canonical_repn = generate_canonical_repn(constraint_data.body) block_canonical_repn[constraint_data] = canonical_repn else: canonical_repn = block_canonical_repn[constraint_data] yield constraint_data, canonical_repn if row_order is not None: sorted_constraint_list = list(constraint_generator()) sorted_constraint_list.sort(key=lambda x: row_order[x[0]]) def yield_all_constraints(): for constraint_data, canonical_repn in sorted_constraint_list: yield constraint_data, canonical_repn else: yield_all_constraints = constraint_generator # FIXME: This is a hack to get nested blocks working... eq_string_template = "= %"+self._precision_string+'\n' geq_string_template = ">= %"+self._precision_string+'\n\n' leq_string_template = "<= %"+self._precision_string+'\n\n' for constraint_data, canonical_repn in yield_all_constraints(): have_nontrivial = True degree = canonical_degree(canonical_repn) # # Write constraint # # There are conditions, e.g., when fixing variables, under which # a constraint block might be empty. Ignore these, for both # practical reasons and the fact that the CPLEX LP format # requires a variable in the constraint body. It is also # possible that the body of the constraint consists of only a # constant, in which case the "variable" of if degree == 0: if skip_trivial_constraints: continue elif degree == 2: if not supports_quadratic_constraint: raise ValueError( "Solver unable to handle quadratic expressions. Constraint" " at issue: '%s'" % (constraint_data.name)) elif degree != 1: raise ValueError( "Cannot write legal LP file. Constraint '%s' has a body " "with nonlinear terms." % (constraint_data.name)) # Create symbol con_symbol = create_symbol_func(symbol_map, constraint_data, labeler) if constraint_data.equality: label = 'c_e_' + con_symbol + '_' alias_symbol_func(symbol_map, constraint_data, label) output_file.write(label+':\n') offset = print_expr_canonical(canonical_repn, output_file, object_symbol_dictionary, variable_symbol_dictionary, False, column_order) bound = constraint_data.lower bound = self._get_bound(bound) - offset output_file.write(eq_string_template % (_no_negative_zero(bound))) output_file.write("\n") else: if constraint_data.lower is not None: if constraint_data.upper is not None: label = 'r_l_' + con_symbol + '_' else: label = 'c_l_' + con_symbol + '_' alias_symbol_func(symbol_map, constraint_data, label) output_file.write(label+':\n') offset = print_expr_canonical(canonical_repn, output_file, object_symbol_dictionary, variable_symbol_dictionary, False, column_order) bound = constraint_data.lower bound = self._get_bound(bound) - offset output_file.write(geq_string_template % (_no_negative_zero(bound))) if constraint_data.upper is not None: if constraint_data.lower is not None: label = 'r_u_' + con_symbol + '_' else: label = 'c_u_' + con_symbol + '_' alias_symbol_func(symbol_map, constraint_data, label) output_file.write(label+':\n') offset = print_expr_canonical(canonical_repn, output_file, object_symbol_dictionary, variable_symbol_dictionary, False, column_order) bound = constraint_data.upper bound = self._get_bound(bound) - offset output_file.write(leq_string_template % (_no_negative_zero(bound))) if not have_nontrivial: logger.warning('Empty constraint block written in LP format ' \ '- solver may error') # the CPLEX LP format doesn't allow constants in the objective (or # constraint body), which is a bit silly. To avoid painful # book-keeping, we introduce the following "variable", constrained # to the value 1. This is used when quadratic terms are present. # worst-case, if not used, is that CPLEX easily pre-processes it out. prefix = "" output_file.write('%sc_e_ONE_VAR_CONSTANT: \n' % prefix) output_file.write('%sONE_VAR_CONSTANT = 1.0\n' % prefix) output_file.write("\n") # SOS constraints # # For now, we write out SOS1 and SOS2 constraints in the cplex format # # All Component objects are stored in model._component, which is a # dictionary of {class: {objName: object}}. # # Consider the variable X, # # model.X = Var(...) # # We print X to CPLEX format as X(i,j,k,...) where i, j, k, ... are the # indices of X. # SOSlines = StringIO() sos1 = solver_capability("sos1") sos2 = solver_capability("sos2") writtenSOS = False for block in all_blocks: for soscondata in block.component_data_objects( SOSConstraint, active=True, sort=sortOrder, descend_into=False): create_symbol_func(symbol_map, soscondata, labeler) level = soscondata.level if (level == 1 and not sos1) or \ (level == 2 and not sos2) or \ (level > 2): raise ValueError( "Solver does not support SOS level %s constraints" % (level)) if writtenSOS == False: SOSlines.write("SOS\n") writtenSOS = True # This updates the referenced_variable_ids, just in case # there is a variable that only appears in an # SOSConstraint, in which case this needs to be known # before we write the "bounds" section (Cplex does not # handle this correctly, Gurobi does) self.printSOS(symbol_map, labeler, variable_symbol_map, soscondata, SOSlines) # # Bounds # output_file.write("bounds\n") # Scan all variables even if we're only writing a subset of them. # required because we don't store maps by variable type currently. # FIXME: This is a hack to get nested blocks working... lb_string_template = "%"+self._precision_string+" <= " ub_string_template = " <= %"+self._precision_string+"\n" # Track the number of integer and binary variables, so you can # output their status later. integer_vars = [] binary_vars = [] for vardata in variable_list: # TODO: We could just loop over the set of items in # self._referenced_variable_ids, except this is # a dictionary that is hashed by id(vardata) # which would make the bounds section # nondeterministic (bad for unit testing) if (not include_all_variable_bounds) and \ (id(vardata) not in self._referenced_variable_ids): continue if vardata.fixed: if not output_fixed_variable_bounds: raise ValueError( "Encountered a fixed variable (%s) inside an active " "objective or constraint expression on model %s, which is " "usually indicative of a preprocessing error. Use the " "IO-option 'output_fixed_variable_bounds=True' to suppress " "this error and fix the variable by overwriting its bounds " "in the LP file." % (vardata.name, model.name)) if vardata.value is None: raise ValueError("Variable cannot be fixed to a value of None.") vardata_lb = value(vardata.value) vardata_ub = value(vardata.value) else: vardata_lb = self._get_bound(vardata.lb) vardata_ub = self._get_bound(vardata.ub) name_to_output = variable_symbol_dictionary[id(vardata)] # track the number of integer and binary variables, so we know whether # to output the general / binary sections below. if vardata.is_integer(): integer_vars.append(name_to_output) elif vardata.is_binary(): binary_vars.append(name_to_output) elif not vardata.is_continuous(): raise TypeError("Invalid domain type for variable with name '%s'. " "Variable is not continuous, integer, or binary." % (vardata.name)) # in the CPLEX LP file format, the default variable # bounds are 0 and +inf. These bounds are in # conflict with Pyomo, which assumes -inf and +inf # (which we would argue is more rational). output_file.write(" ") if (vardata_lb is not None) and (vardata_lb != -infinity): output_file.write(lb_string_template % (_no_negative_zero(vardata_lb))) else: output_file.write(" -inf <= ") if name_to_output == "e": raise ValueError( "Attempting to write variable with name 'e' in a CPLEX LP " "formatted file will cause a parse failure due to confusion with " "numeric values expressed in scientific notation") output_file.write(name_to_output) if (vardata_ub is not None) and (vardata_ub != infinity): output_file.write(ub_string_template % (_no_negative_zero(vardata_ub))) else: output_file.write(" <= +inf\n") if len(integer_vars) > 0: output_file.write("general\n") for var_name in integer_vars: output_file.write(' %s\n' % var_name) if len(binary_vars) > 0: output_file.write("binary\n") for var_name in binary_vars: output_file.write(' %s\n' % var_name) # Write the SOS section output_file.write(SOSlines.getvalue()) # # wrap-up # output_file.write("end\n") # Clean up the symbol map to only contain variables referenced # in the active constraints **Note**: warm start method may # rely on this for choosing the set of potential warm start # variables vars_to_delete = set(variable_symbol_map.byObject.keys()) - \ set(self._referenced_variable_ids.keys()) sm_byObject = symbol_map.byObject sm_bySymbol = symbol_map.bySymbol var_sm_byObject = variable_symbol_map.byObject for varid in vars_to_delete: symbol = var_sm_byObject[varid] del sm_byObject[varid] del sm_bySymbol[symbol] del variable_symbol_map return symbol_map
def _print_model_MPS(self, model, output_file, solver_capability, labeler, output_fixed_variable_bounds=False, file_determinism=1, row_order=None, column_order=None, skip_trivial_constraints=False, force_objective_constant=False, include_all_variable_bounds=False, skip_objective_sense=False): symbol_map = SymbolMap() variable_symbol_map = SymbolMap() # NOTE: we use createSymbol instead of getSymbol because we # know whether or not the symbol exists, and don't want # to the overhead of error/duplicate checking. # cache frequently called functions extract_variable_coefficients = self._extract_variable_coefficients create_symbol_func = SymbolMap.createSymbol create_symbols_func = SymbolMap.createSymbols alias_symbol_func = SymbolMap.alias variable_label_pairs = [] sortOrder = SortComponents.unsorted if file_determinism >= 1: sortOrder = sortOrder | SortComponents.indices if file_determinism >= 2: sortOrder = sortOrder | SortComponents.alphabetical # # Create variable symbols (and cache the block list) # all_blocks = [] variable_list = [] for block in model.block_data_objects(active=True, sort=sortOrder): all_blocks.append(block) for vardata in block.component_data_objects( Var, active=True, sort=sortOrder, descend_into=False): variable_list.append(vardata) variable_label_pairs.append( (vardata,create_symbol_func(symbol_map, vardata, labeler))) variable_symbol_map.addSymbols(variable_label_pairs) # and extract the information we'll need for rapid labeling. object_symbol_dictionary = symbol_map.byObject variable_symbol_dictionary = variable_symbol_map.byObject # sort the variable ordering by the user # column_order ComponentMap if column_order is not None: variable_list.sort(key=lambda _x: column_order[_x]) # prepare to hold the sparse columns variable_to_column = ComponentMap( (vardata, i) for i, vardata in enumerate(variable_list)) # add one position for ONE_VAR_CONSTANT column_data = [[] for i in xrange(len(variable_list)+1)] quadobj_data = [] quadmatrix_data = [] # constraint rhs rhs_data = [] # print the model name and the source, so we know # roughly where output_file.write("* Source: Pyomo MPS Writer\n") output_file.write("* Format: Free MPS\n") output_file.write("*\n") output_file.write("NAME %s\n" % (model.name,)) # # ROWS section # objective_label = None numObj = 0 onames = [] for block in all_blocks: gen_obj_canonical_repn = \ getattr(block, "_gen_obj_canonical_repn", True) # Get/Create the ComponentMap for the repn if not hasattr(block,'_canonical_repn'): block._canonical_repn = ComponentMap() block_canonical_repn = block._canonical_repn for objective_data in block.component_data_objects( Objective, active=True, sort=sortOrder, descend_into=False): numObj += 1 onames.append(objective_data.cname()) if numObj > 1: raise ValueError( "More than one active objective defined for input " "model '%s'; Cannot write legal MPS file\n" "Objectives: %s" % (model.cname(True), ' '.join(onames))) objective_label = create_symbol_func(symbol_map, objective_data, labeler) symbol_map.alias(objective_data, '__default_objective__') if not skip_objective_sense: output_file.write("OBJSENSE\n") if objective_data.is_minimizing(): output_file.write(" MIN\n") else: output_file.write(" MAX\n") # This section is not recognized by the COIN-OR # MPS reader #output_file.write("OBJNAME\n") #output_file.write(" %s\n" % (objective_label)) output_file.write("ROWS\n") output_file.write(" N %s\n" % (objective_label)) if gen_obj_canonical_repn: canonical_repn = \ generate_canonical_repn(objective_data.expr) block_canonical_repn[objective_data] = canonical_repn else: canonical_repn = block_canonical_repn[objective_data] degree = canonical_degree(canonical_repn) if degree == 0: print("Warning: Constant objective detected, replacing " "with a placeholder to prevent solver failure.") force_objective_constant = True elif (degree != 1) and (degree != 2): raise RuntimeError( "Cannot write legal MPS file. Objective '%s' " "has nonlinear terms that are not quadratic." % objective_data.cname(True)) constant = extract_variable_coefficients( objective_label, canonical_repn, column_data, quadobj_data, variable_to_column) if force_objective_constant or (constant != 0.0): # ONE_VAR_CONSTANT column_data[-1].append((objective_label, constant)) if numObj == 0: raise ValueError( "Cannot write legal MPS file: No objective defined " "for input model '%s'." % str(model)) assert objective_label is not None # Constraints def constraint_generator(): for block in all_blocks: gen_con_canonical_repn = \ getattr(block, "_gen_con_canonical_repn", True) # Get/Create the ComponentMap for the repn if not hasattr(block,'_canonical_repn'): block._canonical_repn = ComponentMap() block_canonical_repn = block._canonical_repn for constraint_data in block.component_data_objects( Constraint, active=True, sort=sortOrder, descend_into=False): if isinstance(constraint_data, LinearCanonicalRepn): canonical_repn = constraint_data else: if gen_con_canonical_repn: canonical_repn = generate_canonical_repn( constraint_data.body) block_canonical_repn[constraint_data] = canonical_repn else: canonical_repn = block_canonical_repn[constraint_data] yield constraint_data, canonical_repn if row_order is not None: sorted_constraint_list = list(constraint_generator()) sorted_constraint_list.sort(key=lambda x: row_order[x[0]]) def yield_all_constraints(): for constraint_data, canonical_repn in sorted_constraint_list: yield constraint_data, canonical_repn else: yield_all_constraints = constraint_generator for constraint_data, canonical_repn in yield_all_constraints(): degree = canonical_degree(canonical_repn) # Write constraint if degree == 0: if skip_trivial_constraints: continue elif (degree != 1) and (degree != 2): raise RuntimeError( "Cannot write legal MPS file. Constraint '%s' " "has nonlinear terms that are not quadratic." % constraint_data.cname(True)) # Create symbol con_symbol = create_symbol_func(symbol_map, constraint_data, labeler) if constraint_data.equality: label = 'c_e_' + con_symbol + '_' alias_symbol_func(symbol_map, constraint_data, label) output_file.write(" E %s\n" % (label)) offset = extract_variable_coefficients( label, canonical_repn, column_data, quadmatrix_data, variable_to_column) bound = constraint_data.lower bound = self._get_bound(bound) - offset rhs_data.append((label, bound)) else: if constraint_data.lower is not None: if constraint_data.upper is not None: label = 'r_l_' + con_symbol + '_' else: label = 'c_l_' + con_symbol + '_' alias_symbol_func(symbol_map, constraint_data, label) output_file.write(" G %s\n" % (label)) offset = extract_variable_coefficients( label, canonical_repn, column_data, quadmatrix_data, variable_to_column) bound = constraint_data.lower bound = self._get_bound(bound) - offset rhs_data.append((label, bound)) if constraint_data.upper is not None: if constraint_data.lower is not None: label = 'r_u_' + con_symbol + '_' else: label = 'c_u_' + con_symbol + '_' alias_symbol_func(symbol_map, constraint_data, label) output_file.write(" L %s\n" % (label)) offset = extract_variable_coefficients( label, canonical_repn, column_data, quadmatrix_data, variable_to_column) bound = constraint_data.upper bound = self._get_bound(bound) - offset rhs_data.append((label, bound)) if len(column_data[-1]) > 0: # ONE_VAR_CONSTANT = 1 output_file.write(" E c_e_ONE_VAR_CONSTANT\n") column_data[-1].append(("c_e_ONE_VAR_CONSTANT",1)) rhs_data.append(("c_e_ONE_VAR_CONSTANT",1)) # # COLUMNS section # column_template = " %s %s %"+self._precision_string+"\n" output_file.write("COLUMNS\n") cnt = 0 for vardata in variable_list: col_entries = column_data[variable_to_column[vardata]] cnt += 1 if len(col_entries) > 0: var_label = variable_symbol_dictionary[id(vardata)] for i, (row_label, coef) in enumerate(col_entries): output_file.write(column_template % (var_label, row_label, coef)) elif include_all_variable_bounds: # the column is empty, so add a (0 * var) # term to the objective # * Note that some solvers (e.g., Gurobi) # will accept an empty column as a line # with just the column name. This doesn't # seem to work for CPLEX 12.6, so I am # doing it this way so that it will work for both var_label = variable_symbol_dictionary[id(vardata)] output_file.write(column_template % (var_label, objective_label, 0)) assert cnt == len(column_data)-1 if len(column_data[-1]) > 0: col_entries = column_data[-1] var_label = "ONE_VAR_CONSTANT" for i, (row_label, coef) in enumerate(col_entries): output_file.write(column_template % (var_label, row_label, coef)) # # RHS section # rhs_template = " RHS %s %"+self._precision_string+"\n" output_file.write("RHS\n") for i, (row_label, rhs) in enumerate(rhs_data): output_file.write(rhs_template % (row_label, rhs)) # SOS constraints SOSlines = StringIO() sos1 = solver_capability("sos1") sos2 = solver_capability("sos2") for block in all_blocks: for soscondata in block.component_data_objects( SOSConstraint, active=True, sort=sortOrder, descend_into=False): create_symbol_func(symbol_map, soscondata, labeler) level = soscondata.level if (level == 1 and not sos1) or \ (level == 2 and not sos2) or \ (level > 2): raise ValueError( "Solver does not support SOS level %s constraints" % (level)) # This updates the referenced_variable_ids, just in case # there is a variable that only appears in an # SOSConstraint, in which case this needs to be known # before we write the "bounds" section (Cplex does not # handle this correctly, Gurobi does) self._printSOS(symbol_map, labeler, variable_symbol_map, soscondata, SOSlines) # # BOUNDS section # entry_template = "%s %"+self._precision_string+"\n" output_file.write("BOUNDS\n") for vardata in variable_list: if include_all_variable_bounds or \ (id(vardata) in self._referenced_variable_ids): var_label = variable_symbol_dictionary[id(vardata)] if vardata.fixed: if not output_fixed_variable_bounds: raise ValueError( "Encountered a fixed variable (%s) inside an active " "objective or constraint expression on model %s, which is " "usually indicative of a preprocessing error. Use the " "IO-option 'output_fixed_variable_bounds=True' to suppress " "this error and fix the variable by overwriting its bounds " "in the MPS file." % (vardata.cname(True), model.cname(True))) if vardata.value is None: raise ValueError("Variable cannot be fixed to a value of None.") output_file.write((" FX BOUND "+entry_template) % (var_label, value(vardata.value))) continue vardata_lb = self._get_bound(vardata.lb) vardata_ub = self._get_bound(vardata.ub) # Make it harder for -0 to show up in # the output. This makes file diffing # for test baselines slightly less # annoying if vardata_lb == 0: vardata_lb = 0 if vardata_ub == 0: vardata_ub = 0 unbounded_lb = (vardata_lb is None) or (vardata_lb == -infinity) unbounded_ub = (vardata_ub is None) or (vardata_ub == infinity) treat_as_integer = False if vardata.is_binary(): if (vardata_lb == 0) and (vardata_ub == 1): output_file.write(" BV BOUND %s\n" % (var_label)) continue else: # so we can add bounds treat_as_integer = True if treat_as_integer or vardata.is_integer(): # Indicating unbounded integers is tricky because # the only way to indicate a variable is integer # is using the bounds section. Thus, we signify # infinity with a large number (10E20) # * Note: Gurobi allows values like inf and -inf # but CPLEX 12.6 does not, so I am just # using a large value if not unbounded_lb: output_file.write((" LI BOUND "+entry_template) % (var_label, vardata_lb)) else: output_file.write(" LI BOUND %s -10E20\n" % (var_label)) if not unbounded_ub: output_file.write((" UI BOUND "+entry_template) % (var_label, vardata_ub)) else: output_file.write(" UI BOUND %s 10E20\n" % (var_label)) else: assert vardata.is_continuous() if unbounded_lb and unbounded_ub: output_file.write(" FR BOUND %s\n" % (var_label)) else: if not unbounded_lb: output_file.write((" LO BOUND "+entry_template) % (var_label, vardata_lb)) else: output_file.write(" MI BOUND %s\n" % (var_label)) if not unbounded_ub: output_file.write((" UP BOUND "+entry_template) % (var_label, vardata_ub)) # # SOS section # output_file.write(SOSlines.getvalue()) # Formatting of the next two sections comes from looking # at Gurobi and Cplex output # # QUADOBJ section # if len(quadobj_data) > 0: assert len(quadobj_data) == 1 # it looks like the COIN-OR MPS Reader only # recognizes QUADOBJ (Gurobi and Cplex seem to # be okay with this) output_file.write("QUADOBJ\n") #output_file.write("QMATRIX\n") label, quad_terms = quadobj_data[0] assert label == objective_label for (var1, var2), coef in sorted(quad_terms, key=lambda _x: (variable_to_column[_x[0][0]], variable_to_column[_x[0][1]])): var1_label = variable_symbol_dictionary[id(var1)] var2_label = variable_symbol_dictionary[id(var2)] # Don't forget that a quadratic objective is always # assumed to be divided by 2 if var1_label == var2_label: output_file.write(column_template % (var1_label, var2_label, coef * 2)) else: # the matrix needs to be symmetric so split # the coefficient (but remember it is divided by 2) output_file.write(column_template % (var1_label, var2_label, coef)) output_file.write(column_template % (var2_label, var1_label, coef)) # # QCMATRIX section # if len(quadmatrix_data) > 0: for row_label, quad_terms in quadmatrix_data: output_file.write("QCMATRIX %s\n" % (row_label)) for (var1, var2), coef in sorted(quad_terms, key=lambda _x: (variable_to_column[_x[0][0]], variable_to_column[_x[0][1]])): var1_label = variable_symbol_dictionary[id(var1)] var2_label = variable_symbol_dictionary[id(var2)] if var1_label == var2_label: output_file.write(column_template % (var1_label, var2_label, coef)) else: # the matrix needs to be symmetric so split # the coefficient output_file.write(column_template % (var1_label, var2_label, coef * 0.5)) output_file.write(column_template % (var2_label, var1_label, coef * 0.5)) output_file.write("ENDATA\n") # Clean up the symbol map to only contain variables referenced # in the active constraints **Note**: warm start method may # rely on this for choosing the set of potential warm start # variables vars_to_delete = set(variable_symbol_map.byObject.keys()) - \ set(self._referenced_variable_ids.keys()) sm_byObject = symbol_map.byObject sm_bySymbol = symbol_map.bySymbol var_sm_byObject = variable_symbol_map.byObject for varid in vars_to_delete: symbol = var_sm_byObject[varid] del sm_byObject[varid] del sm_bySymbol[symbol] del variable_symbol_map return symbol_map
def _collect_linear_const(exp, idMap, multiplier, coef, varmap, compute_values): if compute_values: coef[None] += multiplier * value(exp) else: coef[None] += multiplier * exp
def _get_bound(self, exp): if exp is None: return None if is_fixed(exp): return value(exp) raise ValueError("non-fixed bound: " + str(exp))
def _populate_gurobi_instance (self, pyomo_instance): from pyomo.core.base import Var, Objective, Constraint, SOSConstraint from pyomo.repn import LinearCanonicalRepn, canonical_degree try: grbmodel = Model(name=pyomo_instance.name) except Exception: e = sys.exc_info()[1] msg = 'Unable to create Gurobi model. Have you installed the Python'\ '\n bindings for Gurobi?\n\n\tError message: %s' raise Exception(msg % e) if self._symbolic_solver_labels: labeler = TextLabeler() else: labeler = NumericLabeler('x') # cache to avoid dictionary getitem calls in the loops below. self_symbol_map = self._symbol_map = SymbolMap() pyomo_instance.solutions.add_symbol_map(self_symbol_map) self._smap_id = id(self_symbol_map) # we use this when iterating over the constraints because it # will have a much smaller hash table, we also use this for # the warm start code after it is cleaned to only contain # variables referenced in the constraints self_variable_symbol_map = self._variable_symbol_map = SymbolMap() var_symbol_pairs = [] # maps _VarData labels to the corresponding Gurobi variable object pyomo_gurobi_variable_map = {} self._referenced_variable_ids.clear() # cache to avoid dictionary getitem calls in the loop below. grb_infinity = GRB.INFINITY for var_value in pyomo_instance.component_data_objects(Var, active=True): lb = -grb_infinity ub = grb_infinity if (var_value.lb is not None) and (var_value.lb != -infinity): lb = value(var_value.lb) if (var_value.ub is not None) and (var_value.ub != infinity): ub = value(var_value.ub) # _VarValue objects will not be in the symbol map yet, so # avoid some checks. var_value_label = self_symbol_map.createSymbol(var_value, labeler) var_symbol_pairs.append((var_value, var_value_label)) # be sure to impart the integer and binary nature of any variables if var_value.is_integer(): var_type = GRB.INTEGER elif var_value.is_binary(): var_type = GRB.BINARY elif var_value.is_continuous(): var_type = GRB.CONTINUOUS else: raise TypeError("Invalid domain type for variable with name '%s'. " "Variable is not continuous, integer, or binary.") pyomo_gurobi_variable_map[var_value_label] = \ grbmodel.addVar(lb=lb, \ ub=ub, \ vtype=var_type, \ name=var_value_label) self_variable_symbol_map.addSymbols(var_symbol_pairs) grbmodel.update() # The next loop collects the following component types from the model: # - SOSConstraint # - Objective # - Constraint sos1 = self._capabilities.sos1 sos2 = self._capabilities.sos2 modelSOS = ModelSOS() objective_cntr = 0 # Track the range constraints and their associated variables added by gurobi self._last_native_var_idx = grbmodel.NumVars-1 range_var_idx = grbmodel.NumVars _self_range_con_var_pairs = self._range_con_var_pairs = [] for block in pyomo_instance.block_data_objects(active=True): gen_obj_canonical_repn = \ getattr(block, "_gen_obj_canonical_repn", True) gen_con_canonical_repn = \ getattr(block, "_gen_con_canonical_repn", True) # Get/Create the ComponentMap for the repn if not hasattr(block,'_canonical_repn'): block._canonical_repn = ComponentMap() block_canonical_repn = block._canonical_repn # SOSConstraints for soscondata in block.component_data_objects(SOSConstraint, active=True, descend_into=False): level = soscondata.level if (level == 1 and not sos1) or \ (level == 2 and not sos2) or \ (level > 2): raise RuntimeError( "Solver does not support SOS level %s constraints" % (level,)) modelSOS.count_constraint(self_symbol_map, labeler, self_variable_symbol_map, pyomo_gurobi_variable_map, soscondata) # Objective for obj_data in block.component_data_objects(Objective, active=True, descend_into=False): if objective_cntr > 1: raise ValueError( "Multiple active objectives found on Pyomo instance '%s'. " "Solver '%s' will only handle a single active objective" \ % (pyomo_instance.cname(True), self.type)) sense = GRB_MIN if (obj_data.is_minimizing()) else GRB_MAX grbmodel.ModelSense = sense obj_expr = LinExpr() if gen_obj_canonical_repn: obj_repn = generate_canonical_repn(obj_data.expr) block_canonical_repn[obj_data] = obj_repn else: obj_repn = block_canonical_repn[obj_data] if isinstance(obj_repn, LinearCanonicalRepn): if obj_repn.constant != None: obj_expr.addConstant(obj_repn.constant) if obj_repn.linear != None: for i in xrange(len(obj_repn.linear)): var_coefficient = obj_repn.linear[i] var_value = obj_repn.variables[i] self._referenced_variable_ids.add(id(var_value)) label = self_variable_symbol_map.getSymbol(var_value) obj_expr.addTerms(var_coefficient, pyomo_gurobi_variable_map[label]) else: if 0 in obj_repn: # constant term obj_expr.addConstant(obj_repn[0][None]) if 1 in obj_repn: # first-order terms hash_to_variable_map = obj_repn[-1] for var_hash, var_coefficient in iteritems(obj_repn[1]): vardata = hash_to_variable_map[var_hash] self._referenced_variable_ids.add(id(vardata)) label = self_variable_symbol_map.getSymbol(vardata) obj_expr.addTerms(var_coefficient, pyomo_gurobi_variable_map[label]) if 2 in obj_repn: obj_expr = QuadExpr(obj_expr) hash_to_variable_map = obj_repn[-1] for quad_repn, coef in iteritems(obj_repn[2]): gurobi_expr = QuadExpr(coef) for var_hash, exponent in iteritems(quad_repn): vardata = hash_to_variable_map[var_hash] self._referenced_variable_ids.add(id(vardata)) gurobi_var = pyomo_gurobi_variable_map\ [self_variable_symbol_map.\ getSymbol(vardata)] gurobi_expr *= gurobi_var if exponent == 2: gurobi_expr *= gurobi_var obj_expr += gurobi_expr degree = canonical_degree(obj_repn) if (degree is None) or (degree > 2): raise ValueError( "gurobi_direct plugin does not support general nonlinear " "objective expressions (only linear or quadratic).\n" "Objective: %s" % (obj_data.cname(True))) # need to cache the objective label, because the # GUROBI python interface doesn't track this. # _ObjectiveData objects will not be in the symbol map # yet, so avoid some checks. self._objective_label = \ self_symbol_map.createSymbol(obj_data, labeler) grbmodel.setObjective(obj_expr, sense=sense) # Constraint for constraint_data in block.component_data_objects(Constraint, active=True, descend_into=False): if (constraint_data.lower is None) and \ (constraint_data.upper is None): continue # not binding at all, don't bother con_repn = None if isinstance(constraint_data, LinearCanonicalRepn): con_repn = constraint_data else: if gen_con_canonical_repn: con_repn = generate_canonical_repn(constraint_data.body) block_canonical_repn[constraint_data] = con_repn else: con_repn = block_canonical_repn[constraint_data] offset = 0.0 # _ConstraintData objects will not be in the symbol # map yet, so avoid some checks. constraint_label = \ self_symbol_map.createSymbol(constraint_data, labeler) trivial = False if isinstance(con_repn, LinearCanonicalRepn): # # optimization (these might be generated on the fly) # constant = con_repn.constant coefficients = con_repn.linear variables = con_repn.variables if constant is not None: offset = constant expr = LinExpr() + offset if coefficients is not None: linear_coefs = list() linear_vars = list() for i in xrange(len(coefficients)): var_coefficient = coefficients[i] var_value = variables[i] self._referenced_variable_ids.add(id(var_value)) label = self_variable_symbol_map.getSymbol(var_value) linear_coefs.append(var_coefficient) linear_vars.append(pyomo_gurobi_variable_map[label]) expr += LinExpr(linear_coefs, linear_vars) else: trivial = True else: if 0 in con_repn: offset = con_repn[0][None] expr = LinExpr() + offset if 1 in con_repn: # first-order terms linear_coefs = list() linear_vars = list() hash_to_variable_map = con_repn[-1] for var_hash, var_coefficient in iteritems(con_repn[1]): var = hash_to_variable_map[var_hash] self._referenced_variable_ids.add(id(var)) label = self_variable_symbol_map.getSymbol(var) linear_coefs.append( var_coefficient ) linear_vars.append( pyomo_gurobi_variable_map[label] ) expr += LinExpr(linear_coefs, linear_vars) if 2 in con_repn: # quadratic constraint if _GUROBI_VERSION_MAJOR < 5: raise ValueError( "The gurobi_direct plugin does not handle quadratic " "constraint expressions for Gurobi major versions " "< 5. Current version: Gurobi %s.%s%s" % (gurobi.version())) expr = QuadExpr(expr) hash_to_variable_map = con_repn[-1] for quad_repn, coef in iteritems(con_repn[2]): gurobi_expr = QuadExpr(coef) for var_hash, exponent in iteritems(quad_repn): vardata = hash_to_variable_map[var_hash] self._referenced_variable_ids.add(id(vardata)) gurobi_var = pyomo_gurobi_variable_map\ [self_variable_symbol_map.\ getSymbol(vardata)] gurobi_expr *= gurobi_var if exponent == 2: gurobi_expr *= gurobi_var expr += gurobi_expr degree = canonical_degree(con_repn) if (degree is None) or (degree > 2): raise ValueError( "gurobi_direct plugin does not support general nonlinear " "constraint expressions (only linear or quadratic).\n" "Constraint: %s" % (constraint_data.cname(True))) if (not trivial) or (not self._skip_trivial_constraints): if constraint_data.equality: sense = GRB.EQUAL bound = self._get_bound(constraint_data.lower) grbmodel.addConstr(lhs=expr, sense=sense, rhs=bound, name=constraint_label) else: # L <= body <= U if (constraint_data.upper is not None) and \ (constraint_data.lower is not None): grb_con = grbmodel.addRange( expr, self._get_bound(constraint_data.lower), self._get_bound(constraint_data.upper), constraint_label) _self_range_con_var_pairs.append((grb_con,range_var_idx)) range_var_idx += 1 # body <= U elif constraint_data.upper is not None: bound = self._get_bound(constraint_data.upper) if bound < float('inf'): grbmodel.addConstr( lhs=expr, sense=GRB.LESS_EQUAL, rhs=bound, name=constraint_label ) # L <= body else: bound = self._get_bound(constraint_data.lower) if bound > -float('inf'): grbmodel.addConstr( lhs=expr, sense=GRB.GREATER_EQUAL, rhs=bound, name=constraint_label ) if modelSOS.sosType: for key in modelSOS.sosType: grbmodel.addSOS(modelSOS.sosType[key], \ modelSOS.varnames[key], \ modelSOS.weights[key] ) self._referenced_variable_ids.update(modelSOS.varids[key]) for var_id in self._referenced_variable_ids: varname = self._variable_symbol_map.byObject[var_id] vardata = self._variable_symbol_map.bySymbol[varname]() if vardata.fixed: if not self._output_fixed_variable_bounds: raise ValueError("Encountered a fixed variable (%s) inside an active objective " "or constraint expression on model %s, which is usually indicative of " "a preprocessing error. Use the IO-option 'output_fixed_variable_bounds=True' " "to suppress this error and fix the variable by overwriting its bounds in " "the Gurobi instance." % (vardata.cname(True),pyomo_instance.cname(True),)) grbvar = pyomo_gurobi_variable_map[varname] grbvar.setAttr(GRB.Attr.UB, vardata.value) grbvar.setAttr(GRB.Attr.LB, vardata.value) grbmodel.update() self._gurobi_instance = grbmodel self._pyomo_gurobi_variable_map = pyomo_gurobi_variable_map
def to_common_form(self, cdata, free_vars): """ Convert a common form that can processed by AMPL """ _e1 = cdata._canonical_expression(cdata._args[0]) _e2 = cdata._canonical_expression(cdata._args[1]) if False: # pragma:nocover if _e1[0] is None: print(None) else: print(str(_e1[0])) if _e1[1] is None: print(None) else: print(str(_e1[1])) if len(_e1) > 2: if _e1[2] is None: print(None) else: print(str(_e1[2])) if _e2[0] is None: print(None) else: print(str(_e2[0])) if _e2[1] is None: print(None) else: print(str(_e2[1])) if len(_e2) > 2: if _e2[2] is None: print(None) else: print(str(_e2[2])) if len(_e1) == 2: cdata.c = Constraint(expr=_e1) return if len(_e2) == 2: cdata.c = Constraint(expr=_e2) return if (_e1[0] is None) + (_e1[2] is None) + (_e2[0] is None) + (_e2[2] is None) != 2: raise RuntimeError("Complementarity condition %s must have exactly two finite bounds" % cdata.name) # # Swap if the body of the second constraint is not a free variable # if not id(_e2[1]) in free_vars and id(_e1[1]) in free_vars: _e1, _e2 = _e2, _e1 # # Rework the first constraint to have a zero bound. # The bound is a lower or upper bound depending on the # variable bound. # if not _e1[0] is None: cdata.bv = Var() cdata.c = Constraint(expr=0 <= cdata.bv) if not _e2[0] is None: cdata.bc = Constraint(expr=cdata.bv == _e1[1] - _e1[0]) else: cdata.bc = Constraint(expr=cdata.bv == _e1[0] - _e1[1]) elif not _e1[2] is None: cdata.bv = Var() cdata.c = Constraint(expr=0 <= cdata.bv) if not _e2[2] is None: cdata.bc = Constraint(expr=cdata.bv == _e1[1] - _e1[2]) else: cdata.bc = Constraint(expr=cdata.bv == _e1[2] - _e1[1]) else: cdata.bv = Var() cdata.bc = Constraint(expr=cdata.bv == _e1[1]) cdata.c = Constraint(expr=(None, cdata.bv, None)) # # If the body of the second constraint is a free variable, then keep it. # Otherwise, create a new variable and a new constraint. # if id(_e2[1]) in free_vars: var = _e2[1] cdata.c._vid = id(_e2[1]) del free_vars[cdata.c._vid] else: var = cdata.v = Var() cdata.c._vid = id(cdata.v) cdata.e = Constraint(expr=cdata.v == _e2[1]) # # Set the variable bound values, and corresponding _complementarity value # cdata.c._complementarity = 0 if not _e2[0] is None: if var.lb is None or value(_e2[0]) > value(var.lb): var.setlb(_e2[0]) cdata.c._complementarity += 1 if not _e2[2] is None: if var.ub is None or value(_e2[2]) > value(var.ub): var.setub(_e2[2]) cdata.c._complementarity += 2
def convert_dakota(options=Options(), parser=None): # # Import plugins # import pyomo.environ model_file = os.path.basename(options.model.save_file) model_file_no_ext = os.path.splitext(model_file)[0] # # Set options for writing the .nl and related files # # By default replace .py with .nl if options.model.save_file is None: options.model.save_file = model_file_no_ext + '.nl' options.model.save_format = ProblemFormat.nl # Dakota requires .row/.col files options.model.symbolic_solver_labels = True # # Call the core converter # model_data = convert(options, parser) # # Generate Dakota input file fragments for the Vars, Objectives, Constraints # # TODO: the converted model doesn't expose the right symbol_map # for only the vars active in the .nl model = model_data.instance # Easy way #print "VARIABLE:" #lines = open(options.save_model.replace('.nl','.col'),'r').readlines() #for varName in lines: # varName = varName.strip() # var = model_data.symbol_map.getObject(varName) # print "'%s': %s" % (varName, var) # #print var.pprint() # Hard way variables = 0 var_descriptors = [] var_lb = [] var_ub = [] var_initial = [] tmpDict = model_data.symbol_map.getByObjectDictionary() for var in model.component_data_objects(Var, active=True): if id(var) in tmpDict: variables += 1 var_descriptors.append(var.cname(True)) # apply user bound, domain bound, or infinite _lb, _ub = var.bounds if _lb is not None: var_lb.append(str(_lb)) else: var_lb.append("-inf") if _ub is not None: var_ub.append(str(_ub)) else: var_ub.append("inf") try: val = value(var) except: val = None var_initial.append(str(val)) objectives = 0 obj_descriptors = [] for obj in model.component_data_objects(Objective, active=True): objectives += 1 obj_descriptors.append(obj.cname(True)) constraints = 0 cons_descriptors = [] cons_lb = [] cons_ub = [] for con in model.component_data_objects(Constraint, active=True): constraints += 1 cons_descriptors.append(con.cname(True)) if con.lower is not None: cons_lb.append(str(con.lower)) else: cons_lb.append("-inf") if con.upper is not None: cons_ub.append(str(con.upper)) else: cons_ub.append("inf") # Write the Dakota input file fragments dakfrag = open(model_file_no_ext + ".dak", 'w') dakfrag.write("#--- Dakota variables block ---#\n") dakfrag.write("variables\n") dakfrag.write(" continuous_design " + str(variables) + '\n') dakfrag.write(" descriptors\n") for vd in var_descriptors: dakfrag.write(" '%s'\n" % vd) dakfrag.write(" lower_bounds " + " ".join(var_lb) + '\n') dakfrag.write(" upper_bounds " + " ".join(var_ub) + '\n') dakfrag.write(" initial_point " + " ".join(var_initial) + '\n') dakfrag.write("#--- Dakota interface block ---#\n") dakfrag.write("interface\n") dakfrag.write(" algebraic_mappings = '" + options.model.save_file + "'\n") dakfrag.write("#--- Dakota responses block ---#\n") dakfrag.write("responses\n") dakfrag.write(" objective_functions " + str(objectives) + '\n') if (constraints > 0): dakfrag.write(" nonlinear_inequality_constraints " + str(constraints) + '\n') dakfrag.write(" lower_bounds " + " ".join(cons_lb) + '\n') dakfrag.write(" upper_bounds " + " ".join(cons_ub) + '\n') dakfrag.write(" descriptors\n") for od in obj_descriptors: dakfrag.write(" '%s'\n" % od) if (constraints > 0): for cd in cons_descriptors: dakfrag.write(" '%s'\n" % cd) # TODO: detect whether gradient information available in model dakfrag.write(" analytic_gradients\n") dakfrag.write(" no_hessians\n") dakfrag.close() sys.stdout.write( "Dakota input fragment written to file '%s'\n" % (model_file_no_ext + ".dak",) ) return model_data
def collect_general_canonical_repn(exp, idMap, compute_values): # global temp_const # global temp_var # global temp_nonl temp_const = { 0: {None:0.0} } temp_var = { 1: {GeneralCanonicalRepn({None:1}):1.0} } temp_nonl = { None: None } exp_type = type(exp) # # Constant # if exp.is_fixed(): if compute_values: temp_const[0][None] = value(exp) else: temp_const[0][None] = exp return temp_const # # Expression # elif exp.is_expression(): # # Sum # if exp_type is expr._SumExpression: if exp._const != 0.0: repn = { 0: {None:exp._const} } else: repn = {} for i in xrange(len(exp._args)): repn = repn_add( repn, collect_general_canonical_repn(exp._args[i], idMap, compute_values), coef=exp._coef[i]) return repn # # Product # elif exp_type is expr._ProductExpression: # # Iterate through the denominator. If they aren't all # constants, then simply return this expression. # denom=1.0 for e in exp._denominator: if e.is_fixed(): denom *= e() else: temp_nonl[None] = exp return temp_nonl if denom == 0.0: print("Divide-by-zero error - offending sub-expression:") e.pprint() raise ZeroDivisionError # # OK, the denominator is a constant. # repn = { 0: {None:exp._coef / denom} } for e in exp._numerator: repn = repn_mult( repn, collect_general_canonical_repn(e, idMap, compute_values)) return repn # # Power Expression # elif exp_type is expr._PowExpression: if exp.polynomial_degree() is None: raise TypeError("Unsupported general power expression: " +str(exp._args)) # If this is of the form EXPR**1, we can just get the # representation of EXPR if exp._args[1] == 1: return collect_general_canonical_repn(exp._args[0], idMap, compute_values) # The only other way to get a polynomial expression is if # exp=EXPR**p where p is fixed a nonnegative integer. We # can expand this expression and generate a canonical # representation from there. If p=0, this expression is # constant (and is processed by the is_fixed code above # NOTE: There is no check for 0**0 return collect_general_canonical_repn( reduce( lambda x,y: x*y, [exp._args[0]]*int(value(exp._args[1])), 1.0 ), idMap, compute_values) elif exp_type is expr.Expr_if: if exp._if.is_fixed(): if exp._if(): return collect_general_canonical_repn(exp._then, idMap, compute_values) else: return collect_general_canonical_repn(exp._else, idMap, compute_values) else: temp_nonl[None] = exp return temp_nonl # # Expression (the component) # (faster check) elif isinstance(exp, _ExpressionData): return collect_general_canonical_repn(exp.expr, idMap, compute_values) # # ERROR # else: raise ValueError("Unsupported expression type: "+str(exp)) # # Variable # elif (exp.__class__ is _GeneralVarData) or isinstance(exp, _VarData): id_ = id(exp) if id_ in idMap[None]: key = idMap[None][id_] else: key = len(idMap) - 1 idMap[None][id_] = key idMap[key] = exp temp_var = { -1: {key:exp}, 1: {GeneralCanonicalRepn({key:1}):1.0} } return temp_var # # Connector # elif exp_type is _ConnectorValue or exp.type() is Connector: # Silently omit constraint... The ConnectorExpander should # expand this constraint into indvidual constraints that # reference "real" variables. return {} # # ERROR # else: raise ValueError("Unexpected expression (type %s): " % ( type(exp).__name__, str(exp) ))
def pyomo4_generate_canonical_repn(exp, idMap=None, compute_values=True): if exp is None: return CompiledLinearCanonicalRepn() if exp.__class__ in native_numeric_types: ans = CompiledLinearCanonicalRepn() ans.constant = value(exp) return ans if not exp.is_expression(): if exp.is_fixed(): ans = CompiledLinearCanonicalRepn() ans.constant = value(exp) return ans elif isinstance(exp, _VarData): ans = CompiledLinearCanonicalRepn() ans.constant = 0 ans.linear = (1.,) ans.variables = (exp,) return ans else: raise RuntimeError( "Unrecognized expression node: %s" % (type(exp),) ) degree = exp.polynomial_degree() if degree == 1: _stack = [] _args = exp._args _idx = 0 _len = len(_args) _result = None while 1: # Linear expressions just need to be filteres and copied if exp.__class__ is expr_pyomo4._LinearExpression: _result = expr_pyomo4._LinearExpression(None, 0) _result._args = [] _result._coef.clear() _result._const = value(exp._const) for v in _args: _id = id(v) if v.is_fixed(): _result._const += v.value * value(exp._coef[_id]) else: _result._args.append(v) _result._coef[_id] = value(exp._coef[_id]) _idx = _len # Other expressions get their arguments parsed one at a time if _idx < _len: _stack.append((exp, _args, _idx+1, _len, _result)) exp = _args[_idx] if exp.__class__ in native_numeric_types: _len = _idx = 0 _result = exp elif exp.is_expression(): _args = exp._args _idx = 0 _len = len(_args) _result = None continue elif isinstance(exp, _VarData): _len = _idx = 0 if exp.is_fixed(): _result = exp.value else: _result = expr_pyomo4._LinearExpression(exp, 1.) else: raise RuntimeError( "Unrecognized expression node: %s" % (type(exp),) ) # # End of _args... time to move up the stack # # Top of the stack. _result had better be a _LinearExpression if not _stack: ans = CompiledLinearCanonicalRepn() # old format ans.constant = _result._const ans.linear = [] for v in _result._args: # Note: this also filters out the bogus NONE we added above _coef = _result._coef[id(v)] if _coef: ans.variables.append(v) ans.linear.append(_coef) if idMap: if None not in idMap: idMap[None] = {} _test = idMap[None] _key = len(idMap) - 1 for v in ans.variables: if id(v) not in _test: _test[id(v)] = _key idMap[_key] = v _key += 1 return ans # Ok ... process the new argument to the node. Note that # _idx is 1-based now... _inner_result = _result exp, _args, _idx, _len, _result = _stack.pop() if exp.__class__ is expr_pyomo4._SumExpression: if _idx == 1: _result = _inner_result else: _result += _inner_result elif exp.__class__ is expr_pyomo4._ProductExpression: if _idx == 1: _result = _inner_result else: _result *= _inner_result elif exp.__class__ is expr_pyomo4._DivisionExpression: if _idx == 1: _result = _inner_result else: _result /= _inner_result elif exp.__class__ is expr_pyomo4._NegationExpression: _result = -_inner_result elif exp.__class__ is expr_pyomo4._PowExpression: # We know this is either constant or linear if _idx == 1: _result = _inner_result else: coef = value(_inner_result) if not coef: _result = 1. elif coef != 1: _result = _result ** coef elif exp.__class__ is expr_pyomo4.Expr_if: if _idx == 1: _result = [_inner_result] else: _result.append(_inner_result) if _idx == 3: if value(_result[0]): _result = _result[1] else: _result = _result[2] elif exp.__class__ in _identity_collectors: _result = _inner_result elif exp.is_fixed(): _result = value(exp) else: raise RuntimeError( "Unknown non-fixed subexpression type %s" % (type(exp),) ) elif degree == 0: ans = CompiledLinearCanonicalRepn() ans.constant = value(exp) return ans # **Py3k: degree > 1 comparision will error if degree is None elif degree and degree > 1: raise RuntimeError("generate_canonical_repn does not support nonlinear Pyomo4 expressions") ans = collect_general_canonical_repn(exp, idMap, compute_values) if 1 in ans: linear_terms = {} for key, coef in iteritems(ans[1]): linear_terms[list(key.keys())[0]] = coef ans[1] = linear_terms return GeneralCanonicalRepn(ans) else: return GeneralCanonicalRepn( { None: exp, -1 : collect_variables(exp, idMap) } )