def create_linear_dual_from(block, fixed=None, unfixed=None): """ Construct a block that represents the dual of the given block. The resulting block contains variables and constraints whose names are the dual names of the primal block. Note that this involves a many string operations. A quicker operations could be executed, but it would generate a dual representation that is difficult to interpret. Note that the dualization of a maximization problem is performed by negating objective and right-hand side coefficients after dualizing the corresponding minimization problem. This suggestion is made by Dimitri Bertsimas and John Tsitsiklis in section 4.2 page 143 of "Introduction to Linear Optimization" Arguments: block: A Pyomo block or model unfixed: An iterable object with VarData values that are not fixed variables. All other variables are assumed to be fixed. fixed: An iterable object with VarData values that are fixed. All other variables are assumed not fixed. Returns: If the block is a model object, then this returns a ConcreteModel. Otherwise, it returns a Block. """ # # Collect linear terms from the block # # NOTE: We are ignoring the vnames and cnames data # A, b_coef, c_rhs, c_sense, d_sense, v_domain = \ collect_dual_representation(block, fixed, unfixed) # # Construct the block # if isinstance(block, Model): dual = ConcreteModel() else: dual = Block() dual.construct() _vars = {} # Return variable object from name and index (if applicable) def getvar(name, ndx=None): v = _vars.get((name, ndx), None) if v is None: v = Var() if ndx is None: v_name = name elif isinstance(ndx, tuple): v_name = "%s[%s]" % (name, ','.join(map(str, ndx))) else: v_name = "%s[%s]" % (name, str(ndx)) setattr(dual, v_name, v) _vars[name, ndx] = v return v # # Construct the objective # The dualization of a maximization problem is handled by simply negating the # objective and left-hand side coefficients while keeping the dual sense. # if d_sense == minimize: dual.o = Objective(expr=sum(-b_coef[name, ndx] * getvar(name, ndx) for name, ndx in b_coef), sense=d_sense) rhs_multiplier = -1 else: dual.o = Objective(expr=sum(b_coef[name, ndx] * getvar(name, ndx) for name, ndx in b_coef), sense=d_sense) rhs_multiplier = 1 # # Construct the constraints from dual A matrix # for cname in A: for ndx, terms in A[cname].items(): # Build left-hand side of constraint expr = 0 for term in terms: expr += term.coef * getvar(term.var, term.ndx) # # Assign right-hand side coefficient # Note that rhs_multiplier is 1 if the dual is a maximization problem and -1 otherwise # rhsval = rhs_multiplier * c_rhs.get((cname, ndx), 0.0) # Using the correct inequality or equality if c_sense[cname, ndx] == 'e': e = expr - rhsval == 0 elif c_sense[cname, ndx] == 'l': e = expr - rhsval <= 0 else: e = expr - rhsval >= 0 c = Constraint(expr=e) # Build constraint name if ndx is None: c_name = cname elif isinstance(ndx, tuple): c_name = "%s[%s]" % (cname, ','.join(map(str, ndx))) else: c_name = "%s[%s]" % (cname, str(ndx)) # Add new constraint along with its name to the dual setattr(dual, c_name, c) # Set variable domains for (name, ndx), domain in v_domain.items(): v = getvar(name, ndx) #flag = type(ndx) is tuple and (ndx[-1] == 'lb' or ndx[-1] == 'ub') if domain == 1: v.domain = NonNegativeReals elif domain == -1: v.domain = NonPositiveReals else: # This is possible when the variable's corresponding constraint is an equality v.domain = Reals return dual
def _generate_model(self): self.model = ConcreteModel() model = self.model model._name = self.description model.s = RangeSet(1, 12) model.x = Var(model.s) model.x[1].setlb(-1) model.x[1].setub(1) model.x[2].setlb(-1) model.x[2].setub(1) model.obj = Objective(expr=sum(model.x[i] * ((-1)**(i + 1)) for i in model.x.index_set())) model.c = ConstraintList() # to make the variable used in the constraint match the name model.c.add(Constraint.Skip) model.c.add(Constraint.Skip) model.c.add(model.x[3] >= -1.) model.c.add(model.x[4] <= 1.) model.c.add(model.x[5] == -1.) model.c.add(model.x[6] == -1.) model.c.add(model.x[7] == 1.) model.c.add(model.x[8] == 1.) model.c.add((-1., model.x[9], -1.)) model.c.add((-1., model.x[10], -1.)) model.c.add((1., model.x[11], 1.)) model.c.add((1., model.x[12], 1.)) cdata = model.c.add((0, 1, 3)) assert cdata.lower == 0 assert cdata.upper == 3 assert cdata.body() == 1 assert not cdata.equality cdata = model.c.add((0, 2, 3)) assert cdata.lower == 0 assert cdata.upper == 3 assert cdata.body() == 2 assert not cdata.equality cdata = model.c.add((0, 1, None)) assert cdata.lower is None assert cdata.upper == 1 assert cdata.body() == 0 assert not cdata.equality cdata = model.c.add((None, 0, 1)) assert cdata.lower is None assert cdata.upper == 1 assert cdata.body() == 0 assert not cdata.equality cdata = model.c.add((1, 1)) assert cdata.lower == 1 assert cdata.upper == 1 assert cdata.body() == 1 assert cdata.equality model.fixed_var = Var() model.fixed_var.fix(1.0) cdata = model.c.add((0, 1 + model.fixed_var, 3)) cdata = model.c.add((0, 2 + model.fixed_var, 3)) cdata = model.c.add((0, model.fixed_var, None)) cdata = model.c.add((None, model.fixed_var, 1)) cdata = model.c.add((model.fixed_var, 1)) model.c_inactive = ConstraintList() # to make the variable used in the constraint match the name model.c_inactive.add(Constraint.Skip) model.c_inactive.add(Constraint.Skip) model.c_inactive.add(model.x[3] >= -2.) model.c_inactive.add(model.x[4] <= 2.) compile_block_linear_constraints(model, 'Amatrix')
def test_numerical_instability_early_elimination(self): # A more subtle numerical problem is that, in infinite precision, a # variable might be eliminated early. However, if this goes wrong, the # result can be unexpected (including getting no constraints when some # are expected.) m = ConcreteModel() m.x = Var() m.x0 = Var() m.y = Var() # we'll pretend that the 1.123e-9 is noise from previous calculations m.cons1 = Constraint( expr=0 <= (4.27 + 1.123e-9) * m.x + 13 * m.y - m.x0) m.cons2 = Constraint(expr=m.x0 >= 12 * m.y + 4.27 * m.x) fme = TransformationFactory('contrib.fourier_motzkin_elimination') # doing my own clones because I want assertIs tests first = m.clone() second = m.clone() third = m.clone() fme.apply_to(first, vars_to_eliminate=[first.x0], zero_tolerance=1e-10) constraints = first._pyomo_contrib_fme_transformation.\ projected_constraints cons = constraints[1] self.assertEqual(cons.lower, 0) repn = generate_standard_repn(cons.body) self.assertTrue(repn.is_linear()) self.assertEqual(repn.constant, 0) self.assertEqual(len(repn.linear_coefs), 2) # x is still around self.assertIs(repn.linear_vars[0], first.x) self.assertAlmostEqual(repn.linear_coefs[0], 1.123e-9) self.assertIs(repn.linear_vars[1], first.y) self.assertEqual(repn.linear_coefs[1], 1) self.assertIsNone(cons.upper) # so just to drive home the point, this results in no constraints: # (Though also note that that only happens if x0 is the first to be # projected out) fme.apply_to(second, vars_to_eliminate=[second.x0, second.x], zero_tolerance=1e-10) self.assertEqual(len(second._pyomo_contrib_fme_transformation.\ projected_constraints), 0) # but in this version, we assume that x is already gone... fme.apply_to(third, vars_to_eliminate=[third.x0], verbose=True, zero_tolerance=1e-8) constraints = third._pyomo_contrib_fme_transformation.\ projected_constraints cons = constraints[1] self.assertEqual(cons.lower, 0) self.assertIs(cons.body, third.y) self.assertIsNone(cons.upper) # and this is exactly the same as the above: fme.apply_to(m, vars_to_eliminate=[m.x0, m.x], verbose=True, zero_tolerance=1e-8) constraints = m._pyomo_contrib_fme_transformation.projected_constraints cons = constraints[1] self.assertEqual(cons.lower, 0) self.assertIs(cons.body, m.y) self.assertIsNone(cons.upper)
def _generate_model(self): self.model = None self.model = ConcreteModel() model = self.model model._name = self.description model.s = Set(initialize=[1, 2]) model.x_unused = Var() model.x_unused.stale = False model.x_unused_initialy_stale = Var() model.x_unused_initialy_stale.stale = True model.X_unused = Var(model.s) model.X_unused_initialy_stale = Var(model.s) for i in model.s: model.X_unused[i].stale = False model.X_unused_initialy_stale[i].stale = True model.x = Var() model.x.stale = False model.x_initialy_stale = Var() model.x_initialy_stale.stale = True model.X = Var(model.s) model.X_initialy_stale = Var(model.s) for i in model.s: model.X[i].stale = False model.X_initialy_stale[i].stale = True model.obj = Objective(expr= model.x + \ model.x_initialy_stale + \ summation(model.X) + \ summation(model.X_initialy_stale)) model.c = ConstraintList() model.c.add(model.x >= 1) model.c.add(model.x_initialy_stale >= 1) model.c.add(model.X[1] >= 0) model.c.add(model.X[2] >= 1) model.c.add(model.X_initialy_stale[1] >= 0) model.c.add(model.X_initialy_stale[2] >= 1) # Test that stale flags do not get updated # on inactive blocks (where "inactive blocks" mean blocks # that do NOT follow a path of all active parent blocks # up to the top-level model) flat_model = model.clone() model.b = Block() model.B = Block(model.s) model.b.b = flat_model.clone() model.B[1].b = flat_model.clone() model.B[2].b = flat_model.clone() model.b.deactivate() model.B.deactivate() model.b.b.activate() model.B[1].b.activate() model.B[2].b.deactivate() assert model.b.active is False assert model.B[1].active is False assert model.B[1].active is False assert model.b.b.active is True assert model.B[1].b.active is True assert model.B[2].b.active is False
def test_project_disaggregated_vars(self): """This is a little bit more of an integration test with GDP, but also an example of why FME is 'useful.' We will give a GDP, take chull relaxation, and then project out the disaggregated variables.""" m = ConcreteModel() m.p = Var([1, 2], bounds=(0, 10)) m.time1 = Disjunction(expr=[m.p[1] >= 1, m.p[1] == 0]) m.on = Disjunct() m.on.above_min = Constraint(expr=m.p[2] >= 1) m.on.ramping = Constraint(expr=m.p[2] - m.p[1] <= 3) m.on.on_before = Constraint(expr=m.p[1] >= 1) m.startup = Disjunct() m.startup.startup_limit = Constraint(expr=(1, m.p[2], 2)) m.startup.off_before = Constraint(expr=m.p[1] == 0) m.off = Disjunct() m.off.off = Constraint(expr=m.p[2] == 0) m.time2 = Disjunction(expr=[m.on, m.startup, m.off]) TransformationFactory('gdp.chull').apply_to(m) relaxationBlocks = m._pyomo_gdp_chull_relaxation.relaxedDisjuncts disaggregatedVars = ComponentSet([ relaxationBlocks[0].component("p[1]"), relaxationBlocks[1].component("p[1]"), relaxationBlocks[2].component("p[1]"), relaxationBlocks[2].component("p[2]"), relaxationBlocks[3].component("p[1]"), relaxationBlocks[3].component("p[2]"), relaxationBlocks[4].component("p[1]"), relaxationBlocks[4].component("p[2]") ]) TransformationFactory('contrib.fourier_motzkin_elimination').apply_to( m, vars_to_eliminate=disaggregatedVars) constraints = m._pyomo_contrib_fme_transformation.projected_constraints # we of course get tremendous amounts of garbage, but we make sure that # what should be here is: # p[1] >= on.ind_var cons = constraints[22] self.assertEqual(cons.lower, 0) self.assertIsNone(cons.upper) body = generate_standard_repn(cons.body) self.assertEqual(body.constant, 0) self.assertEqual(len(body.linear_vars), 2) self.assertTrue(body.is_linear()) self.assertIs(body.linear_vars[0], m.p[1]) self.assertEqual(body.linear_coefs[0], 1) self.assertIs(body.linear_vars[1], m.on.indicator_var) self.assertEqual(body.linear_coefs[1], -1) # p[1] <= 10*on.ind_var + 10*off.ind_var cons = constraints[20] self.assertEqual(cons.lower, 0) self.assertIsNone(cons.upper) body = generate_standard_repn(cons.body) self.assertEqual(body.constant, 0) self.assertEqual(len(body.linear_vars), 3) self.assertTrue(body.is_linear()) self.assertIs(body.linear_vars[0], m.on.indicator_var) self.assertEqual(body.linear_coefs[0], 10) self.assertIs(body.linear_vars[1], m.off.indicator_var) self.assertEqual(body.linear_coefs[1], 10) self.assertIs(body.linear_vars[2], m.p[1]) self.assertEqual(body.linear_coefs[2], -1) # p[1] >= time1_disjuncts[0].ind_var cons = constraints[58] self.assertEqual(cons.lower, 0) self.assertIsNone(cons.upper) body = generate_standard_repn(cons.body) self.assertEqual(body.constant, 0) self.assertEqual(len(body.linear_vars), 2) self.assertTrue(body.is_linear()) self.assertIs(body.linear_vars[1], m.time1_disjuncts[0].indicator_var) self.assertEqual(body.linear_coefs[1], -1) self.assertIs(body.linear_vars[0], m.p[1]) self.assertEqual(body.linear_coefs[0], 1) # p[1] <= 10*time1_disjuncts[0].ind_var cons = constraints[61] self.assertEqual(cons.lower, 0) self.assertIsNone(cons.upper) body = generate_standard_repn(cons.body) self.assertEqual(body.constant, 0) self.assertEqual(len(body.linear_vars), 2) self.assertTrue(body.is_linear()) self.assertIs(body.linear_vars[0], m.time1_disjuncts[0].indicator_var) self.assertEqual(body.linear_coefs[0], 10) self.assertIs(body.linear_vars[1], m.p[1]) self.assertEqual(body.linear_coefs[1], -1) # p[2] - p[1] <= 3*on.ind_var + 2*startup.ind_var cons = constraints[56] self.assertEqual(value(cons.lower), 0) self.assertIsNone(cons.upper) body = generate_standard_repn(cons.body) self.assertEqual(body.constant, 0) self.assertEqual(len(body.linear_vars), 4) self.assertTrue(body.is_linear()) self.assertIs(body.linear_vars[3], m.p[2]) self.assertEqual(body.linear_coefs[3], -1) self.assertIs(body.linear_vars[0], m.p[1]) self.assertEqual(body.linear_coefs[0], 1) self.assertIs(body.linear_vars[1], m.on.indicator_var) self.assertEqual(body.linear_coefs[1], 3) self.assertIs(body.linear_vars[2], m.startup.indicator_var) self.assertEqual(body.linear_coefs[2], 2) # p[2] >= on.ind_var + startup.ind_var cons = constraints[38] self.assertEqual(cons.lower, 0) self.assertIsNone(cons.upper) body = generate_standard_repn(cons.body) self.assertEqual(body.constant, 0) self.assertEqual(len(body.linear_vars), 3) self.assertTrue(body.is_linear()) self.assertIs(body.linear_vars[0], m.p[2]) self.assertEqual(body.linear_coefs[0], 1) self.assertIs(body.linear_vars[1], m.startup.indicator_var) self.assertEqual(body.linear_coefs[1], -1) self.assertIs(body.linear_vars[2], m.on.indicator_var) self.assertEqual(body.linear_coefs[2], -1) # p[2] <= 10*on.ind_var + 2*startup.ind_var cons = constraints[32] self.assertEqual(cons.lower, 0) self.assertIsNone(cons.upper) body = generate_standard_repn(cons.body) self.assertEqual(body.constant, 0) self.assertEqual(len(body.linear_vars), 3) self.assertTrue(body.is_linear()) self.assertIs(body.linear_vars[0], m.on.indicator_var) self.assertEqual(body.linear_coefs[0], 10) self.assertIs(body.linear_vars[1], m.startup.indicator_var) self.assertEqual(body.linear_coefs[1], 2) self.assertIs(body.linear_vars[2], m.p[2]) self.assertEqual(body.linear_coefs[2], -1) # 1 <= time1_disjuncts[0].ind_var + time_1.disjuncts[1].ind_var cons = constraints[1] self.assertEqual(cons.lower, 1) self.assertIsNone(cons.upper) body = generate_standard_repn(cons.body) self.assertEqual(body.constant, 0) self.assertEqual(len(body.linear_vars), 2) self.assertTrue(body.is_linear()) self.assertIs(body.linear_vars[0], m.time1_disjuncts[0].indicator_var) self.assertEqual(body.linear_coefs[0], 1) self.assertIs(body.linear_vars[1], m.time1_disjuncts[1].indicator_var) self.assertEqual(body.linear_coefs[1], 1) # 1 >= time1_disjuncts[0].ind_var + time_1.disjuncts[1].ind_var cons = constraints[2] self.assertEqual(cons.lower, -1) self.assertIsNone(cons.upper) body = generate_standard_repn(cons.body) self.assertEqual(body.constant, 0) self.assertEqual(len(body.linear_vars), 2) self.assertTrue(body.is_linear()) self.assertIs(body.linear_vars[0], m.time1_disjuncts[0].indicator_var) self.assertEqual(body.linear_coefs[0], -1) self.assertIs(body.linear_vars[1], m.time1_disjuncts[1].indicator_var) self.assertEqual(body.linear_coefs[1], -1) # 1 <= on.ind_var + startup.ind_var + off.ind_var cons = constraints[3] self.assertEqual(cons.lower, 1) self.assertIsNone(cons.upper) body = generate_standard_repn(cons.body) self.assertEqual(body.constant, 0) self.assertEqual(len(body.linear_vars), 3) self.assertTrue(body.is_linear()) self.assertIs(body.linear_vars[0], m.on.indicator_var) self.assertEqual(body.linear_coefs[0], 1) self.assertIs(body.linear_vars[1], m.startup.indicator_var) self.assertEqual(body.linear_coefs[1], 1) self.assertIs(body.linear_vars[2], m.off.indicator_var) self.assertEqual(body.linear_coefs[2], 1) # 1 >= on.ind_var + startup.ind_var + off.ind_var cons = constraints[4] self.assertEqual(cons.lower, -1) self.assertIsNone(cons.upper) body = generate_standard_repn(cons.body) self.assertEqual(body.constant, 0) self.assertEqual(len(body.linear_vars), 3) self.assertTrue(body.is_linear()) self.assertIs(body.linear_vars[0], m.on.indicator_var) self.assertEqual(body.linear_coefs[0], -1) self.assertIs(body.linear_vars[1], m.startup.indicator_var) self.assertEqual(body.linear_coefs[1], -1) self.assertIs(body.linear_vars[2], m.off.indicator_var) self.assertEqual(body.linear_coefs[2], -1)
def create_linear_dual_from(block, fixed=None, unfixed=None): """ Construct a block that represents the dual of the given block. The resulting block contains variables and constraints whose names are the dual names of the primal block. Note that this involves a many string operations. A quicker operations could be executed, but it would generate a dual representation that is difficult to interpret. Note that the dualization of a maximization problem is performed by negating objective and right-hand side coefficients after dualizing the corresponding minimization problem. This suggestion is made by Dimitri Bertsimas and John Tsitsiklis in section 4.2 page 143 of "Introduction to Linear Optimization" Arguments: block: A Pyomo block or model unfixed: An iterable object with Variable and VarData values that are not fixed variables. All other variables are assumed to be fixed. fixed: An iterable object with Variable and VarData values that are fixed. All other variables are assumed not fixed. Returns: If the block is a model object, then this returns a ConcreteModel. Otherwise, it returns a Block. """ # # Collect vardata that needs to be fixed # fixed_modelvars = {} if fixed or unfixed: # # Collect model variables # modelvars = {} # # vardata in objectives # for obj in block.component_objects(Objective, active=True): for ndx in obj: #odata = generate_standard_repn(obj[ndx].expr, compute_values=False) for vdata in identify_variables(obj[ndx].expr, include_fixed=False): id_ = id(vdata) if not id_ in modelvars: modelvars[id_] = vdata # # vardata in constraints # for con in block.component_objects(Constraint, active=True): for ndx in con: #cdata = generate_standard_repn(con[ndx].body, compute_values=False) for vdata in identify_variables(con[ndx].body, include_fixed=False): id_ = id(vdata) if not id_ in modelvars: modelvars[id_] = vdata # # Fix everything that isn't specified as unfixed # if unfixed: unfixed_vars = set() for v in unfixed: if v.is_indexed(): for vardata in v.values(): unfixed_vars.add(id(vardata)) else: unfixed_vars.add(id(v)) for id_, vdata in modelvars.items(): if id_ not in unfixed_vars: fixed_modelvars[id_] = vdata # # ... or fix everything that is specified as fixed # elif fixed: fixed_vars = set() for v in fixed: if v.is_indexed(): for vardata in v.values(): fixed_vars.add(id(vardata)) else: fixed_vars.add(id(v)) for id_ in fixed_vars: if id_ in modelvars: fixed_modelvars[id_] = modelvars[id_] A, b_coef, obj_constant, c_rhs, c_sense, d_sense, v_domain =\ collect_dual_representation(block, fixed_modelvars) # # Construct the block # if isinstance(block, Model): dual = ConcreteModel() else: dual = Block() dual.construct() _vars = {} # Return variable object from name and index (if applicable) def getvar(name, ndx=None): v = _vars.get((name, ndx), None) if v is None: v = Var() if ndx is None: v_name = name elif isinstance(ndx, tuple): v_name = "%s[%s]" % (name, ','.join(map(str, ndx))) else: v_name = "%s[%s]" % (name, str(ndx)) setattr(dual, v_name, v) _vars[name, ndx] = v return v # # Construct the objective # The dualization of a maximization problem is handled by simply negating the # objective and left-hand side coefficients while keeping the dual sense. # if d_sense == minimize: dual.o = Objective(expr=obj_constant + sum(-b_coef[name, ndx] * getvar(name, ndx) for name, ndx in b_coef), sense=d_sense) rhs_multiplier = -1 else: dual.o = Objective(expr=obj_constant + sum(b_coef[name, ndx] * getvar(name, ndx) for name, ndx in b_coef), sense=d_sense) rhs_multiplier = 1 # # Construct the constraints from dual A matrix # for cname in A: for ndx, terms in A[cname].items(): # Build left-hand side of constraint expr = 0 for term in terms: expr += term.coef * getvar(term.var, term.ndx) # # Assign right-hand side coefficient # Note that rhs_multiplier is 1 if the dual is a maximization problem and -1 otherwise # rhsval = rhs_multiplier * c_rhs.get((cname, ndx), 0.0) # Using the correct inequality or equality if c_sense[cname, ndx] == 'e': e = expr - rhsval == 0 elif c_sense[cname, ndx] == 'l': e = expr - rhsval <= 0 else: e = expr - rhsval >= 0 c = Constraint(expr=e) # Build constraint name if ndx is None: c_name = cname elif isinstance(ndx, tuple): c_name = "%s[%s]" % (cname, ','.join(map(str, ndx))) else: c_name = "%s[%s]" % (cname, str(ndx)) # Add new constraint along with its name to the dual setattr(dual, c_name, c) # Set variable domains for (name, ndx), domain in v_domain.items(): v = getvar(name, ndx) #flag = type(ndx) is tuple and (ndx[-1] == 'lb' or ndx[-1] == 'ub') if domain == 1: v.domain = NonNegativeReals elif domain == -1: v.domain = NonPositiveReals else: # This is possible when the variable's corresponding constraint is an equality v.domain = Reals return dual
def test_abs(self): m = ConcreteModel() m.x = Var(bounds=(-1, 1), initialize=0) mc_expr = mc(abs((m.x))) self.assertEqual(mc_expr.lower(), 0) self.assertEqual(mc_expr.upper(), 1)
def criticalityCheck(self, x, y, z, rom_params, worstcase=False, M=[0.0]): model = self.model self.setVarValue(x=x,y=y,z=z) self.setBound(x, y, z, 1e10) self.deactiveExtraConObj() self.activateRomCons(x, rom_params) optGJH = SolverFactory('contrib.gjh') optGJH.solve(model, tee=False, symbolic_solver_labels=True) g, J, varlist, conlist = model._gjh_info l = ConcreteModel() l.v = Var(varlist, domain=Reals) for i in varlist: #dummy = model.find_component(i) l.v[i] = 0.0 l.v[i].setlb(-1.0) l.v[i].setub(1.0) if worstcase: if M.all() == 0.0: print('WARNING: worstcase criticality was requested but Jacobian error bound is zero') l.t = Var(range(0, self.ly), domain=Reals) for i in range(0, self.ly): l.t[i].setlb(-M[i]) l.t[i].setub(M[i]) def linConMaker(l, i): # i should be range(len(conlist) - 1) # because last element of conlist is the objective con_i = model.find_component(conlist[i]) isEquality = con_i.equality isROM = False if conlist[i][:7] == '.' + self.TRF.name + '.rom': isROM = True romIndex = int(filter(str.isdigit, conlist[i])) # This is very inefficient # Fix this later if these problems get big # This is the ith row of J times v Jv = sum(x[2] * l.v[varlist[x[1]]] for x in J if x[0] == i) if isEquality: if worstcase and isROM: return Jv + l.t[romIndex] == 0 else: return Jv == 0 else: lo = con_i.lower up = con_i.upper if lo is not None: level = lo.value - con_i.lslack() if up is not None: return (lo.value <= level + Jv <= up.value) else: return (lo.value <= level + Jv) elif up is not None: level = up.value - con_i.uslack() return (level + Jv <= up.value) else: raise Exception( "This constraint seems to be neither equality or inequality: " + conlist(i)) l.lincons = Constraint(range(len(conlist)-1), rule=linConMaker) l.obj = Objective(expr=sum(x[1] * l.v[varlist[x[0]]] for x in g)) # Calculate gradient norm for scaling purposes gfnorm = sqrt(sum(x[1]**2 for x in g)) opt = SolverFactory(self.solver, solver_io=self.solver_io) opt.options['halt_on_ampl_error'] = 'yes' opt.options['max_iter'] = 5000 results = opt.solve( l, keepfiles=self.keepfiles, tee=self.stream_solver) if ((results.solver.status == SolverStatus.ok) and (results.solver.termination_condition == TerminationCondition.optimal)): l.solutions.load_from(results) if gfnorm > 1: return True, abs(l.obj())/gfnorm else: return True, abs(l.obj()) else: print("Waring: Crticality check fails with solver Status: " + str(results.solver.status)) print("And Termination Conditions: " + str(results.solver.termination_condition)) return False, infinity
def _construct_bundle_dual_master_model(self, ph): self._master_model = ConcreteModel() for scenario in ph._scenario_tree._scenarios: for tree_node in scenario._node_list[:-1]: new_w_variable_name = "WVAR_" + str( tree_node._name) + "_" + str(scenario._name) new_w_k_parameter_name = "WDATA_" + str( tree_node._name) + "_" + str(scenario._name) + "_K" setattr(self._master_model, new_w_variable_name, Var(tree_node._standard_variable_ids, within=Reals)) setattr( self._master_model, new_w_k_parameter_name, Param(tree_node._standard_variable_ids, within=Reals, default=0.0, mutable=True)) setattr(self._master_model, "V_" + str(scenario._name), Var(within=Reals)) # HERE - NEED TO MAKE CK VARAIBLE-DEPENDENT - PLUS WE NEED A SANE INITIAL VALUE (AND SUBSEQUENT VALUE) # DLW SAYS NO - THIS SHOULD BE VARIABLE-SPECIFIC setattr(self._master_model, "CK", Param(default=1.0, mutable=True)) def obj_rule(m): expr = 0.0 for scenario in ph._scenario_tree._scenarios: for tree_node in scenario._node_list[:-1]: new_w_variable_name = "WVAR_" + str( tree_node._name) + "_" + str(scenario._name) new_w_k_parameter_name = "WDATA_" + str( tree_node._name) + "_" + str(scenario._name) + "_K" w_variable = m.find_component(new_w_variable_name) w_k_parameter = m.find_component(new_w_k_parameter_name) expr += 1.0 / (2.0 * m.CK) * sum( w_variable[i]**2 - 2.0 * w_variable[i] * w_k_parameter[i] for i in w_variable) expr -= getattr(m, "V_" + str(scenario._name)) return expr self._master_model.TheObjective = Objective(sense=minimize, rule=obj_rule) self._master_model.W_Balance = ConstraintList() for stage in ph._scenario_tree._stages[:-1]: for tree_node in stage._tree_nodes: # GABE SHOULD HAVE A SERVICE FOR THIS??? for idx in tree_node._standard_variable_ids: expr = 0.0 for scenario in tree_node._scenarios: scenario_probability = scenario._probability new_w_variable_name = "WVAR_" + str( tree_node._name) + "_" + str(scenario._name) w_variable = self._master_model.find_component( new_w_variable_name) expr += scenario_probability * w_variable[idx] self._master_model.W_Balance.add(expr == 0.0) # we can't populate until we see data from PH.... self._master_model.V_Bound = ConstraintList()
def device_scheduler( # noqa C901 device_constraints: List[pd.DataFrame], ems_constraints: pd.DataFrame, commitment_quantities: List[pd.Series], commitment_downwards_deviation_price: Union[List[pd.Series], List[float]], commitment_upwards_deviation_price: Union[List[pd.Series], List[float]], ) -> Tuple[List[pd.Series], float, SolverResults]: """This generic device scheduler is able to handle an EMS with multiple devices, with various types of constraints on the EMS level and on the device level, and with multiple market commitments on the EMS level. A typical example is a house with many devices. The commitments are assumed to be with regard to the flow of energy to the device (positive for consumption, negative for production). The solver minimises the costs of deviating from the commitments. Device constraints are on a device level. Handled constraints (listed by column name): max: maximum stock assuming an initial stock of zero (e.g. in MWh or boxes) min: minimum stock assuming an initial stock of zero equal: exact amount of stock (we do this by clamping min and max) derivative max: maximum flow (e.g. in MW or boxes/h) derivative min: minimum flow derivative equals: exact amount of flow (we do this by clamping derivative min and derivative max) derivative down efficiency: ratio of downwards flows (flow into EMS : flow out of device) derivative up efficiency: ratio of upwards flows (flow into device : flow out of EMS) EMS constraints are on an EMS level. Handled constraints (listed by column name): derivative max: maximum flow derivative min: minimum flow Commitments are on an EMS level. Parameter explanations: commitment_quantities: amounts of flow specified in commitments (both previously ordered and newly requested) - e.g. in MW or boxes/h commitment_downwards_deviation_price: penalty for downwards deviations of the flow - e.g. in EUR/MW or EUR/(boxes/h) - either a single value (same value for each flow value) or a Series (different value for each flow value) commitment_upwards_deviation_price: penalty for upwards deviations of the flow All Series and DataFrames should have the same resolution. For now, we pass in the various constraints and prices as separate variables, from which we make a MultiIndex DataFrame. Later we could pass in a MultiIndex DataFrame directly. """ # If the EMS has no devices, don't bother if len(device_constraints) == 0: return [], 0, SolverResults() # Check if commitments have the same time window and resolution as the constraints start = device_constraints[0].index.to_pydatetime()[0] resolution = pd.to_timedelta(device_constraints[0].index.freq) end = device_constraints[0].index.to_pydatetime()[-1] + resolution if len(commitment_quantities) != 0: start_c = commitment_quantities[0].index.to_pydatetime()[0] resolution_c = pd.to_timedelta(commitment_quantities[0].index.freq) end_c = commitment_quantities[0].index.to_pydatetime()[-1] + resolution if not (start_c == start and end_c == end): raise Exception( "Not implemented for different time windows.\n(%s,%s)\n(%s,%s)" % (start, end, start_c, end_c)) if resolution_c != resolution: raise Exception( "Not implemented for different resolutions.\n%s\n%s" % (resolution, resolution_c)) # Turn prices per commitment into prices per commitment flow if len(commitment_downwards_deviation_price) != 0: if all( isinstance(price, float) for price in commitment_downwards_deviation_price): commitment_downwards_deviation_price = [ initialize_series(price, start, end, resolution) for price in commitment_downwards_deviation_price ] if len(commitment_upwards_deviation_price) != 0: if all( isinstance(price, float) for price in commitment_upwards_deviation_price): commitment_upwards_deviation_price = [ initialize_series(price, start, end, resolution) for price in commitment_upwards_deviation_price ] model = ConcreteModel() # Add indices for devices (d), datetimes (j) and commitments (c) model.d = RangeSet(0, len(device_constraints) - 1, doc="Set of devices") model.j = RangeSet(0, len(device_constraints[0].index.to_pydatetime()) - 1, doc="Set of datetimes") model.c = RangeSet(0, len(commitment_quantities) - 1, doc="Set of commitments") # Add parameters def price_down_select(m, c, j): return commitment_downwards_deviation_price[c].iloc[j] def price_up_select(m, c, j): return commitment_upwards_deviation_price[c].iloc[j] def commitment_quantity_select(m, c, j): return commitment_quantities[c].iloc[j] def device_max_select(m, d, j): max_v = device_constraints[d]["max"].iloc[j] equal_v = device_constraints[d]["equals"].iloc[j] if np.isnan(max_v) and np.isnan(equal_v): return infinity else: return np.nanmin([max_v, equal_v]) def device_min_select(m, d, j): min_v = device_constraints[d]["min"].iloc[j] equal_v = device_constraints[d]["equals"].iloc[j] if np.isnan(min_v) and np.isnan(equal_v): return -infinity else: return np.nanmax([min_v, equal_v]) def device_derivative_max_select(m, d, j): max_v = device_constraints[d]["derivative max"].iloc[j] equal_v = device_constraints[d]["derivative equals"].iloc[j] if np.isnan(max_v) and np.isnan(equal_v): return infinity else: return np.nanmin([max_v, equal_v]) def device_derivative_min_select(m, d, j): min_v = device_constraints[d]["derivative min"].iloc[j] equal_v = device_constraints[d]["derivative equals"].iloc[j] if np.isnan(min_v) and np.isnan(equal_v): return -infinity else: return np.nanmax([min_v, equal_v]) def ems_derivative_max_select(m, j): v = ems_constraints["derivative max"].iloc[j] if np.isnan(v): return infinity else: return v def ems_derivative_min_select(m, j): v = ems_constraints["derivative min"].iloc[j] if np.isnan(v): return -infinity else: return v def device_derivative_down_efficiency(m, d, j): try: return device_constraints[d]["derivative down efficiency"].iloc[j] except KeyError: return 1 def device_derivative_up_efficiency(m, d, j): try: return device_constraints[d]["derivative up efficiency"].iloc[j] except KeyError: return 1 model.up_price = Param(model.c, model.j, initialize=price_up_select) model.down_price = Param(model.c, model.j, initialize=price_down_select) model.commitment_quantity = Param(model.c, model.j, initialize=commitment_quantity_select) model.device_max = Param(model.d, model.j, initialize=device_max_select) model.device_min = Param(model.d, model.j, initialize=device_min_select) model.device_derivative_max = Param( model.d, model.j, initialize=device_derivative_max_select) model.device_derivative_min = Param( model.d, model.j, initialize=device_derivative_min_select) model.ems_derivative_max = Param(model.j, initialize=ems_derivative_max_select) model.ems_derivative_min = Param(model.j, initialize=ems_derivative_min_select) model.device_derivative_down_efficiency = Param( model.d, model.j, initialize=device_derivative_down_efficiency) model.device_derivative_up_efficiency = Param( model.d, model.j, initialize=device_derivative_up_efficiency) # Add variables model.ems_power = Var(model.d, model.j, domain=Reals, initialize=0) model.device_power_down = Var(model.d, model.j, domain=NonPositiveReals, initialize=0) model.device_power_up = Var(model.d, model.j, domain=NonNegativeReals, initialize=0) model.commitment_downwards_deviation = Var(model.c, model.j, domain=NonPositiveReals, initialize=0) model.commitment_upwards_deviation = Var(model.c, model.j, domain=NonNegativeReals, initialize=0) # Add constraints as a tuple of (lower bound, value, upper bound) def device_bounds(m, d, j): return ( m.device_min[d, j], sum(m.device_power_down[d, k] + m.device_power_up[d, k] for k in range(0, j + 1)), m.device_max[d, j], ) def device_derivative_bounds(m, d, j): return ( m.device_derivative_min[d, j], m.device_power_down[d, j] + m.device_power_up[d, j], m.device_derivative_max[d, j], ) def device_down_derivative_bounds(m, d, j): return ( m.device_derivative_min[d, j], m.device_power_down[d, j], 0, ) def device_up_derivative_bounds(m, d, j): return ( 0, m.device_power_up[d, j], m.device_derivative_max[d, j], ) def ems_derivative_bounds(m, j): return m.ems_derivative_min[j], sum( m.ems_power[:, j]), m.ems_derivative_max[j] def ems_flow_commitment_equalities(m, j): """Couple EMS flows (sum over devices) to commitments.""" return ( 0, sum(m.commitment_quantity[:, j]) + sum(m.commitment_downwards_deviation[:, j]) + sum(m.commitment_upwards_deviation[:, j]) - sum(m.ems_power[:, j]), 0, ) def device_derivative_equalities(m, d, j): """Couple device flows to EMS flows per device, applying efficiencies.""" return ( 0, m.device_power_up[d, j] / m.device_derivative_up_efficiency[d, j] + m.device_power_down[d, j] * m.device_derivative_down_efficiency[d, j] - m.ems_power[d, j], 0, ) model.device_energy_bounds = Constraint(model.d, model.j, rule=device_bounds) model.device_power_bounds = Constraint(model.d, model.j, rule=device_derivative_bounds) model.device_power_down_bounds = Constraint( model.d, model.j, rule=device_down_derivative_bounds) model.device_power_up_bounds = Constraint(model.d, model.j, rule=device_up_derivative_bounds) model.ems_power_bounds = Constraint(model.j, rule=ems_derivative_bounds) model.ems_power_commitment_equalities = Constraint( model.j, rule=ems_flow_commitment_equalities) model.device_power_equalities = Constraint( model.d, model.j, rule=device_derivative_equalities) # Add objective def cost_function(m): costs = 0 for c in m.c: for j in m.j: costs += m.commitment_downwards_deviation[c, j] * m.down_price[c, j] costs += m.commitment_upwards_deviation[c, j] * m.up_price[c, j] return costs model.costs = Objective(rule=cost_function, sense=minimize) # Solve results = SolverFactory( current_app.config.get("FLEXMEASURES_LP_SOLVER")).solve(model) planned_costs = value(model.costs) planned_power_per_device = [] for d in model.d: planned_device_power = [ model.device_power_down[d, j].value + model.device_power_up[d, j].value for j in model.j ] planned_power_per_device.append( pd.Series( index=pd.date_range(start=start, end=end, freq=to_offset(resolution), closed="left"), data=planned_device_power, )) # model.pprint() # print(results.solver.termination_condition) # print(planned_costs) # model.display() return planned_power_per_device, planned_costs, results
def test_outofbounds(self): m = ConcreteModel() m.x = Var(bounds=(-1, 5), initialize=2) with self.assertRaisesRegexp(MCPP_Error, '.*Log with negative values in range'): mc(log(m.x))
def test_cast_to_binary(self): m = ConcreteModel() m.iv = AutoLinkedBooleanVar() m.biv = AutoLinkedBinaryVar(m.iv) m.iv.associate_binary_var(m.biv) m.biv = 1 deprecation_msg = ( "Implicit conversion of the Boolean indicator_var 'iv'") out = StringIO() with LoggingIntercept(out): self.assertEqual(m.iv.lb, 0) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertEqual(m.iv.ub, 1) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertEqual(m.iv.bounds, (0, 1)) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): with self.assertRaisesRegex( AttributeError, "Assignment not allowed. Use the setlb"): m.iv.lb = 1 self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): with self.assertRaisesRegex( AttributeError, "Assignment not allowed. Use the setub"): m.iv.ub = 1 self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): with self.assertRaisesRegex( AttributeError, "Assignment not allowed. Use the " "setub and setlb"): m.iv.bounds = (1, 1) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): m.iv.setlb(1) self.assertEqual(m.biv.lb, 1) m.biv.setlb(0) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): m.iv.setub(0) self.assertEqual(m.biv.ub, 0) m.biv.setub(1) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertIs(abs(m.iv).args[0], m.biv) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertIs(bool(m.iv), True) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): with self.assertRaisesRegex( TypeError, "Implicit conversion of Pyomo NumericValue " "type `biv' to a float"): float(m.iv) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): with self.assertRaisesRegex( TypeError, "Implicit conversion of Pyomo NumericValue " "type `biv' to an integer"): int(m.iv) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertIs((-m.iv).args[1], m.biv) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertIs(+m.iv, m.biv) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertTrue(m.iv.has_lb()) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertTrue(m.iv.has_ub()) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertTrue(m.iv.is_binary()) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertFalse(m.iv.is_continuous()) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertTrue(m.iv.is_integer()) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertEqual(m.iv.polynomial_degree(), 1) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertIs((m.iv == 0).args[0], m.biv) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertIs((m.iv <= 0).args[0], m.biv) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertIs((m.iv >= 0).args[1], m.biv) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertIs((m.iv < 0).args[0], m.biv) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertIs((m.iv > 0).args[1], m.biv) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertIs((m.iv + 1).args[0], m.biv) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertIs((m.iv - 1).args[0], m.biv) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertIs((m.iv * 2).args[1], m.biv) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertIs((m.iv / 2).args[1], m.biv) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertIs((m.iv**2).args[0], m.biv) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertIs((1 + m.iv).args[1], m.biv) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertIs((1 - m.iv).args[1].args[1], m.biv) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertIs((2 * m.iv).args[1], m.biv) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertIs((2 / m.iv).args[1], m.biv) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): self.assertIs((2**m.iv).args[1], m.biv) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): a = m.iv a += 1 self.assertIs(a.args[0], m.biv) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): a = m.iv a -= 1 self.assertIs(a.args[0], m.biv) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): a = m.iv a *= 2 self.assertIs(a.args[1], m.biv) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): a = m.iv a /= 2 self.assertIs(a.args[1], m.biv) self.assertIn(deprecation_msg, out.getvalue()) out = StringIO() with LoggingIntercept(out): a = m.iv a **= 2 self.assertIs(a.args[0], m.biv) self.assertIn(deprecation_msg, out.getvalue())
def test_synchronize_value(self): m = ConcreteModel() m.iv = AutoLinkedBooleanVar() m.biv = AutoLinkedBinaryVar(m.iv) m.iv.associate_binary_var(m.biv) self.assertIsNone(m.iv.value) self.assertIsNone(m.biv.value) # Note: test the following twice to exercise the "no update" # situation, and to ensure no infinite loops m.iv = True self.assertEqual(m.iv.value, True) self.assertEqual(m.biv.value, 1) m.iv = True self.assertEqual(m.iv.value, True) self.assertEqual(m.biv.value, 1) m.iv = False self.assertEqual(m.iv.value, False) self.assertEqual(m.biv.value, 0) m.iv = False self.assertEqual(m.iv.value, False) self.assertEqual(m.biv.value, 0) m.iv = None self.assertEqual(m.iv.value, None) self.assertEqual(m.biv.value, None) m.iv = None self.assertEqual(m.iv.value, None) self.assertEqual(m.biv.value, None) m.biv = 1 self.assertEqual(m.iv.value, True) self.assertEqual(m.biv.value, 1) m.biv = 1 self.assertEqual(m.iv.value, True) self.assertEqual(m.biv.value, 1) eps = AutoLinkedBinaryVar.INTEGER_TOLERANCE / 10 m.biv = None self.assertEqual(m.iv.value, None) self.assertEqual(m.biv.value, None) m.biv = None self.assertEqual(m.iv.value, None) self.assertEqual(m.biv.value, None) m.biv.value = 1 - eps self.assertEqual(m.iv.value, True) self.assertEqual(m.biv.value, 1 - eps) m.biv.value = 1 - eps self.assertEqual(m.iv.value, True) self.assertEqual(m.biv.value, 1 - eps) m.biv.value = eps self.assertEqual(m.iv.value, False) self.assertEqual(m.biv.value, eps) m.biv.value = eps self.assertEqual(m.iv.value, False) self.assertEqual(m.biv.value, eps) m.biv.value = 0.5 self.assertEqual(m.iv.value, None) self.assertEqual(m.biv.value, 0.5) m.biv.value = 0.5 self.assertEqual(m.iv.value, None) self.assertEqual(m.biv.value, 0.5)
def test_deactivate(self): m = ConcreteModel() m.x = Var() m.d1 = Disjunct() m.d1.constraint = Constraint(expr=m.x <= 0) m.d = Disjunction(expr=[m.d1, m.x >= 1, m.x >= 5]) d2 = m.d.disjuncts[1].parent_component() self.assertEqual(len(m.component_map(Disjunction)), 1) self.assertEqual(len(m.component_map(Disjunct)), 2) self.assertIsNot(m.d1, d2) self.assertTrue(m.d1.active) self.assertTrue(d2.active) self.assertTrue(m.d.disjuncts[0].active) self.assertTrue(m.d.disjuncts[1].active) self.assertTrue(m.d.disjuncts[2].active) self.assertFalse(m.d.disjuncts[0].indicator_var.is_fixed()) self.assertFalse(m.d.disjuncts[1].indicator_var.is_fixed()) self.assertFalse(m.d.disjuncts[2].indicator_var.is_fixed()) m.d.disjuncts[0].deactivate() self.assertFalse(m.d1.active) self.assertTrue(d2.active) self.assertFalse(m.d.disjuncts[0].active) self.assertTrue(m.d.disjuncts[1].active) self.assertTrue(m.d.disjuncts[2].active) self.assertTrue(m.d.disjuncts[0].indicator_var.is_fixed()) self.assertFalse(m.d.disjuncts[1].indicator_var.is_fixed()) self.assertFalse(m.d.disjuncts[2].indicator_var.is_fixed()) m.d.disjuncts[1].deactivate() self.assertFalse(m.d1.active) self.assertTrue(d2.active) self.assertFalse(m.d.disjuncts[0].active) self.assertFalse(m.d.disjuncts[1].active) self.assertTrue(m.d.disjuncts[2].active) self.assertTrue(m.d.disjuncts[0].indicator_var.is_fixed()) self.assertTrue(m.d.disjuncts[1].indicator_var.is_fixed()) self.assertFalse(m.d.disjuncts[2].indicator_var.is_fixed()) d2.deactivate() self.assertFalse(m.d1.active) self.assertFalse(d2.active) self.assertFalse(m.d.disjuncts[0].active) self.assertFalse(m.d.disjuncts[1].active) self.assertFalse(m.d.disjuncts[2].active) self.assertTrue(m.d.disjuncts[0].indicator_var.is_fixed()) self.assertTrue(m.d.disjuncts[1].indicator_var.is_fixed()) self.assertTrue(m.d.disjuncts[2].indicator_var.is_fixed()) m.d.disjuncts[2].activate() self.assertFalse(m.d1.active) self.assertTrue(d2.active) self.assertFalse(m.d.disjuncts[0].active) self.assertFalse(m.d.disjuncts[1].active) self.assertTrue(m.d.disjuncts[2].active) self.assertTrue(m.d.disjuncts[0].indicator_var.is_fixed()) self.assertTrue(m.d.disjuncts[1].indicator_var.is_fixed()) self.assertFalse(m.d.disjuncts[2].indicator_var.is_fixed()) d2.activate() self.assertFalse(m.d1.active) self.assertTrue(d2.active) self.assertFalse(m.d.disjuncts[0].active) self.assertTrue(m.d.disjuncts[1].active) self.assertTrue(m.d.disjuncts[2].active) self.assertTrue(m.d.disjuncts[0].indicator_var.is_fixed()) self.assertFalse(m.d.disjuncts[1].indicator_var.is_fixed()) self.assertFalse(m.d.disjuncts[2].indicator_var.is_fixed()) m.d1.activate() self.assertTrue(m.d1.active) self.assertTrue(d2.active) self.assertTrue(m.d.disjuncts[0].active) self.assertTrue(m.d.disjuncts[1].active) self.assertTrue(m.d.disjuncts[2].active) self.assertFalse(m.d.disjuncts[0].indicator_var.is_fixed()) self.assertFalse(m.d.disjuncts[1].indicator_var.is_fixed()) self.assertFalse(m.d.disjuncts[2].indicator_var.is_fixed())
def generate_structured_model(self): """ Using the community map and the original model used to create this community map, we will create structured_model, which will be based on the original model but will place variables, constraints, and objectives into or outside of various blocks (communities) based on the community map. Returns ------- structured_model: Block a Pyomo model that reflects the nature of the community map """ # Initialize a new model (structured_model) which will contain variables and constraints in blocks based on # their respective communities within the CommunityMap structured_model = ConcreteModel() # Create N blocks (where N is the number of communities found within the model) structured_model.b = Block([0, len(self.community_map) - 1, 1]) # values given for (start, stop, step) # Initialize a ComponentMap that will map a variable from the model (for example, old_model.x1) used to # create the CommunityMap to a list of variables in various blocks that were created based on this # variable (for example, [structured_model.b[0].x1, structured_model.b[3].x1]) blocked_variable_map = ComponentMap() # Example key-value pair -> {original_model.x1 : [structured_model.b[0].x1, structured_model.b[3].x1]} # TODO - Consider changing structure of the next two for loops to be more efficient (maybe loop through # constraints and add variables as you go) (but note that disconnected variables would be # missed with this strategy) # First loop through community_map to add all the variables to structured_model before we add constraints # that use those variables for community_key, community in self.community_map.items(): _, variables_in_community = community # Loop through all of the variables (from the original model) in the given community for stored_variable in variables_in_community: # Construct a new_variable whose attributes are determined by querying the variable from the # original model new_variable = Var(domain=stored_variable.domain, bounds=stored_variable.bounds) # Add this new_variable to its block/community and name it using the string of the variable from the # original model structured_model.b[community_key].add_component(str(stored_variable), new_variable) # Since there could be multiple variables 'x1' (such as # structured_model.b[0].x1, structured_model.b[3].x1, etc), we need to create equality constraints # for all of the variables 'x1' within structured_model (this is the purpose of blocked_variable_map) # Here we update blocked_variable_map to keep track of what equality constraints need to be made variable_in_new_model = structured_model.find_component(new_variable) blocked_variable_map[stored_variable] = blocked_variable_map.get(stored_variable, []) + [variable_in_new_model] # Now that we have all of our variables within the model, we will initialize a dictionary that used to # replace variables within constraints to other variables (in our case, this will convert variables from the # original model into variables from the new model (structured_model)) replace_variables_in_expression_map = dict() # Loop through community_map again, this time to add constraints (with replaced variables) for community_key, community in self.community_map.items(): constraints_in_community, _ = community # Loop through all of the constraints (from the original model) in the given community for stored_constraint in constraints_in_community: # Now, loop through all of the variables within the given constraint expression for variable_in_stored_constraint in identify_variables(stored_constraint.expr): # Loop through each of the "blocked" variables that a variable is mapped to and update # replace_variables_in_expression_map if a variable has a "blocked" form in the given community # What this means is that if we are looping through constraints in community 0, then it would be # best to change a variable x1 into b[0].x1 as opposed to b[2].x1 or b[5].x1 (assuming all of these # blocked versions of the variable x1 exist (which depends on the community map)) variable_in_current_block = False for blocked_variable in blocked_variable_map[variable_in_stored_constraint]: if 'b[%d]' % community_key in str(blocked_variable): # Update replace_variables_in_expression_map accordingly replace_variables_in_expression_map[id(variable_in_stored_constraint)] = blocked_variable variable_in_current_block = True if not variable_in_current_block: # Create a version of the given variable outside of blocks then add it to # replace_variables_in_expression_map new_variable = Var(domain=variable_in_stored_constraint.domain, bounds=variable_in_stored_constraint.bounds) # Add the new variable just as we did above (but now it is not in any blocks) structured_model.add_component(str(variable_in_stored_constraint), new_variable) # Update blocked_variable_map to keep track of what equality constraints need to be made variable_in_new_model = structured_model.find_component(new_variable) blocked_variable_map[variable_in_stored_constraint] = blocked_variable_map.get( variable_in_stored_constraint, []) + [variable_in_new_model] # Update replace_variables_in_expression_map accordingly replace_variables_in_expression_map[id(variable_in_stored_constraint)] = variable_in_new_model # TODO - Is there a better way to check whether something is actually an objective? (as done below) # Check to see whether 'stored_constraint' is actually an objective (since constraints and objectives # grouped together) if self.with_objective and isinstance(stored_constraint, (_GeneralObjectiveData, Objective)): # If the constraint is actually an objective, we add it to the block as an objective new_objective = Objective( expr=replace_expressions(stored_constraint.expr, replace_variables_in_expression_map)) structured_model.b[community_key].add_component(str(stored_constraint), new_objective) else: # Construct a constraint based on the expression within stored_constraint and the dict we have # created for the purpose of replacing the variables within the constraint expression new_constraint = Constraint( expr=replace_expressions(stored_constraint.expr, replace_variables_in_expression_map)) # Add this new constraint to the corresponding community/block with its name as the string of the # constraint from the original model structured_model.b[community_key].add_component(str(stored_constraint), new_constraint) # If with_objective was set to False, that means we might have missed an objective function within the # original model if not self.with_objective: # Construct a new dictionary for replacing the variables (replace_variables_in_objective_map) which will # be specific to the variables in the objective function, since there is the possibility that the # objective contains variables we have not yet seen (and thus not yet added to our new model) for objective_function in self.model.component_data_objects(ctype=Objective, active=self.use_only_active_components, descend_into=True): for variable_in_objective in identify_variables(objective_function): # Add all of the variables in the objective function (not within any blocks) # Check to make sure a form of the variable has not already been made outside of the blocks if structured_model.find_component(str(variable_in_objective)) is None: new_variable = Var(domain=variable_in_objective.domain, bounds=variable_in_objective.bounds) structured_model.add_component(str(variable_in_objective), new_variable) # Again we update blocked_variable_map to keep track of what # equality constraints need to be made variable_in_new_model = structured_model.find_component(new_variable) blocked_variable_map[variable_in_objective] = blocked_variable_map.get( variable_in_objective, []) + [variable_in_new_model] # Update the dictionary that we will use to replace the variables replace_variables_in_expression_map[id(variable_in_objective)] = variable_in_new_model else: for version_of_variable in blocked_variable_map[variable_in_objective]: if 'b[' not in str(version_of_variable): replace_variables_in_expression_map[id(variable_in_objective)] = version_of_variable # Now we will construct a new objective function based on the one from the original model and then # add it to the new model just as we have done before new_objective = Objective( expr=replace_expressions(objective_function.expr, replace_variables_in_expression_map)) structured_model.add_component(str(objective_function), new_objective) # Now, we need to create equality constraints for all of the different "versions" of a variable (such # as x1, b[0].x1, b[2].x2, etc.) # Create a constraint list for the equality constraints structured_model.equality_constraint_list = ConstraintList(doc="Equality Constraints for the different " "forms of a given variable") # Loop through blocked_variable_map and create constraints accordingly for variable, duplicate_variables in blocked_variable_map.items(): # variable -> variable from the original model # duplicate_variables -> list of variables in the new model # Create a list of all the possible equality constraints that need to be made equalities_to_make = combinations(duplicate_variables, 2) # Loop through the list of two-variable tuples and create an equality constraint for those two variables for variable_1, variable_2 in equalities_to_make: structured_model.equality_constraint_list.add(expr=variable_1 == variable_2) # Return 'structured_model', which is essentially identical to the original model but now has all of the # variables, constraints, and objectives placed into blocks based on the nature of the CommunityMap return structured_model
def run_pyomo(options=Options(), parser=None): data = Options(options=options) if options.model.filename == '': parser.print_help() return Container() try: pyomo.scripting.util.setup_environment(data) pyomo.scripting.util.apply_preprocessing(data, parser=parser) except: # TBD: I should be able to call this function in the case of # an exception to perform cleanup. However, as it stands # calling finalize with its default keyword value for # model(=None) results in an a different error related to # task port values. Not sure how to interpret that. pyomo.scripting.util.finalize(data, model=ConcreteModel(), instance=None, results=None) raise else: if data.error: # TBD: I should be able to call this function in the case of # an exception to perform cleanup. However, as it stands # calling finalize with its default keyword value for # model(=None) results in an a different error related to # task port values. Not sure how to interpret that. pyomo.scripting.util.finalize(data, model=ConcretModel(), instance=None, results=None) return Container() #pragma:nocover try: model_data = pyomo.scripting.util.create_model(data) except: # TBD: I should be able to call this function in the case of # an exception to perform cleanup. However, as it stands # calling finalize with its default keyword value for # model(=None) results in an a different error related to # task port values. Not sure how to interpret that. pyomo.scripting.util.finalize(data, model=ConcreteModel(), instance=None, results=None) raise else: if (((not options.runtime.logging == 'debug') and \ options.model.save_file) or \ options.runtime.only_instance): pyomo.scripting.util.finalize(data, model=model_data.model, instance=model_data.instance, results=None) return Container(instance=model_data.instance) try: opt_data = pyomo.scripting.util.apply_optimizer( data, instance=model_data.instance) pyomo.scripting.util.process_results(data, instance=model_data.instance, results=opt_data.results, opt=opt_data.opt) pyomo.scripting.util.apply_postprocessing(data, instance=model_data.instance, results=opt_data.results) except: # TBD: I should be able to call this function in the case of # an exception to perform cleanup. However, as it stands # calling finalize with its default keyword value for # model(=None) results in an a different error related to # task port values. Not sure how to interpret that. pyomo.scripting.util.finalize(data, model=ConcreteModel(), instance=None, results=None) raise else: pyomo.scripting.util.finalize(data, model=model_data.model, instance=model_data.instance, results=opt_data.results) return Container(options=options, instance=model_data.instance, results=opt_data.results, local=opt_data.local)
def define_model(**kwds): model = ConcreteModel() model.x1 = Var(bounds=(-5, 4)) # domain variable model.x2 = Var(bounds=(-5, 4)) # domain variable model.x3 = Var(bounds=(-5, 4)) # domain variable model.x4 = Var(bounds=(-5, 4)) # domain variable model.x5 = Var(bounds=(-5, 4)) # domain variable model.x6 = Var(bounds=(-5, 4)) # domain variable model.x7 = Var(bounds=(-5, 4)) # domain variable model.Fx1 = Var() # range variable model.Fx2 = Var() # range variable model.Fx3 = Var() # range variable model.Fx4 = Var() # range variable model.Fx5 = Var() # range variable model.Fx6 = Var() # range variable model.Fx7 = Var() # range variable model.obj = Objective(expr=model.Fx1 + model.Fx2 + model.Fx3 + model.Fx4 + model.Fx5 + model.Fx6 + model.Fx7, sense=kwds.pop('sense', maximize)) model.piecewise1 = Piecewise(model.Fx1, model.x1, pw_pts=DOMAIN_PTS, f_rule=F, **kwds) model.piecewise2 = Piecewise(model.Fx2, model.x2, pw_pts=DOMAIN_PTS, f_rule=F, **kwds) model.piecewise3 = Piecewise(model.Fx3, model.x3, pw_pts=DOMAIN_PTS, f_rule=F, **kwds) model.piecewise4 = Piecewise(model.Fx4, model.x4, pw_pts=DOMAIN_PTS, f_rule=F, **kwds) model.piecewise5 = Piecewise(model.Fx5, model.x5, pw_pts=DOMAIN_PTS, f_rule=F, **kwds) model.piecewise6 = Piecewise(model.Fx6, model.x6, pw_pts=DOMAIN_PTS, f_rule=F, **kwds) model.piecewise7 = Piecewise(model.Fx7, model.x7, pw_pts=DOMAIN_PTS, f_rule=F, **kwds) #Fix the answer for testing purposes model.set_answer_constraint1 = Constraint(expr=model.x1 == -5.0) model.set_answer_constraint2 = Constraint(expr=model.x2 == -3.0) model.set_answer_constraint3 = Constraint(expr=model.x3 == -2.5) model.set_answer_constraint4 = Constraint(expr=model.x4 == -1.5) model.set_answer_constraint5 = Constraint(expr=model.x5 == 2.0) model.set_answer_constraint6 = Constraint(expr=model.x6 == 3.5) model.set_answer_constraint7 = Constraint(expr=model.x7 == 4.0) return model
def get_mock_model(self): model = ConcreteModel() model.x = Var(within=Binary) model.con = Constraint(expr=model.x >= 1) model.obj = Objective(expr=model.x) return model
def create_ef_instance(scenario_tree, ef_instance_name="MASTER", verbose_output=False, generate_weighted_cvar=False, cvar_weight=None, risk_alpha=None, cc_indicator_var_name=None, cc_alpha=0.0): # # create the new and empty binding instance. # # scenario tree must be "linked" with a set of instances # to used this function scenario_instances = {} for scenario in scenario_tree.scenarios: if scenario._instance is None: raise ValueError("Cannot construct extensive form instance. " "The scenario tree does not appear to be linked " "to any Pyomo models. Missing model for scenario " "with name: %s" % (scenario.name)) scenario_instances[scenario.name] = scenario._instance binding_instance = ConcreteModel(name=ef_instance_name) root_node = scenario_tree.findRootNode() opt_sense = minimize \ if (scenario_tree._scenarios[0]._instance_objective.is_minimizing()) \ else maximize # # validate cvar options, if specified. # cvar_excess_vardatas = [] if generate_weighted_cvar: if (cvar_weight is None) or (cvar_weight < 0.0): raise RuntimeError( "Weight of CVaR term must be >= 0.0 - value supplied=" + str(cvar_weight)) if (risk_alpha is None) or (risk_alpha <= 0.0) or (risk_alpha >= 1.0): raise RuntimeError( "CVaR risk alpha must be between 0 and 1, exclusive - value supplied=" + str(risk_alpha)) if verbose_output: print("Writing CVaR weighted objective") print("CVaR term weight=" + str(cvar_weight)) print("CVaR alpha=" + str(risk_alpha)) print("") # create the eta and excess variable on a per-scenario basis, # in addition to the constraint relating to the two. cvar_eta_variable_name = "CVAR_ETA_" + str(root_node._name) cvar_eta_variable = Var() binding_instance.add_component(cvar_eta_variable_name, cvar_eta_variable) excess_var_domain = NonNegativeReals if (opt_sense == minimize) else \ NonPositiveReals compute_excess_constraint = \ binding_instance.COMPUTE_SCENARIO_EXCESS = \ ConstraintList() for scenario in scenario_tree._scenarios: cvar_excess_variable_name = "CVAR_EXCESS_" + scenario._name cvar_excess_variable = Var(domain=excess_var_domain) binding_instance.add_component(cvar_excess_variable_name, cvar_excess_variable) compute_excess_expression = cvar_excess_variable compute_excess_expression -= scenario._instance_cost_expression compute_excess_expression += cvar_eta_variable if opt_sense == maximize: compute_excess_expression *= -1 compute_excess_constraint.add( (0.0, compute_excess_expression, None)) cvar_excess_vardatas.append( (cvar_excess_variable, scenario._probability)) # the individual scenario instances are sub-blocks of the binding instance. for scenario in scenario_tree._scenarios: scenario_instance = scenario_instances[scenario._name] binding_instance.add_component(str(scenario._name), scenario_instance) # Now deactivate the scenario instance Objective since we are creating # a new master objective scenario._instance_objective.deactivate() # walk the scenario tree - create variables representing the # common values for all scenarios associated with that node, along # with equality constraints to enforce non-anticipativity. also # create expected cost variables for each node, to be computed via # constraints/objectives defined in a subsequent pass. master # variables are created for all nodes but those in the last # stage. expected cost variables are, for no particularly good # reason other than easy coding, created for nodes in all stages. if verbose_output: print("Creating variables for master binding instance") _cmap = binding_instance.MASTER_CONSTRAINT_MAP = ComponentMap() for stage in scenario_tree._stages[:-1]: # skip the leaf stage for tree_node in stage._tree_nodes: # create the master blending variable and constraints for this node master_blend_variable_name = \ "MASTER_BLEND_VAR_"+str(tree_node._name) master_blend_constraint_name = \ "MASTER_BLEND_CONSTRAINT_"+str(tree_node._name) # don't create master variables for derived # stage variables as they will not be used in # the problem, and their values would likely # never be consistent with what is stored on the # scenario variables master_variable_index = Set( initialize=sorted(tree_node._standard_variable_ids), ordered=True, name=master_blend_variable_name + "_index") binding_instance.add_component( master_blend_variable_name + "_index", master_variable_index) master_variable = Var(master_variable_index, name=master_blend_variable_name) binding_instance.add_component(master_blend_variable_name, master_variable) master_constraint = ConstraintList( name=master_blend_constraint_name) binding_instance.add_component(master_blend_constraint_name, master_constraint) tree_node_variable_datas = tree_node._variable_datas for variable_id in sorted(tree_node._standard_variable_ids): master_vardata = master_variable[variable_id] vardatas = tree_node_variable_datas[variable_id] # Don't blend fixed variables if not tree_node.is_variable_fixed(variable_id): for scenario_vardata, scenario_probability in vardatas: _cmap[scenario_vardata] = master_constraint.add( (master_vardata - scenario_vardata, 0.0)) if generate_weighted_cvar: cvar_cost_expression_name = "CVAR_COST_" + str(root_node._name) cvar_cost_expression = Expression(name=cvar_cost_expression_name) binding_instance.add_component(cvar_cost_expression_name, cvar_cost_expression) # create an expression to represent the expected cost at the root node binding_instance.EF_EXPECTED_COST = \ Expression(initialize=sum(scenario._probability * \ scenario._instance_cost_expression for scenario in scenario_tree._scenarios)) opt_expression = \ binding_instance.MASTER_OBJECTIVE_EXPRESSION = \ Expression(initialize=binding_instance.EF_EXPECTED_COST) if generate_weighted_cvar: cvar_cost_expression_name = "CVAR_COST_" + str(root_node._name) cvar_cost_expression = \ binding_instance.find_component(cvar_cost_expression_name) if cvar_weight == 0.0: # if the cvar weight is 0, then we're only # doing cvar - no mean. opt_expression.set_value(cvar_cost_expression) else: opt_expression.expr += cvar_weight * cvar_cost_expression binding_instance.MASTER = Objective(sense=opt_sense, expr=opt_expression) # CVaR requires the addition of a variable per scenario to # represent the cost excess, and a constraint to compute the cost # excess relative to eta. if generate_weighted_cvar: # add the constraint to compute the master CVaR variable value. iterate # over scenario instances to create the expected excess component first. cvar_cost_expression_name = "CVAR_COST_" + str(root_node._name) cvar_cost_expression = binding_instance.find_component( cvar_cost_expression_name) cvar_eta_variable_name = "CVAR_ETA_" + str(root_node._name) cvar_eta_variable = binding_instance.find_component( cvar_eta_variable_name) cost_expr = 1.0 for scenario_excess_vardata, scenario_probability in cvar_excess_vardatas: cost_expr += (scenario_probability * scenario_excess_vardata) cost_expr /= (1.0 - risk_alpha) cost_expr += cvar_eta_variable cvar_cost_expression.set_value(cost_expr) if cc_indicator_var_name is not None: if verbose_output is True: print("Creating chance constraint for indicator variable= " + cc_indicator_var_name) print("with alpha= " + str(cc_alpha)) if not isVariableNameIndexed(cc_indicator_var_name): cc_expression = 0 #?????? for scenario in scenario_tree._scenarios: scenario_instance = scenario_instances[scenario._name] scenario_probability = scenario._probability cc_var = scenario_instance.find_component( cc_indicator_var_name) cc_expression += scenario_probability * cc_var def makeCCRule(expression): def CCrule(model): return (1.0 - cc_alpha, cc_expression, None) return CCrule cc_constraint_name = "cc_" + cc_indicator_var_name cc_constraint = Constraint(name=cc_constraint_name, rule=makeCCRule(cc_expression)) binding_instance.add_component(cc_constraint_name, cc_constraint) else: print("multiple cc not yet supported.") variable_name, index_template = extractVariableNameAndIndex( cc_indicator_var_name) # verify that the root variable exists and grab it. # NOTE: we are using whatever scenario happens to laying around... it might be better to use the reference variable = scenario_instance.find_component(variable_name) if variable is None: raise RuntimeError("Unknown variable=" + variable_name + " referenced as the CC indicator variable.") # extract all "real", i.e., fully specified, indices matching the index template. match_indices = extractComponentIndices(variable, index_template) # there is a possibility that no indices match the input template. # if so, let the user know about it. if len(match_indices) == 0: raise RuntimeError("No indices match template=" + str(index_template) + " for variable=" + variable_name) # add the suffix to all variable values identified. for index in match_indices: variable_value = variable[index] cc_expression = 0 #?????? for scenario in scenario_tree._scenarios: scenario_instance = scenario_instances[scenario._name] scenario_probability = scenario._probability cc_var = scenario_instance.find_component( variable_name)[index] cc_expression += scenario_probability * cc_var def makeCCRule(expression): def CCrule(model): return (1.0 - cc_alpha, cc_expression, None) return CCrule indexasname = '' for c in str(index): if c not in ' ,': indexasname += c cc_constraint_name = "cc_" + variable_name + "_" + indexasname cc_constraint = Constraint(name=cc_constraint_name, rule=makeCCRule(cc_expression)) binding_instance.add_component(cc_constraint_name, cc_constraint) return binding_instance
def test_model_with_unrelated_nonlinear_expressions(self): m = ConcreteModel() m.x = Var([1, 2, 3], bounds=(0, 3)) m.y = Var() m.z = Var() @m.Constraint([1, 2]) def cons(m, i): return m.x[i] <= m.y**i m.cons2 = Constraint(expr=m.x[1] >= m.y) m.cons3 = Constraint(expr=m.x[2] >= m.z - 3) # This is vacuous, but I just want something that's not quadratic m.cons4 = Constraint(expr=m.x[3] <= log(m.y + 1)) TransformationFactory('contrib.fourier_motzkin_elimination').\ apply_to(m, vars_to_eliminate=m.x) constraints = m._pyomo_contrib_fme_transformation.projected_constraints # 0 <= y <= 3 cons = constraints[6] self.assertEqual(cons.lower, 0) self.assertIs(cons.body, m.y) cons = constraints[5] self.assertEqual(cons.lower, -3) body = generate_standard_repn(cons.body) self.assertTrue(body.is_linear()) self.assertEqual(len(body.linear_vars), 1) self.assertIs(body.linear_vars[0], m.y) self.assertEqual(body.linear_coefs[0], -1) # z <= y**2 + 3 cons = constraints[4] self.assertEqual(cons.lower, -3) body = generate_standard_repn(cons.body) self.assertTrue(body.is_quadratic()) self.assertEqual(len(body.linear_vars), 1) self.assertIs(body.linear_vars[0], m.z) self.assertEqual(body.linear_coefs[0], -1) self.assertEqual(len(body.quadratic_vars), 1) self.assertEqual(body.quadratic_coefs[0], 1) self.assertIs(body.quadratic_vars[0][0], m.y) self.assertIs(body.quadratic_vars[0][1], m.y) # z <= 6 cons = constraints[2] self.assertEqual(cons.lower, -6) body = generate_standard_repn(cons.body) self.assertTrue(body.is_linear()) self.assertEqual(len(body.linear_vars), 1) self.assertEqual(body.linear_coefs[0], -1) self.assertIs(body.linear_vars[0], m.z) # 0 <= ln(y+ 1) cons = constraints[1] self.assertEqual(cons.lower, 0) body = generate_standard_repn(cons.body) self.assertTrue(body.is_nonlinear()) self.assertFalse(body.is_quadratic()) self.assertEqual(len(body.linear_vars), 0) self.assertEqual(body.nonlinear_expr.name, 'log') self.assertEqual(len(body.nonlinear_expr.args[0].args), 2) self.assertIs(body.nonlinear_expr.args[0].args[0], m.y) self.assertEqual(body.nonlinear_expr.args[0].args[1], 1) # 0 <= y**2 cons = constraints[3] self.assertEqual(cons.lower, 0) body = generate_standard_repn(cons.body) self.assertTrue(body.is_quadratic()) self.assertEqual(len(body.quadratic_vars), 1) self.assertEqual(body.quadratic_coefs[0], 1) self.assertIs(body.quadratic_vars[0][0], m.y) self.assertIs(body.quadratic_vars[0][1], m.y) # check constraints valid for a selection of points (this is nonconvex, # but anyway...) pts = [ #(sqrt(3), 6), Not numerically stable enough for this test (1, 4), (3, 6), (3, 0), (0, 0), (2, 6) ] for pt in pts: m.y.fix(pt[0]) m.z.fix(pt[1]) for i in constraints: self.assertLessEqual(value(constraints[i].lower), value(constraints[i].body))
def test_construct_implicit_disjuncts(self): m = ConcreteModel() m.x = Var() m.y = Var() m.d = Disjunction(expr=[m.x <= 0, m.y >= 1]) self.assertEqual(len(m.component_map(Disjunction)), 1) self.assertEqual(len(m.component_map(Disjunct)), 1) implicit_disjuncts = list(iterkeys(m.component_map(Disjunct))) self.assertEqual(implicit_disjuncts[0][:2], "d_") disjuncts = m.d.disjuncts self.assertEqual(len(disjuncts), 2) self.assertIs(disjuncts[0].parent_block(), m) self.assertIs(disjuncts[0].constraint[1].body, m.x) self.assertIs(disjuncts[1].parent_block(), m) self.assertIs(disjuncts[1].constraint[1].body, m.y) # Test that the implicit disjuncts get a unique name m.add_component('e_disjuncts', Var()) m.e = Disjunction(expr=[m.y <= 0, m.x >= 1]) self.assertEqual(len(m.component_map(Disjunction)), 2) self.assertEqual(len(m.component_map(Disjunct)), 2) implicit_disjuncts = list(iterkeys(m.component_map(Disjunct))) self.assertEqual(implicit_disjuncts[1][:12], "e_disjuncts_") disjuncts = m.e.disjuncts self.assertEqual(len(disjuncts), 2) self.assertIs(disjuncts[0].parent_block(), m) self.assertIs(disjuncts[0].constraint[1].body, m.y) self.assertIs(disjuncts[1].parent_block(), m) self.assertIs(disjuncts[1].constraint[1].body, m.x) self.assertEqual(len(disjuncts[0].parent_component().name), 13) self.assertEqual(disjuncts[0].name[:12], "e_disjuncts_") # Test that the implicit disjuncts can be lists/tuples/generators def _gen(): yield m.y <= 4 yield m.x >= 5 m.f = Disjunction(expr=[[m.y <= 0, m.x >= 1], ( m.y <= 2, m.x >= 3), _gen()]) self.assertEqual(len(m.component_map(Disjunction)), 3) self.assertEqual(len(m.component_map(Disjunct)), 3) implicit_disjuncts = list(iterkeys(m.component_map(Disjunct))) self.assertEqual(implicit_disjuncts[2][:12], "f_disjuncts") disjuncts = m.f.disjuncts self.assertEqual(len(disjuncts), 3) self.assertIs(disjuncts[0].parent_block(), m) self.assertIs(disjuncts[0].constraint[1].body, m.y) self.assertEqual(disjuncts[0].constraint[1].upper, 0) self.assertIs(disjuncts[0].constraint[2].body, m.x) self.assertEqual(disjuncts[0].constraint[2].lower, 1) self.assertIs(disjuncts[1].parent_block(), m) self.assertIs(disjuncts[1].constraint[1].body, m.y) self.assertEqual(disjuncts[1].constraint[1].upper, 2) self.assertIs(disjuncts[1].constraint[2].body, m.x) self.assertEqual(disjuncts[1].constraint[2].lower, 3) self.assertIs(disjuncts[2].parent_block(), m) self.assertIs(disjuncts[2].constraint[1].body, m.y) self.assertEqual(disjuncts[2].constraint[1].upper, 4) self.assertIs(disjuncts[2].constraint[2].body, m.x) self.assertEqual(disjuncts[2].constraint[2].lower, 5) self.assertEqual(len(disjuncts[0].parent_component().name), 11) self.assertEqual(disjuncts[0].name, "f_disjuncts[0]")
def test_model_with_unrelated_nonlinear_expressions(self): m = ConcreteModel() m.x = Var([1, 2, 3], bounds=(0, 3)) m.y = Var() m.z = Var() @m.Constraint([1, 2]) def cons(m, i): return m.x[i] <= m.y**i m.cons2 = Constraint(expr=m.x[1] >= m.y) m.cons3 = Constraint(expr=m.x[2] >= m.z - 3) # This is vacuous, but I just want something that's not quadratic m.cons4 = Constraint(expr=m.x[3] <= log(m.y + 1)) fme = TransformationFactory('contrib.fourier_motzkin_elimination') fme.apply_to(m, vars_to_eliminate=m.x, projected_constraints_name='projected_constraints', constraint_filtering_callback=None) constraints = m.projected_constraints # 0 <= y <= 3 cons = constraints[5] self.assertEqual(value(cons.lower), 0) self.assertIs(cons.body, m.y) cons = constraints[6] self.assertEqual(value(cons.lower), -3) body = generate_standard_repn(cons.body) self.assertTrue(body.is_linear()) self.assertEqual(len(body.linear_vars), 1) self.assertIs(body.linear_vars[0], m.y) self.assertEqual(body.linear_coefs[0], -1) # z <= y**2 + 3 cons = constraints[2] self.assertEqual(value(cons.lower), -3) body = generate_standard_repn(cons.body) self.assertTrue(body.is_quadratic()) self.assertEqual(len(body.linear_vars), 1) self.assertIs(body.linear_vars[0], m.z) self.assertEqual(body.linear_coefs[0], -1) self.assertEqual(len(body.quadratic_vars), 1) self.assertEqual(body.quadratic_coefs[0], 1) self.assertIs(body.quadratic_vars[0][0], m.y) self.assertIs(body.quadratic_vars[0][1], m.y) # z <= 6 cons = constraints[4] self.assertEqual(cons.lower, -6) body = generate_standard_repn(cons.body) self.assertTrue(body.is_linear()) self.assertEqual(len(body.linear_vars), 1) self.assertEqual(body.linear_coefs[0], -1) self.assertIs(body.linear_vars[0], m.z) # 0 <= ln(y+ 1) cons = constraints[1] self.assertEqual(value(cons.lower), 0) body = generate_standard_repn(cons.body) self.assertTrue(body.is_nonlinear()) self.assertFalse(body.is_quadratic()) self.assertEqual(len(body.linear_vars), 0) self.assertEqual(body.nonlinear_expr.name, 'log') self.assertEqual(len(body.nonlinear_expr.args[0].args), 2) self.assertIs(body.nonlinear_expr.args[0].args[0], m.y) self.assertEqual(body.nonlinear_expr.args[0].args[1], 1) # 0 <= y**2 cons = constraints[3] self.assertEqual(value(cons.lower), 0) body = generate_standard_repn(cons.body) self.assertTrue(body.is_quadratic()) self.assertEqual(len(body.quadratic_vars), 1) self.assertEqual(body.quadratic_coefs[0], 1) self.assertIs(body.quadratic_vars[0][0], m.y) self.assertIs(body.quadratic_vars[0][1], m.y) # check constraints valid for a selection of points (this is nonconvex, # but anyway...) pts = [ #(sqrt(3), 6), Not numerically stable enough for this test (1, 4), (3, 6), (3, 0), (0, 0), (2, 6) ] for pt in pts: m.y.fix(pt[0]) m.z.fix(pt[1]) for i in constraints: self.assertLessEqual(value(constraints[i].lower), value(constraints[i].body)) m.y.fixed = False m.z.fixed = False # check post process these are non-convex, so I don't want to deal with # it... (and this is a good test that I *don't* deal with it.) constraints[2].deactivate() constraints[3].deactivate() constraints[1].deactivate() # NOTE also that some of the suproblems in this test are unbounded: We # need to keep those constraints. fme.post_process_fme_constraints( m, SolverFactory('glpk'), projected_constraints=m.projected_constraints) # we needed all the constraints, so we kept them all self.assertEqual(len(constraints), 6) # last check that if someone activates something on the model in # between, we just use it. (I struggle to imagine why you would do this # because why withold the information *during* FME, but if there's some # reason, we may as well use all the information we've got.) m.some_new_cons = Constraint(expr=m.y <= 2) fme.post_process_fme_constraints( m, SolverFactory('glpk'), projected_constraints=m.projected_constraints) # now we should have lost one constraint self.assertEqual(len(constraints), 5) # and it should be the y <= 3 one... self.assertIsNone(dict(constraints).get(6))
def test_getname(self): m = ConcreteModel() m.b = Block() m.b.v = Var() self.assertEqual(m.b.v.getname(fully_qualified=True, relative_to=m.b), 'v')
# ___________________________________________________________________________ # # Pyomo: Python Optimization Modeling Objects # Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC # Under the terms of Contract DE-NA0003525 with National Technology and # Engineering Solutions of Sandia, LLC, the U.S. Government retains certain # rights in this software. # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ from pyomo.core import ConcreteModel, Var, Objective, Constraint, maximize model = ConcreteModel() model.x = Var(bounds=(1, None)) model.y = Var(bounds=(1, None)) model.o = Objective(expr=-model.x - model.y, sense=maximize) model.c = Constraint(expr=model.x + model.y <= 0)
def test_getname_error(self): m = ConcreteModel() m.b = Block() m.b.v = Var() m.c = Block() self.assertRaises(RuntimeError, m.b.v.getname, fully_qualified=True, relative_to=m.c)
def build_model(use_mccormick=False): """Build the GDP model.""" m = ConcreteModel() m.F = Var(bounds=(0, 8), doc="Flow into reactor") m.X = Var(bounds=(0, 1), doc="Reactor conversion") m.d = Param(initialize=2, doc="Max product demand") m.c = Param( [1, 2, 'I', 'II'], doc="Costs", initialize={ 1: 2, # Value of product 2: 0.2, # Cost of raw material 'I': 2.5, # Cost of reactor I 'II': 1.5 # Cost of reactor II }) m.alpha = Param(['I', 'II'], doc="Reactor coefficient", initialize={ 'I': -8, 'II': -10 }) m.beta = Param(['I', 'II'], doc="Reactor coefficient", initialize={ 'I': 9, 'II': 15 }) m.X_LB = Param(['I', 'II'], doc="Reactor conversion lower bound", initialize={ 'I': 0.2, 'II': 0.7 }) m.X_UB = Param(['I', 'II'], doc="Reactor conversion upper bound", initialize={ 'I': 0.95, 'II': 0.99 }) m.C_rxn = Var(bounds=(1.5, 2.5), doc="Cost of reactor") m.reactor_choice = Disjunction( expr=[ # Disjunct 1: Choose reactor I [ m.F == m.alpha['I'] * m.X + m.beta['I'], m.X_LB['I'] <= m.X, m.X <= m.X_UB['I'], m.C_rxn == m.c['I'] ], # Disjunct 2: Choose reactor II [ m.F == m.alpha['II'] * m.X + m.beta['II'], m.X_LB['II'] <= m.X, m.X <= m.X_UB['II'], m.C_rxn == m.c['II'] ] ], xor=True) if use_mccormick: m.P = Var(bounds=(0, 8), doc="McCormick approximation of F*X") m.mccormick_1 = Constraint( expr=m.P <= m.F.lb * m.X + m.F * m.X.ub - m.F.lb * m.X.ub, doc="McCormick overestimator") m.mccormick_2 = Constraint( expr=m.P <= m.F.ub * m.X + m.F * m.X.lb - m.F.ub * m.X.lb, doc="McCormick underestimator") m.max_demand = Constraint(expr=m.P <= m.d, doc="product demand") m.profit = Objective(expr=m.c[1] * m.P - m.c[2] * m.F - m.C_rxn, sense=maximize) else: m.max_demand = Constraint(expr=m.F * m.X <= m.d, doc="product demand") m.profit = Objective(expr=m.c[1] * m.F * m.X - m.c[2] * m.F - m.C_rxn, sense=maximize) return m
def test_gdp_tree_nested_indexed_disjunction(self): m = ConcreteModel() m.I = RangeSet(1, 4) m.x = Var(m.I, bounds=(-2, 6)) m.disj1 = Disjunct() self.add_indexed_disjunction(m.disj1, m) m.disj2 = Disjunct() m.another_disjunction = Disjunction(expr=[m.disj1, m.disj2]) # First, we still just give the indexed disjunction as a target, and # make sure that we don't pick up the parent Disjunct in the tree, since # it is not in targets. targets = (m.disj1.indexed, ) knownBlocks = {} tree = get_gdp_tree(targets, m, knownBlocks) vertices = tree.vertices self.assertEqual(len(vertices), 6) in_degrees = { m.disj1.indexed[0]: 0, m.disj1.indexed[1]: 0, m.disj1.indexed[0].disjuncts[0]: 1, m.disj1.indexed[0].disjuncts[1]: 1, m.disj1.indexed[1].disjuncts[0]: 1, m.disj1.indexed[1].disjuncts[1]: 1 } for key, val in in_degrees.items(): self.assertEqual(tree.in_degree(key), val) topo_sort = [ m.disj1.indexed[0], m.disj1.indexed[0].disjuncts[1], m.disj1.indexed[0].disjuncts[0], m.disj1.indexed[1], m.disj1.indexed[1].disjuncts[1], m.disj1.indexed[1].disjuncts[0] ] sort = tree.topological_sort() for i, node in enumerate(sort): self.assertIs(node, topo_sort[i]) # Now, let targets be everything and make sure that we get the correct # tree. targets = (m, ) tree = get_gdp_tree(targets, m, knownBlocks) vertices = tree.vertices self.assertEqual(len(vertices), 9) # update that now the disjunctions have a parent in_degrees[m.disj1.indexed[0]] = 1 in_degrees[m.disj1.indexed[1]] = 1 # and add new nodes in_degrees[m.disj1] = 1 in_degrees[m.disj2] = 1 in_degrees[m.another_disjunction] = 0 for key, val in in_degrees.items(): self.assertEqual(tree.in_degree(key), val) topo_sort = [ m.another_disjunction, m.disj2, m.disj1, m.disj1.indexed[1], m.disj1.indexed[1].disjuncts[1], m.disj1.indexed[1].disjuncts[0], m.disj1.indexed[0], m.disj1.indexed[0].disjuncts[1], m.disj1.indexed[0].disjuncts[0] ] sort = tree.topological_sort() for i, node in enumerate(sort): self.assertIs(node, topo_sort[i])