def f(model, i): if i == 0: return complements(M.y + M.x3, M.x1 + 2 * M.x2 == 0) if i == 1: return Complementarity.Skip if i == 2: return complements(M.y + M.x3, M.x1 + 2 * M.x2 == 2)
def test_list2(self): M = self._setup() M.cc = ComplementarityList() M.cc.add(complements(M.y + M.x3, M.x1 + 2 * M.x2 == 0)) M.cc.add(complements(M.y + M.x3, M.x1 + 2 * M.x2 == 1)) M.cc.add(complements(M.y + M.x3, M.x1 + 2 * M.x2 == 2)) M.cc[2].deactivate() self._test("list2", M)
def test_t3b(self): # Reversing the expressions in test t3a: # x1 + x2 >= -1 _|_ y + x3 >= 0 M = self._setup() M.cc = Complementarity( expr=complements(M.x1 + M.x2 >= -1, M.y + M.x3 >= 0)) self._test("t3b", M)
def test_t4b(self): # Reversing the expressions in test t7b: # y + x3 _|_ x1 + 2*x2 + 3*x3 = 1 M = self._setup() M.cc = Complementarity( expr=complements(M.y + M.x3, M.x1 + 2 * M.x2 + 3 * M.x3 == 1)) self._test("t4b", M)
def test_t9(self): # Testing that we can skip deactivated complementarity conditions M = self._setup() M.cc = Complementarity(expr=complements(M.y + M.x3, M.x1 + 2 * M.x2 == 1)) M.cc.deactivate() self._test("t9", M)
def test_t1a(self): # y + x1 >= 0 _|_ x1 + 2*x2 + 3*x3 >= 1 M = self._setup() M.c = Constraint(expr=M.y + M.x3 >= M.x2) M.cc = Complementarity( expr=complements(M.y + M.x1 >= 0, M.x1 + 2 * M.x2 + 3 * M.x3 >= 1)) self._test("t1a", M)
def test_t1b(self): # Reversing the expressions in test t1a: # x1 + 2*x2 + 3*x3 >= 1 _|_ y + x1 >= 0 M = self._setup() M.cc = Complementarity( expr=complements(M.x1 + 2 * M.x2 + 3 * M.x3 >= 1, M.y + M.x1 >= 0)) self._test("t1b", M)
def test_t2b(self): # Reversing the expressions in test t2a: # x2 - x3 <= -1 _|_ y + x2 >= 0 M = self._setup() M.cc = Complementarity( expr=complements(M.x2 - M.x3 <= -1, M.y + M.x2 >= 0)) self._test("t2b", M)
def test_list5(self): M = self._setup() M.cc = ComplementarityList( rule=( complements(M.y + M.x3, M.x1 + 2*M.x2 == i) for i in range(3) ) ) self._test("list5", M)
def test_cov11(self): # Testing construction with a badly formed expression M = self._setup() M.cc = Complementarity(expr=complements(1 <= M.x1 <= M.y, M.x2)) try: M.cc.to_standard_form() self.fail("Expected a RuntimeError") except RuntimeError: pass
def test_t9(self): # Testing that we can skip deactivated complementarity conditions M = self._setup() M.cc = Complementarity(expr=complements(M.y + M.x3, M.x1 + 2 * M.x2 == 1)) M.cc.deactivate() # AMPL needs at least one variable in the problem therefore # we need to have a constraint that keeps them around M.keep_var_con = Constraint(expr=M.x1 == 0.5) self._test("t9", M)
def get_model(self): m = ConcreteModel() m.x = Var(bounds=(-100, 100)) m.obj = Objective(expr=m.x) m.disjunct1 = Disjunct() m.disjunct1.comp = Complementarity(expr=complements(m.x >= 0, 4*m.x - 3 >= 0)) m.disjunct2 = Disjunct() m.disjunct2.cons = Constraint(expr=m.x >= 2) m.disjunction = Disjunction(expr=[m.disjunct1, m.disjunct2]) return m
def test_t4c(self): # 1 = x1 + 2*x2 + 3*x3 _|_ y + x3 M = self._setup() M.cc = Complementarity(expr=complements(1 == M.x1 + 2 * M.x2 + 3 * M.x3, M.y + M.x3)) self._test("t4c", M)
def f(M, i): if i == 1: return complements(M.y + M.x3, M.x1 + 2 * M.x2 == 0) elif i == 2: return complements(M.y + M.x3, M.x1 + 2 * M.x2 == 2) return ComplementarityList.End
def test_list1(self): M = self._setup() M.cc = ComplementarityList() M.cc.add(complements(M.y + M.x3, M.x1 + 2 * M.x2 == 0)) M.cc.add(complements(M.y + M.x3, M.x1 + 2 * M.x2 == 2)) self._test("list1", M)
def test_t11(self): # 2 <= y + x1 <= 3 _|_ x1 M = self._setup() M.cc = Complementarity(expr=complements(inequality(2, M.y + M.x1, 3), M.x1)) self._test("t11", M)
# ex1e.py import pyomo.environ as pyo from pyomo.mpec import ComplementarityList, complements n = 5 model = pyo.ConcreteModel() model.x = pyo.Var(range(1, n + 1)) model.f = pyo.Objective(expr=sum(i * (model.x[i] - 1)**2 for i in range(1, n + 1))) model.compl = ComplementarityList( rule=(complements(model.x[i] >= 0, model.x[i + 1] >= 0) for i in range(1, n)))
def test_t2a(self): # y + x2 >= 0 _|_ x2 - x3 <= -1 M = self._setup() M.cc = Complementarity(expr=complements(M.y + M.x2 >= 0, M.x2 - M.x3 <= -1)) self._test("t2a", M)
def test_t1c(self): # y >= - x1 _|_ x1 + 2*x2 >= 1 - 3*x3 M = self._setup() M.cc = Complementarity(expr=complements(M.y >= -M.x1, M.x1 + 2 * M.x2 >= 1 - 3 * M.x3)) self._test("t1c", M)
def test_t1b(self): # Reversing the expressions in test t1a: # x1 + 2*x2 + 3*x3 >= 1 _|_ y + x1 >= 0 M = self._setup() M.cc = Complementarity(expr=complements(M.x1 + 2 * M.x2 + 3 * M.x3 >= 1, M.y + M.x1 >= 0)) self._test("t1b", M)
def test_t1a(self): # y + x1 >= 0 _|_ x1 + 2*x2 + 3*x3 >= 1 M = self._setup() M.c = Constraint(expr=M.y + M.x3 >= M.x2) M.cc = Complementarity(expr=complements(M.y + M.x1 >= 0, M.x1 + 2 * M.x2 + 3 * M.x3 >= 1)) self._test("t1a", M)
def test_list5(self): M = self._setup() M.cc = ComplementarityList(rule=(complements(M.y + M.x3, M.x1 + 2 * M.x2 == i) for i in range(3))) self._test("list5", M)
def f(M): yield complements(M.y + M.x3, M.x1 + 2 * M.x2 == 0) yield complements(M.y + M.x3, M.x1 + 2 * M.x2 == 2) yield ComplementarityList.End
def test_t1c(self): # y >= - x1 _|_ x1 + 2*x2 >= 1 - 3*x3 M = self._setup() M.cc = Complementarity( expr=complements(M.y >= -M.x1, M.x1 + 2 * M.x2 >= 1 - 3 * M.x3)) self._test("t1c", M)
def test_t4a(self): # x1 + 2*x2 + 3*x3 = 1 _|_ y + x3 M = self._setup() M.cc = Complementarity(expr=complements(M.x1 + 2 * M.x2 + 3 * M.x3 == 1, M.y + M.x3)) self._test("t4a", M)
def test_t2b(self): # Reversing the expressions in test t2a: # x2 - x3 <= -1 _|_ y + x2 >= 0 M = self._setup() M.cc = Complementarity(expr=complements(M.x2 - M.x3 <= -1, M.y + M.x2 >= 0)) self._test("t2b", M)
def compl_(model, i): return complements(model.x[i] >= 0, model.x[i+1] >= 0)
def test_t3a(self): # y + x3 >= 0 _|_ x1 + x2 >= -1 M = self._setup() M.cc = Complementarity(expr=complements(M.y + M.x3 >= 0, M.x1 + M.x2 >= -1)) self._test("t3a", M)
def test_t11(self): # 2 <= y + x1 <= 3 _|_ x1 M = self._setup() M.cc = Complementarity(expr=complements(2 <= M.y + M.x1 <= 3, M.x1)) self._test("t11", M)
def test_t3b(self): # Reversing the expressions in test t3a: # x1 + x2 >= -1 _|_ y + x3 >= 0 M = self._setup() M.cc = Complementarity(expr=complements(M.x1 + M.x2 >= -1, M.y + M.x3 >= 0)) self._test("t3b", M)
def test_t12(self): # x1 _|_ 2 <= y + x1 <= 3""" M = self._setup() M.cc = Complementarity( expr=complements(M.x1, inequality(2, M.y + M.x1, 3))) self._test("t12", M)
def _add_optimality_conditions(self, instance, submodel): """ Add optimality conditions for the submodel This assumes that the original model has the form: min c1*x + d1*y A3*x <= b3 A1*x + B1*y <= b1 min c2*x + d2*y + x'*Q*y A2*x + B2*y + x'*E2*y <= b2 y >= 0 NOTE THE VARIABLE BOUNDS! """ # # Populate the block with the linear constraints. # Note that we don't simply clone the current block. # We need to collect a single set of equations that # can be easily expressed. # d2 = {} B2 = {} vtmp = {} utmp = {} sids_set = set() sids_list = [] # block = Block(concrete=True) block.u = VarList() block.v = VarList() block.c1 = ConstraintList() block.c2 = ComplementarityList() block.c3 = ComplementarityList() # # Collect submodel objective terms # # TODO: detect fixed variables # for odata in submodel.component_data_objects(Objective, active=True): if odata.sense == maximize: d_sense = -1 else: d_sense = 1 # # Iterate through the variables in the representation # o_terms = generate_standard_repn(odata.expr, compute_values=False) # # Linear terms # for i, var in enumerate(o_terms.linear_vars): if var.parent_component().local_name in self._fixed_upper_vars: # # Skip fixed upper variables # continue # # Store the coefficient for the variable. The coefficient is # negated if the objective is maximized. # id_ = id(var) d2[id_] = d_sense * o_terms.linear_coefs[i] if not id_ in sids_set: sids_set.add(id_) sids_list.append(id_) # # Quadratic terms # for i, var in enumerate(o_terms.quadratic_vars): if var[0].parent_component().local_name in self._fixed_upper_vars: if var[1].parent_component().local_name in self._fixed_upper_vars: # # Skip fixed upper variables # continue # # Add the linear term # id_ = id(var[1]) d2[id_] = d2.get(id_,0) + d_sense * o_terms.quadratic_coefs[i] * var[0] if not id_ in sids_set: sids_set.add(id_) sids_list.append(id_) elif var[1].parent_component().local_name in self._fixed_upper_vars: # # Add the linear term # id_ = id(var[0]) d2[id_] = d2.get(id_,0) + d_sense * o_terms.quadratic_coefs[i] * var[1] if not id_ in sids_set: sids_set.add(id_) sids_list.append(id_) else: raise RuntimeError("Cannot apply this transformation to a problem with quadratic terms where both variables are in the lower level.") # # Stop after the first objective # break # # Iterate through all lower level variables, adding dual variables # and complementarity slackness conditions for y bound constraints # for vcomponent in instance.component_objects(Var, active=True): if vcomponent.local_name in self._fixed_upper_vars: # # Skip fixed upper variables # continue for ndx in vcomponent: # # For each index, get the bounds for the variable # lb, ub = vcomponent[ndx].bounds if not lb is None: # # Add the complementarity slackness condition for a lower bound # v = block.v.add() block.c3.add( complements(vcomponent[ndx] >= lb, v >= 0) ) else: v = None if not ub is None: # # Add the complementarity slackness condition for an upper bound # w = block.v.add() vtmp[id(vcomponent[ndx])] = w block.c3.add( complements(vcomponent[ndx] <= ub, w >= 0) ) else: w = None if not (v is None and w is None): # # Record the variables for which complementarity slackness conditions # were created. # id_ = id(vcomponent[ndx]) vtmp[id_] = (v,w) if not id_ in sids_set: sids_set.add(id_) sids_list.append(id_) # # Iterate through all constraints, adding dual variables and # complementary slackness conditions (for inequality constraints) # for cdata in submodel.component_data_objects(Constraint, active=True): if cdata.equality: # Don't add a complementary slackness condition for an equality constraint u = block.u.add() utmp[id(cdata)] = (None,u) else: if not cdata.lower is None: # # Add the complementarity slackness condition for a greater-than inequality # u = block.u.add() block.c2.add( complements(- cdata.body <= - cdata.lower, u >= 0) ) else: u = None if not cdata.upper is None: # # Add the complementarity slackness condition for a less-than inequality # w = block.u.add() block.c2.add( complements(cdata.body <= cdata.upper, w >= 0) ) else: w = None if not (u is None and w is None): utmp[id(cdata)] = (u,w) # # Store the coefficients for the constraint variables that are not fixed # c_terms = generate_standard_repn(cdata.body, compute_values=False) # # Linear terms # for i, var in enumerate(c_terms.linear_vars): if var.parent_component().local_name in self._fixed_upper_vars: continue id_ = id(var) B2.setdefault(id_,{}).setdefault(id(cdata),c_terms.linear_coefs[i]) if not id_ in sids_set: sids_set.add(id_) sids_list.append(id_) # # Quadratic terms # for i, var in enumerate(c_terms.quadratic_vars): if var[0].parent_component().local_name in self._fixed_upper_vars: if var[1].parent_component().local_name in self._fixed_upper_vars: continue id_ = id(var[1]) if id_ in B2: B2[id_][id(cdata)] = c_terms.quadratic_coefs[i] * var[0] else: B2.setdefault(id_,{}).setdefault(id(cdata),c_terms.quadratic_coefs[i] * var[0]) if not id_ in sids_set: sids_set.add(id_) sids_list.append(id_) elif var[1].parent_component().local_name in self._fixed_upper_vars: id_ = id(var[0]) if id_ in B2: B2[id_][id(cdata)] = c_terms.quadratic_coefs[i] * var[1] else: B2.setdefault(id_,{}).setdefault(id(cdata),c_terms.quadratic_coefs[i] * var[1]) if not id_ in sids_set: sids_set.add(id_) sids_list.append(id_) else: raise RuntimeError("Cannot apply this transformation to a problem with quadratic terms where both variables are in the lower level.") # # Generate stationarity equations # tmp__ = (None, None) for vid in sids_list: exp = d2.get(vid,0) # lb_dual, ub_dual = vtmp.get(vid, tmp__) if vid in vtmp: if not lb_dual is None: exp -= lb_dual # dual for variable lower bound if not ub_dual is None: exp += ub_dual # dual for variable upper bound # B2_ = B2.get(vid,{}) utmp_keys = list(utmp.keys()) if self._deterministic: utmp_keys.sort(key=lambda x:utmp[x][0].local_name if utmp[x][1] is None else utmp[x][1].local_name) for uid in utmp_keys: if uid in B2_: lb_dual, ub_dual = utmp[uid] if not lb_dual is None: exp -= B2_[uid] * lb_dual if not ub_dual is None: exp += B2_[uid] * ub_dual if type(exp) in six.integer_types or type(exp) is float: # TODO: Annotate the model as unbounded raise IOError("Unbounded variable without side constraints") else: block.c1.add( exp == 0 ) # # Return block # return block
def f(model): return complements(M.y + M.x3, M.x1 + 2 * M.x2 == 1)
def _apply_solver(self): start_time = time.time() M=self.options.dual_bound if not self.options.dual_bound: M=1e6 print(f'Dual bound not specified, set to default {M}') delta = self.options.delta if not self.options.delta: delta = 0.05 #What should default robustness delta be if not specified? Or should I raise an error? print(f'Robustness parameter not specified, set to default {delta}') # matrix representation for bilevel problem matrix_repn = BilevelMatrixRepn(self._instance,standard_form=False) # each lower-level problem submodel = [block for block in self._instance.component_objects(SubModel)][0] if len(submodel) != 1: raise Exception('Problem encountered, this is not a valid bilevel model for the solver.') self._instance.reclassify_component_type(submodel, Block) #varref(submodel) #dataref(submodel) all_vars = {key: var for (key, var) in matrix_repn._all_vars.items()} # get the variables that are fixed for the submodel (lower-level block) fixed_vars = {key: var for (key, var) in matrix_repn._all_vars.items() if key in matrix_repn._fixed_var_ids[submodel.name]} #Is there a way to get integer, continuous, etc for the upper level rather than lumping them all into fixed? # continuous variables in SubModel c_vars = {key: var for (key, var) in matrix_repn._all_vars.items() if key in matrix_repn._c_var_ids - fixed_vars.keys()} # binary variables in SubModel SHOULD BE EMPTY FOR THIS SOLVER b_vars = {key: var for (key, var) in matrix_repn._all_vars.items() if key in matrix_repn._b_var_ids - fixed_vars.keys()} if len(b_vars)!= 0: raise Exception('Problem encountered, this is not a valid bilevel model for the solver. Binary variables present!') # integer variables in SubModel SHOULD BE EMPTY FOR THIS SOLVER i_vars = {key: var for (key, var) in matrix_repn._all_vars.items() if key in matrix_repn._i_var_ids - fixed_vars.keys()} if len(i_vars) != 0: raise Exception('Problem encountered, this is not a valid bilevel model for the solver. Integer variables present!') # get constraint information related to constraint id, sign, and rhs value sub_cons = matrix_repn._cons_sense_rhs[submodel.name] cons= matrix_repn._cons_sense_rhs[self._instance.name] # construct the high-point problem (LL feasible, no LL objective) # s0 <- solve the high-point # if s0 infeasible then return high_point_infeasible xfrm = TransformationFactory('pao.bilevel.highpoint') xfrm.apply_to(self._instance) # # Solve with a specified solver # solver = self.options.solver if not self.options.solver: solver = 'gurobi' for c in self._instance.component_objects(Block, descend_into=False): if 'hp' in c.name: #if '_hp' in c.name: c.activate() with pyomo.opt.SolverFactory(solver) as opt: self.results.append(opt.solve(c, tee=self._tee, timelimit=self._timelimit)) _check_termination_condition(self.results[-1]) c.deactivate() if self.options.do_print==True: print('Solution to the Highpoint Relaxation') for _, var in all_vars.items(): var.pprint() # s1 <- solve the optimistic bilevel (linear/linear) problem (call solver3) # if s1 infeasible then return optimistic_infeasible' with pyomo.opt.SolverFactory('pao.bilevel.blp_global') as opt: opt.options.solver = solver self.results.append(opt.solve(self._instance,tee=self._tee,timelimit=self._timelimit)) _check_termination_condition(self.results[-1]) if self.options.do_print==True: print('Solution to the Optimistic Bilevel') for _, var in all_vars.items(): var.pprint() #self._instance.pprint() #checking for active blocks left over from previous solves # sk <- solve the dual adversarial problem # if infeasible then return dual_adversarial_infeasible # Collect the vertices solutions for the dual adversarial problem #Collect up the matrix B and the vector d for use in all adversarial feasibility problems n=len(c_vars.items()) m=len(sub_cons.items()) K=len(cons.items()) B=np.empty([m,n]) L=np.empty([K,1]) i=0 p=0 for _, var in c_vars.items(): (A, A_q, sign, b) = matrix_repn.coef_matrices(submodel, var) B[:,i]=np.transpose(np.array(A)) i+=1 _ad_block_name='_adversarial' self._instance.add_component(_ad_block_name, Block(Any)) _Vertices_name='_Vertices' _Vertices_B_name='_VerticesB' self._instance.add_component(_Vertices_name,Param(cons.keys()*NonNegativeIntegers*sub_cons.keys(),mutable=True)) Vertices=getattr(self._instance,_Vertices_name) self._instance.add_component(_Vertices_B_name,Param(cons.keys()*NonNegativeIntegers,mutable=True)) VerticesB=getattr(self._instance,_Vertices_B_name) adversarial=getattr(self._instance,_ad_block_name) #Add Adversarial blocks for _cidS, _ in cons.items(): # <for each constraint in the upper-level problem> (_cid,_)=_cidS ad=adversarial[_cid] #shorthand ad.alpha=Var(sub_cons.keys(),within=NonNegativeReals) #sub_cons.keys() because it's a dual variable on the lower level constraints ad.beta=Var(within=NonNegativeReals) Hk=np.empty([n,1]) i=0 d=np.empty([n,1]) ad.cons=Constraint(c_vars.keys()) #B^Talpha+beta*d>= H_k, v-dimension constraints so index by c_vars lhs_expr = {key: 0. for key in c_vars.keys()} rhs_expr = {key: 0. for key in c_vars.keys()} for _vid, var in c_vars.items(): (A, A_q, sign, b) = matrix_repn.coef_matrices(submodel, var) coef = A #+ dot(A_q.toarray(), _fixed) (C, C_q, C_constant) = matrix_repn.cost_vectors(submodel, var) d[i,0]=float(C) lhs_expr[_vid]=float(C)*ad.beta (A,A_q,sign,b)=matrix_repn.coef_matrices(self._instance,var) idx = list(cons.keys()).index(_cidS) Hk[i,0]=A[idx] i+=1 for _cid2 in sub_cons.keys(): idx = list(sub_cons.keys()).index(_cid2) lhs_expr[_vid] += float(coef[idx])*ad.alpha[_cid2] rhs_expr[_vid] = float(A[idx]) expr = lhs_expr[_vid] >= rhs_expr[_vid] if not type(expr) is bool: ad.cons[_vid] = expr else: ad.cons[_vid] = Constraint.Skip ad.Obj=Objective(expr=0) #THIS IS A FEASIBILITY PROBLEM with pyomo.opt.SolverFactory(solver) as opt: self.results.append(opt.solve(ad, tee=self._tee, timelimit=self._timelimit)) _check_termination_condition(self.results[-1]) ad.deactivate() Bd=np.hstack((np.transpose(B),d)) Eye=np.identity(m+1) Bd=np.vstack((Bd,Eye)) Hk=np.vstack((Hk,np.zeros((m+1,1)))) mat=np.hstack((-Hk,Bd)) mat=cdd.Matrix(mat,number_type='float') mat.rep_type=cdd.RepType.INEQUALITY poly=cdd.Polyhedron(mat) ext=poly.get_generators() extreme=np.array(ext) if self.options.do_print==True: print(ext) (s,t)=extreme.shape l=1 for i in range(0,s): j=1 if extreme[0,i]==1: for _scid in sub_cons.keys(): #for j in range(1,t-1): #Need to loop over extreme 1 to t-1 and link those to the cons.keys for alpha? Vertices[(_cidS,l,_scid)]=extreme[i,j] #Vertex l of the k-th polytope j+=1 VerticesB[(_cidS,l)]=extreme[i,t-1] l+=1 L[p,0]=l-1 p+=1 #vertex enumeration goes from 1 to L # Solving the full problem sn0 _model_name = '_extended' _model_name = unique_component_name(self._instance, _model_name) xfrm = TransformationFactory('pao.bilevel.highpoint') #5.6a-c kwds = {'submodel_name': _model_name} xfrm.apply_to(self._instance, **kwds) extended=getattr(self._instance,_model_name) extended.sigma=Var(c_vars.keys(),within=NonNegativeReals,bounds=(0,M)) extended.lam=Var(sub_cons.keys(),within=NonNegativeReals,bounds=(0,M)) #5.d extended.d = Constraint(c_vars.keys()) #indexed by lower level variables d_expr= {key: 0. for key in c_vars.keys()} for _vid, var in c_vars.items(): (C, C_q, C_constant) = matrix_repn.cost_vectors(submodel, var) #gets d_i d_expr[_vid]+=float(C) d_expr[_vid]=d_expr[_vid]-extended.sigma[_vid] (A, A_q, sign, b) = matrix_repn.coef_matrices(submodel, var) for _cid, _ in sub_cons.items(): idx = list(sub_cons.keys()).index(_cid) d_expr[_vid]+=extended.lam[_cid]*float(A[idx]) expr = d_expr[_vid] == 0 if not type(expr) is bool: extended.d[_vid] = expr else: extended.d[_vid] = Constraint.Skip #5.e (Complementarity) extended.e = ComplementarityList() for _cid, _ in sub_cons.items(): idx=list(sub_cons.keys()).index(_cid) expr=0 for _vid, var in fixed_vars.items(): #A_i*x (A, A_q, sign, b) = matrix_repn.coef_matrices(submodel, var) expr+=float(A[idx])*fixed_vars[_vid] for _vid, var in c_vars.items(): #B_i*v (A, A_q, sign, b) = matrix_repn.coef_matrices(submodel, var) expr+=float(A[idx])*c_vars[_vid] expr=expr-float(b[idx]) extended.e.add(complements(extended.lam[_cid] >= 0, expr <= 0)) #5.f (Complementarity) extended.f = ComplementarityList() for _vid,var in c_vars.items(): extended.f.add(complements(extended.sigma[_vid]>=0,var>=0)) #Replace 5.h-5.j with 5.7 Disjunction extended.disjunction=Block(cons.keys()) #One disjunction per adversarial problem, one adversarial problem per upper level constraint k=0 for _cidS,_ in cons.items(): idxS=list(cons.keys()).index(_cidS) [_cid,sign]=_cidS disjunction=extended.disjunction[_cidS] #shorthand disjunction.Lset=RangeSet(1,L[k,0]) disjunction.disjuncts=Disjunct(disjunction.Lset) for i in disjunction.Lset: #defining the L disjuncts l_expr=0 for _vid, var in c_vars.items(): (C, C_q, C_constant) = matrix_repn.cost_vectors(submodel, var) l_expr+=float(C)*var #d^Tv l_expr+=delta l_expr=VerticesB[(_cidS,i)]*l_expr #beta(d^Tv+delta) for _cid, Scons in sub_cons.items(): #SUM over i to ml Ax=0 idx=list(sub_cons.keys()).index(_cid) for _vid, var in fixed_vars.items(): (A, A_q, sign, b) = matrix_repn.coef_matrices(submodel, var) Ax += float(A[idx])*var l_expr+=Vertices[(_cidS,i,_cid)]*(float(b[idx])-Ax) r_expr=0 for _vid,var in fixed_vars.items(): (A, A_q, sign, b) = matrix_repn.coef_matrices(self._instance, var) #get q and G r_expr=r_expr-float(A[idxS])*var r_expr+=float(b[idxS]) disjunction.disjuncts[i].cons=Constraint(expr= l_expr<=r_expr) disjunction.seven=Disjunction(expr=[disjunction.disjuncts[i] for i in disjunction.Lset],xor=False) k+=1 #extended.pprint() TransformationFactory('mpec.simple_disjunction').apply_to(extended) bigm = TransformationFactory('gdp.bigm') bigm.apply_to(extended) with pyomo.opt.SolverFactory(solver) as opt: self.results.append(opt.solve(extended, tee=self._tee, timelimit=self._timelimit)) _check_termination_condition(self.results[-1]) # Return the sn0 solution if self.options.do_print==True: print('Robust Solution') for _vid, _ in fixed_vars.items(): fixed_vars[_vid].pprint() for _vid, _ in c_vars.items(): c_vars[_vid].pprint() extended.lam.pprint() extended.sigma.pprint() stop_time = time.time() self.wall_time = stop_time - start_time return pyutilib.misc.Bunch(rc=getattr(opt, '_rc', None), log=getattr(opt, '_log', None))
def test_t4d(self): # x1 + 2*x2 == 1 - 3*x3 _|_ y + x3 M = self._setup() M.cc = Complementarity(expr=complements(M.x1 + 2 * M.x2 == 1 - 3 * M.x3, M.y + M.x3)) self._test("t4d", M)
from pyomo import environ, mpec model = environ.ConcreteModel() # define the model variables model.x1 = environ.Var() model.x2 = environ.Var() model.x3 = environ.Var() # define the complementarity conditions model.f1 = mpec.Complementarity( expr=mpec.complements(model.x1 >= 0, model.x1 + 2 * model.x2 + 3 * model.x3 >= 1)) model.f2 = mpec.Complementarity( expr=mpec.complements(model.x2 >= 0, model.x2 - model.x3 >= -1)) model.f3 = mpec.Complementarity( expr=mpec.complements(model.x3 >= 0, model.x1 + model.x2 >= -1))
def test_t4b(self): # Reversing the expressions in test t7b: # y + x3 _|_ x1 + 2*x2 + 3*x3 = 1 M = self._setup() M.cc = Complementarity(expr=complements(M.y + M.x3, M.x1 + 2 * M.x2 + 3 * M.x3 == 1)) self._test("t4b", M)
def test_t4a(self): # x1 + 2*x2 + 3*x3 = 1 _|_ y + x3 M = self._setup() M.cc = Complementarity( expr=complements(M.x1 + 2 * M.x2 + 3 * M.x3 == 1, M.y + M.x3)) self._test("t4a", M)
def test_t2a(self): # y + x2 >= 0 _|_ x2 - x3 <= -1 M = self._setup() M.cc = Complementarity( expr=complements(M.y + M.x2 >= 0, M.x2 - M.x3 <= -1)) self._test("t2a", M)
def test_t12(self): # x1 _|_ 2 <= y + x1 <= 3""" M = self._setup() M.cc = Complementarity(expr=complements(M.x1, 2 <= M.y + M.x1 <= 3)) self._test("t12", M)
def test_t3a(self): # y + x3 >= 0 _|_ x1 + x2 >= -1 M = self._setup() M.cc = Complementarity( expr=complements(M.y + M.x3 >= 0, M.x1 + M.x2 >= -1)) self._test("t3a", M)
def test_t4c(self): # 1 = x1 + 2*x2 + 3*x3 _|_ y + x3 M = self._setup() M.cc = Complementarity( expr=complements(1 == M.x1 + 2 * M.x2 + 3 * M.x3, M.y + M.x3)) self._test("t4c", M)
def _add_optimality_conditions(self, instance, submodel): """ Add optimality conditions for the submodel This assumes that the original model has the form: min c1*x + d1*y A3*x <= b3 A1*x + B1*y <= b1 min c2*x + d2*y + x'*Q*y A2*x + B2*y + x'*E2*y <= b2 y >= 0 NOTE THE VARIABLE BOUNDS! """ # # Populate the block with the linear constraints. # Note that we don't simply clone the current block. # We need to collect a single set of equations that # can be easily expressed. # d2 = {} B2 = {} vtmp = {} utmp = {} sids_set = set() sids_list = [] # block = Block(concrete=True) block.u = VarList() block.v = VarList() block.c1 = ConstraintList() block.c2 = ComplementarityList() block.c3 = ComplementarityList() # # Collect submodel objective terms # # TODO: detect fixed variables # for odata in submodel.component_data_objects(Objective, active=True): if odata.sense == maximize: d_sense = -1 else: d_sense = 1 # # Iterate through the variables in the representation # o_terms = generate_standard_repn(odata.expr, compute_values=False) # # Linear terms # for i, var in enumerate(o_terms.linear_vars): if var.parent_component().local_name in self._fixed_upper_vars: # # Skip fixed upper variables # continue # # Store the coefficient for the variable. The coefficient is # negated if the objective is maximized. # id_ = id(var) d2[id_] = d_sense * o_terms.linear_coefs[i] if not id_ in sids_set: sids_set.add(id_) sids_list.append(id_) # # Quadratic terms # for i, var in enumerate(o_terms.quadratic_vars): if var[0].parent_component( ).local_name in self._fixed_upper_vars: if var[1].parent_component( ).local_name in self._fixed_upper_vars: # # Skip fixed upper variables # continue # # Add the linear term # id_ = id(var[1]) d2[id_] = d2.get( id_, 0) + d_sense * o_terms.quadratic_coefs[i] * var[0] if not id_ in sids_set: sids_set.add(id_) sids_list.append(id_) elif var[1].parent_component( ).local_name in self._fixed_upper_vars: # # Add the linear term # id_ = id(var[0]) d2[id_] = d2.get( id_, 0) + d_sense * o_terms.quadratic_coefs[i] * var[1] if not id_ in sids_set: sids_set.add(id_) sids_list.append(id_) else: raise RuntimeError( "Cannot apply this transformation to a problem with quadratic terms where both variables are in the lower level." ) # # Stop after the first objective # break # # Iterate through all lower level variables, adding dual variables # and complementarity slackness conditions for y bound constraints # for vcomponent in instance.component_objects(Var, active=True): if vcomponent.local_name in self._fixed_upper_vars: # # Skip fixed upper variables # continue for ndx in vcomponent: # # For each index, get the bounds for the variable # lb, ub = vcomponent[ndx].bounds if not lb is None: # # Add the complementarity slackness condition for a lower bound # v = block.v.add() block.c3.add(complements(vcomponent[ndx] >= lb, v >= 0)) else: v = None if not ub is None: # # Add the complementarity slackness condition for an upper bound # w = block.v.add() vtmp[id(vcomponent[ndx])] = w block.c3.add(complements(vcomponent[ndx] <= ub, w >= 0)) else: w = None if not (v is None and w is None): # # Record the variables for which complementarity slackness conditions # were created. # id_ = id(vcomponent[ndx]) vtmp[id_] = (v, w) if not id_ in sids_set: sids_set.add(id_) sids_list.append(id_) # # Iterate through all constraints, adding dual variables and # complementary slackness conditions (for inequality constraints) # for cdata in submodel.component_data_objects(Constraint, active=True): if cdata.equality: # Don't add a complementary slackness condition for an equality constraint u = block.u.add() utmp[id(cdata)] = (None, u) else: if not cdata.lower is None: # # Add the complementarity slackness condition for a greater-than inequality # u = block.u.add() block.c2.add( complements(-cdata.body <= -cdata.lower, u >= 0)) else: u = None if not cdata.upper is None: # # Add the complementarity slackness condition for a less-than inequality # w = block.u.add() block.c2.add(complements(cdata.body <= cdata.upper, w >= 0)) else: w = None if not (u is None and w is None): utmp[id(cdata)] = (u, w) # # Store the coefficients for the contraint variables that are not fixed # c_terms = generate_standard_repn(cdata.body, compute_values=False) # # Linear terms # for i, var in enumerate(c_terms.linear_vars): if var.parent_component().local_name in self._fixed_upper_vars: continue id_ = id(var) B2.setdefault(id_, {}).setdefault(id(cdata), c_terms.linear_coefs[i]) if not id_ in sids_set: sids_set.add(id_) sids_list.append(id_) # # Quadratic terms # for i, var in enumerate(c_terms.quadratic_vars): if var[0].parent_component( ).local_name in self._fixed_upper_vars: if var[1].parent_component( ).local_name in self._fixed_upper_vars: continue id_ = id(var[1]) if id_ in B2: B2[id_][id( cdata)] = c_terms.quadratic_coefs[i] * var[0] else: B2.setdefault(id_, {}).setdefault( id(cdata), c_terms.quadratic_coefs[i] * var[0]) if not id_ in sids_set: sids_set.add(id_) sids_list.append(id_) elif var[1].parent_component( ).local_name in self._fixed_upper_vars: id_ = id(var[0]) if id_ in B2: B2[id_][id( cdata)] = c_terms.quadratic_coefs[i] * var[1] else: B2.setdefault(id_, {}).setdefault( id(cdata), c_terms.quadratic_coefs[i] * var[1]) if not id_ in sids_set: sids_set.add(id_) sids_list.append(id_) else: raise RuntimeError( "Cannot apply this transformation to a problem with quadratic terms where both variables are in the lower level." ) # # Generate stationarity equations # tmp__ = (None, None) for vid in sids_list: exp = d2.get(vid, 0) # lb_dual, ub_dual = vtmp.get(vid, tmp__) if vid in vtmp: if not lb_dual is None: exp -= lb_dual # dual for variable lower bound if not ub_dual is None: exp += ub_dual # dual for variable upper bound # B2_ = B2.get(vid, {}) utmp_keys = list(utmp.keys()) if self._deterministic: utmp_keys.sort(key=lambda x: utmp[x][0].local_name if utmp[x][ 1] is None else utmp[x][1].local_name) for uid in utmp_keys: if uid in B2_: lb_dual, ub_dual = utmp[uid] if not lb_dual is None: exp -= B2_[uid] * lb_dual if not ub_dual is None: exp += B2_[uid] * ub_dual if type(exp) in six.integer_types or type(exp) is float: # TODO: Annotate the model as unbounded raise IOError("Unbounded variable without side constraints") else: block.c1.add(exp == 0) # # Return block # return block
def test_t4d(self): # x1 + 2*x2 == 1 - 3*x3 _|_ y + x3 M = self._setup() M.cc = Complementarity( expr=complements(M.x1 + 2 * M.x2 == 1 - 3 * M.x3, M.y + M.x3)) self._test("t4d", M)
# ex1b.py import pyomo.environ as pyo from pyomo.mpec import ComplementarityList, complements n = 5 model = pyo.ConcreteModel() model.x = pyo.Var(range(1, n + 1)) model.f = pyo.Objective(expr=sum(i * (model.x[i] - 1)**2 for i in range(1, n + 1))) model.compl = ComplementarityList() model.compl.add(complements(model.x[1] >= 0, model.x[2] >= 0)) model.compl.add(complements(model.x[2] >= 0, model.x[3] >= 0)) model.compl.add(complements(model.x[3] >= 0, model.x[4] >= 0)) model.compl.add(complements(model.x[4] >= 0, model.x[5] >= 0))