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)
Exemple #2
0
def create_submodel_kkt_block(instance, submodel, deterministic,
                              fixed_upper_vars):
    """
    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!
    """
    fixed_vars = {id(v) for v in fixed_upper_vars}
    #
    # 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(
    )  # Note: Dual variables associated to bounds in primal problem
    block.v = VarList(
    )  # Note: Dual variables associated to constraints in primal problem
    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 id(var) in fixed_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 id(var[0]) in fixed_vars:
                if id(var[1]) in fixed_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 id(var[1]) in fixed_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):
        for ndx in vcomponent:
            if id(vcomponent[ndx]) in fixed_vars:
                #
                # Skip fixed upper variables
                #
                continue
            #
            # 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 id(var) in fixed_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 id(var[0]) in fixed_vars:
                if id(var[1]) in fixed_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 id(var[1]) in fixed_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 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")
        block.c1.add(exp == 0)
    return block
 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)
Exemple #4
0
    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))
Exemple #5
0
# ex1c.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)))


def compl_(model):
    yield complements(model.x[1] >= 0, model.x[2] >= 0)
    yield complements(model.x[2] >= 0, model.x[3] >= 0)
    yield complements(model.x[3] >= 0, model.x[4] >= 0)
    yield complements(model.x[4] >= 0, model.x[5] >= 0)


model.compl = ComplementarityList(rule=compl_)
Exemple #6
0
    def _apply_solver(self):
        self.results = []
        start_time = time.time()
        #
        # Solve with a specified solver
        #
        solver = self.options.solver
        if not self.options.solver:
            solver = 'gurobi'

        bigm = TransformationFactory('gdp.bigm')

        # Step 1. Initialization
        LB = -inf
        UB = inf
        theta = 0.
        xi = 10e-3
        k = 0
        epsilon = 1e-4
        M = 1e6

        # matrix representation for bilevel problem
        matrix_repn = BilevelMatrixRepn(self._instance)

        # 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 algorithm blocks
        algorithm_blocks = list()
        algorithm_blocks.append(submodel)

        all_vars = {key: var for (key, var) in matrix_repn._all_vars.items()}
        # for k,v in all_vars.items():
        #     if v.ub is None:
        #         v.setub(M)
        #     if v.lb is None:
        #         v.setlb(-M)

        # 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]
        }

        # 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
        b_vars = {
            key: var
            for (key, var) in matrix_repn._all_vars.items()
            if key in matrix_repn._b_var_ids - fixed_vars.keys()
        }

        # integer variables in SubModel
        i_vars = {
            key: var
            for (key, var) in matrix_repn._all_vars.items()
            if key in matrix_repn._i_var_ids - fixed_vars.keys()
        }

        # get constraint information related to constraint id, sign, and rhs value
        sub_cons = matrix_repn._cons_sense_rhs[submodel.name]

        # lower bounding block name -- unique naming convention
        _lower_model_name = '_p9'
        _lower_model_name = unique_component_name(self._instance,
                                                  _lower_model_name)

        # upper bounding block name -- unique naming convention
        _upper_model_name = '_p7'
        _upper_model_name = unique_component_name(self._instance,
                                                  _upper_model_name)

        while k <= self._k_max_iter:
            # Step 2. Lower Bounding
            # Solve problem (P5) master problem.
            # This includes equations (53), (12), (13), (15), and (54)
            # On iteration k = 0, (54) does not exist. Instead of implementing (54),
            # this approach applies problem (P9) which incorporates KKT-based tightening
            # constraints, and a projection and indicator constraint set.
            lower_bounding_master = getattr(self._instance, _lower_model_name,
                                            None)
            if lower_bounding_master is None:
                xfrm = TransformationFactory('pao.bilevel.highpoint')
                kwds = {'submodel_name': _lower_model_name}
                xfrm.apply_to(self._instance, **kwds)
                lower_bounding_master = getattr(self._instance,
                                                _lower_model_name)
                algorithm_blocks.append(lower_bounding_master)

                _c_var_bounds_rule = lambda m, k: c_vars[k].bounds
                _c_var_init_rule = lambda m, k: (c_vars[k].lb + c_vars[k].ub
                                                 ) / 2
                lower_bounding_master._iter_c = Var(
                    Any, bounds=(0, M), within=Reals,
                    dense=False)  # set (iter k, c_var_ids)
                lower_bounding_master._iter_c_tilde = Var(
                    c_vars.keys(),
                    bounds=_c_var_bounds_rule,
                    initialize=_c_var_init_rule,
                    within=Reals)  # set (iter k, c_var_ids)
                lower_bounding_master._iter_b = Var(
                    Any, within=Binary, bounds=(0, M),
                    dense=False)  # set (iter k, b_var_ids)
                lower_bounding_master._iter_i = Var(
                    Any, within=Integers, bounds=(0, M),
                    dense=False)  # set (iter k, i_var_ids)

                lower_bounding_master._iter_pi_tilde = Var(
                    sub_cons.keys(),
                    bounds=(0, M))  # set (iter k, sub_cons_ids)
                lower_bounding_master._iter_pi = Var(
                    Any, bounds=(0, M),
                    dense=False)  # set (iter k, sub_cons_ids)
                lower_bounding_master._iter_t = Var(
                    Any, bounds=(0, M),
                    dense=False)  # set (iter k, sub_cons_ids)
                lower_bounding_master._iter_lambda = Var(
                    Any, bounds=(0, M),
                    dense=False)  # set (iter k, sub_cons_ids)
                m = lower_bounding_master  # shorthand reference to model

            if k == 0:
                # constraint (74)
                lhs_expr = 0.
                rhs_expr = 0.
                for _vid, var in c_vars.items():
                    (C, C_q,
                     C_constant) = matrix_repn.cost_vectors(submodel, var)
                    coef = float(C)  # + dot(C_q,_fixed))
                    ref = m._map[var]
                    lhs_expr += coef * ref
                    rhs_expr += coef * m._iter_c_tilde[_vid]
                expr = lhs_expr >= rhs_expr
                if not type(expr) is bool:
                    lower_bounding_master.KKT_tight1 = Constraint(
                        expr=lhs_expr >= rhs_expr)

                # constraint (75a)
                lower_bounding_master.KKT_tight2a = Constraint(sub_cons.keys())
                lhs_expr_a = {key: 0. for key in sub_cons.keys()}
                rhs_expr_a = {key: 0. for key in sub_cons.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)
                    for _cid in sub_cons.keys():
                        idx = list(sub_cons.keys()).index(_cid)
                        lhs_expr_a[_cid] += float(
                            coef[idx]) * m._iter_c_tilde[_vid]

                for var in {**b_vars, **i_vars, **fixed_vars}.values():
                    (A, A_q, sign,
                     b) = matrix_repn.coef_matrices(submodel, var)
                    coef = A  #+ dot(A_q.toarray(), _fixed)
                    for _cid in sub_cons.keys():
                        idx = list(sub_cons.keys()).index(_cid)
                        ref = m._map[var]
                        rhs_expr_a[_cid] += -float(coef[idx]) * ref

                for _cid, b in sub_cons.items():
                    (_, sign) = _cid
                    rhs_expr_a[_cid] += b
                    if sign == 'l' or sign == 'g->l':
                        expr = lhs_expr_a[_cid] <= rhs_expr_a[_cid]
                    if sign == 'e' or sign == 'g':
                        raise Exception(
                            'Problem encountered, this problem is not in standard form.'
                        )
                    if not type(expr) is bool:
                        lower_bounding_master.KKT_tight2a[_cid] = expr
                    else:
                        lower_bounding_master.KKT_tight2a[
                            _cid] = Constraint.Skip

                # constraint (75b)
                lower_bounding_master.KKT_tight2b = Constraint(c_vars.keys())
                lhs_expr_b = {key: 0. for key in c_vars.keys()}
                rhs_expr_b = {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)
                    for _cid in sub_cons.keys():
                        idx = list(sub_cons.keys()).index(_cid)
                        lhs_expr_b[_vid] += float(
                            coef[idx]) * m._iter_pi_tilde[_cid]

                    (C, C_q,
                     C_constant) = matrix_repn.cost_vectors(submodel, var)
                    rhs_expr_b[_vid] = float(C)  #+ dot(C_q,_fixed))

                    expr = lhs_expr_b[_vid] >= rhs_expr_b[_vid]
                    if not type(expr) is bool:
                        lower_bounding_master.KKT_tight2b[_vid] = expr
                    else:
                        lower_bounding_master.KKT_tight2b[
                            _vid] = Constraint.Skip

                # constraint (76a)
                lower_bounding_master.KKT_tight3a = ComplementarityList()
                for _vid in c_vars.keys():
                    lower_bounding_master.KKT_tight3a.add(
                        complements(m._iter_c_tilde[_vid] >= 0,
                                    lhs_expr_b[_vid] - rhs_expr_b[_vid] >= 0))

                # constraint (76b)
                lower_bounding_master.KKT_tight3b = ComplementarityList()
                for _cid in sub_cons.keys():
                    lower_bounding_master.KKT_tight3b.add(
                        complements(m._iter_pi_tilde[_cid] >= 0,
                                    rhs_expr_a[_cid] - lhs_expr_a[_cid] >= 0))

                # constraint (77a)
                lower_bounding_master.KKT_tight4a = Constraint(c_vars.keys())
                for _vid in c_vars.keys():
                    lower_bounding_master.KKT_tight4a[
                        _vid] = m._iter_c_tilde[_vid] >= 0

                # constraint (77b)
                lower_bounding_master.KKT_tight4b = Constraint(sub_cons.keys())
                for _cid in sub_cons.keys():
                    lower_bounding_master.KKT_tight4b[
                        _cid] = m._iter_pi_tilde[_cid] >= 0

            # solve the HPR and check the termination condition
            lower_bounding_master.activate()
            TransformationFactory('mpec.simple_disjunction').apply_to(
                lower_bounding_master)
            bigm.apply_to(lower_bounding_master)

            with pyomo.opt.SolverFactory(solver) as opt:
                self.results.append(
                    opt.solve(lower_bounding_master,
                              tee=self._tee,
                              timelimit=self._timelimit))
            if not _check_termination_condition(self.results[-1]):
                raise Exception(
                    'The lower-bounding master is infeasible or sub-optimal.')

            # the LB should be a sequence of non-decreasing lower-bounds
            _lb = [
                value(odata)
                for odata in self._instance.component_objects(Objective)
                if odata.parent_block() == self._instance
            ][0]
            if _lb < LB:
                raise Exception(
                    'The lower-bound should be non-decreasing; a decreasing lower-bound indicates an algorithm issue.'
                )
            LB = max(LB, _lb)
            lower_bounding_master.deactivate()

            # Step 3. Termination
            if UB - LB < xi:
                # print(UB)
                # print(LB)
                if self._instance.solutions.solutions:
                    self._instance.solutions.select(0, ignore_fixed_vars=True)
                stop_time = time.time()
                self.wall_time = stop_time - start_time
                self.results_obj = self._setup_results_obj()
                return pyutilib.misc.Bunch(rc=getattr(opt, '_rc', None),
                                           log=getattr(opt, '_log', None))

            # fix the upper-level (master) variables to solve (P6) and (P7)
            for key, var in fixed_vars.items():
                var.fix(var.value)

            # Step 4. Subproblem 1
            # Solve problem (P6) lower-level problem for fixed upper-level optimal vars.
            # In iteration k=0, this first subproblem is always feasible; furthermore, the
            # optimal solution to (P5), alternatively (P9), will also be feasible to (P6).
            with pyomo.opt.SolverFactory(solver) as _opt:
                _results = _opt.solve(submodel,
                                      tee=self._tee,
                                      timelimit=self._timelimit)
            if not _check_termination_condition(_results):
                raise Exception(
                    'The lower-level subproblem with fixed upper-level variables is infeasible or sub-optimal.'
                )

            theta = [
                value(odata) for odata in submodel.component_objects(Objective)
            ][0]

            # solution for all variable values
            _fixed = array(
                [var.value for (key, var) in matrix_repn._all_vars.items()])

            # temporary dictionary for b_vars
            _b_vars_subproblem1 = dict()
            for _vid, var in b_vars.items():
                _b_vars_subproblem1[_vid] = var.value

            # temporary dictionary for i_vars
            _i_vars_subproblem1 = dict()
            for _vid, var in i_vars.items():
                _i_vars_subproblem1[_vid] = var.value

            # Step 5. Subproblem 2
            # Solve problem (P7) upper bounding problem for fixed upper-level optimal vars.
            upper_bounding_subproblem = getattr(self._instance,
                                                _upper_model_name, None)
            if upper_bounding_subproblem is None:
                xfrm = TransformationFactory('pao.bilevel.highpoint')
                kwds = {'submodel_name': _upper_model_name}
                xfrm.apply_to(self._instance, **kwds)
                upper_bounding_subproblem = getattr(self._instance,
                                                    _upper_model_name)
                algorithm_blocks.append(upper_bounding_subproblem)

                for odata in upper_bounding_subproblem.component_objects(
                        Objective):
                    upper_bounding_subproblem.del_component(odata)

            # solve for the master problem objective value for just the lower level variables
            upper_bounding_subproblem.del_component('objective')
            obj_constant = 0.
            obj_expr = 0.
            for var in {**c_vars, **b_vars, **i_vars}.values():
                (C, C_q,
                 C_constant) = matrix_repn.cost_vectors(self._instance, var)
                if obj_constant == 0. and C_constant != 0.:
                    obj_constant += C_constant  # only add the constant once
                obj_expr += float(C + dot(C_q, _fixed)) * var
            upper_bounding_subproblem.objective = Objective(expr=obj_expr +
                                                            obj_constant)

            # include lower bound constraint on the subproblem objective
            upper_bounding_subproblem.del_component('theta_pareto')
            sub_constant = 0.
            sub_expr = 0.
            for var in all_vars.values():
                (C, C_q, C_constant) = matrix_repn.cost_vectors(submodel, var)
                if sub_constant == 0. and C_constant != 0.:
                    sub_constant += C_constant  # only add the constant once
                sub_expr += float(C + dot(C_q, _fixed)) * var
            upper_bounding_subproblem.theta_pareto = Constraint(
                expr=sub_expr + sub_constant >= theta)

            with pyomo.opt.SolverFactory(solver) as opt:
                self.results.append(
                    opt.solve(upper_bounding_subproblem,
                              tee=self._tee,
                              timelimit=self._timelimit))
            if _check_termination_condition(self.results[-1]):
                # calculate new upper bound
                obj_constant = 0.
                obj_expr = 0.
                for var in fixed_vars.values():
                    (C, C_q, C_constant) = matrix_repn.cost_vectors(
                        self._instance, var)
                    if obj_constant == 0. and C_constant != 0.:
                        obj_constant += C_constant  # only add the constant once
                    obj_expr += float(C + dot(C_q, _fixed)) * var.value
                # line 16 of decomposition algorithm
                _ub = obj_expr + obj_constant + [
                    value(odata) for odata in
                    upper_bounding_subproblem.component_objects(Objective)
                ][0]
                UB = min(UB, _ub)

                # unfix the upper-level variables
                for var in fixed_vars.values():
                    var.unfix()

                # fix the solution for submodel binary variables
                for _vid, var in b_vars.items():
                    m._iter_b[(k, _vid)].fix(var.value)

                # fix the solution for submodel integer variables
                for _vid, var in i_vars.items():
                    m._iter_i[(k, _vid)].fix(var.value)
            else:  # infeasible problem
                # unfix the upper-level variables
                for var in fixed_vars.values():
                    var.unfix()

                # retrieve b_vars from temporary dictionary for subproblem1
                for _vid, var in b_vars.items():
                    m._iter_b[(k, _vid)].fix(_b_vars_subproblem1[_vid])

                # retrieve i_vars from temporary dictionary for subproblem1
                for _vid, var in i_vars.items():
                    m._iter_i[(k, _vid)].fix(_i_vars_subproblem1[_vid])

            # Step 6. Tightening the Master Problem
            projections = getattr(lower_bounding_master, 'projections', None)
            if projections is None:
                lower_bounding_master.projections = Block(Any)
                projections = lower_bounding_master.projections

            # constraint (79)
            projections[k].projection1 = Constraint(sub_cons.keys())
            lhs_expr_proj = {key: 0. for key in sub_cons.keys()}
            rhs_expr_proj = {key: 0. for key in sub_cons.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)
                for _cid in sub_cons.keys():
                    idx = list(sub_cons.keys()).index(_cid)
                    lhs_expr_proj[_cid] += float(
                        coef[idx]) * m._iter_c[(k, _vid)]

            for _vid, var in b_vars.items():
                (A, A_q, sign, b) = matrix_repn.coef_matrices(submodel, var)
                coef = A + dot(A_q.toarray(), _fixed)
                for _cid in sub_cons.keys():
                    idx = list(sub_cons.keys()).index(_cid)
                    rhs_expr_proj[_cid] += -float(coef[idx]) * m._iter_b[
                        (k, _vid)]

            for _vid, var in i_vars.items():
                (A, A_q, sign, b) = matrix_repn.coef_matrices(submodel, var)
                coef = A + dot(A_q.toarray(), _fixed)
                for _cid in sub_cons.keys():
                    idx = list(sub_cons.keys()).index(_cid)
                    rhs_expr_proj[_cid] += -float(coef[idx]) * m._iter_i[
                        (k, _vid)]

            for _vid, var in fixed_vars.items():
                (A, A_q, sign, b) = matrix_repn.coef_matrices(submodel, var)
                coef = A + dot(A_q.toarray(), _fixed)
                for _cid in sub_cons.keys():
                    idx = list(sub_cons.keys()).index(_cid)
                    rhs_expr_proj[_cid] += -float(coef[idx]) * var

            for _cid, b in sub_cons.items():
                (id, sign) = _cid
                rhs_expr_proj[_cid] += b
                if sign == 'l' or sign == 'g->l':
                    projections[k].projection1[
                        _cid] = lhs_expr_proj[_cid] - m._iter_t[
                            (k, _cid)] <= rhs_expr_proj[_cid]
                if sign == 'e' or sign == 'g':
                    raise Exception(
                        'Problem encountered, this problem is not in standard form.'
                    )

            # constraint (80a)
            projections[k].projection2a = Constraint(c_vars.keys())
            for _vid in c_vars.keys():
                projections[k].projection2a[_vid] = m._iter_c[(k, _vid)] >= 0

            # constraint (80b)
            projections[k].projection2b = Constraint(sub_cons.keys())
            for _cid in sub_cons.keys():
                projections[k].projection2b[_cid] = m._iter_t[(k, _cid)] >= 0

            # constraint (82)
            projections[k].projection3 = Block()
            projections[k].projection3.indicator = Disjunct()
            projections[k].projection3.block = Disjunct()
            projections[k].projection3.disjunct = Disjunction(expr=[
                projections[k].projection3.indicator,
                projections[k].projection3.block
            ])

            projections[k].projection3.indicator.cons_feas = Constraint(
                expr=sum(m._iter_t[(k, _cid)]
                         for _cid in sub_cons.keys()) >= epsilon)

            disjunction = projections[k].projection3.block

            # constraint (82a)
            lhs_constant = 0.
            lhs_expr = 0.
            rhs_constant = 0.
            rhs_expr = 0.
            for _vid, var in {**c_vars, **b_vars, **i_vars}.items():
                (C, C_q, C_constant) = matrix_repn.cost_vectors(submodel, var)
                lhs_expr += float(C + dot(C_q, _fixed)) * var
                if var.is_continuous():
                    rhs_expr += float(C + dot(C_q, _fixed)) * m._iter_c[(k,
                                                                         _vid)]
                if var.is_binary():
                    rhs_expr += float(C + dot(C_q, _fixed)) * m._iter_b[(k,
                                                                         _vid)]
                if var.is_integer():
                    rhs_expr += float(C + dot(C_q, _fixed)) * m._iter_i[(k,
                                                                         _vid)]
            disjunction.projection3a = Constraint(
                expr=lhs_expr + lhs_constant >= rhs_expr + rhs_constant)

            # constraint (82b)
            disjunction.projection3b = Constraint(sub_cons.keys())
            for _cid in sub_cons.keys():
                (_, sign) = _cid
                if sign == 'l' or sign == 'g->l':
                    expr = lhs_expr_proj[_cid] <= rhs_expr_proj[_cid]
                if sign == 'e' or sign == 'g':
                    raise Exception(
                        'Problem encountered, this problem is not in standard form.'
                    )
                if not type(expr) is bool:
                    disjunction.projection3b[_cid] = expr
                else:
                    disjunction.projection3b[_cid] = Constraint.Skip

            # constraint (82c)
            disjunction.projection3c = Constraint(c_vars.keys())
            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)
                idx = list(sub_cons.keys()).index(_cid)
                lhs_expr[_vid] += float(coef[idx]) * m._iter_pi[(k, _cid)]

                (C, C_q, C_constant) = matrix_repn.cost_vectors(submodel, var)
                rhs_expr[_vid] = float(C + dot(C_q, _fixed))

                expr = lhs_expr[_vid] >= rhs_expr[_vid]
                if not type(expr) is bool:
                    disjunction.projection3c[_vid] = expr
                else:
                    disjunction.projection3c[_vid] = Constraint.Skip

            # constraint (82d)
            disjunction.projection3d = ComplementarityList()
            for _vid in c_vars.keys():
                disjunction.projection3d.add(
                    complements(m._iter_c[(k, _vid)] >= 0,
                                lhs_expr[_vid] - rhs_expr[_vid] >= 0))

            # constraint (82e)
            disjunction.projection3e = ComplementarityList()
            for _cid in sub_cons.keys():
                disjunction.projection3e.add(
                    complements(
                        m._iter_pi[(k, _cid)] >= 0,
                        rhs_expr_proj[_cid] - lhs_expr_proj[_cid] >= 0))

            # constraint (82f)
            disjunction.projection3f = Constraint(c_vars.keys())
            for _vid in c_vars.keys():
                disjunction.projection3f[_vid] = m._iter_c[(k, _vid)] >= 0

            # constraint (82g)
            disjunction.projection3g = Constraint(sub_cons.keys())
            for _cid in sub_cons.keys():
                disjunction.projection3g[_cid] = m._iter_pi[(k, _cid)] >= 0

            # constraint (83a)
            projections[k].projection4a = Constraint(c_vars.keys())
            lhs_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)
                for _cid in sub_cons.keys():
                    idx = list(sub_cons.keys()).index(_cid)
                    lhs_expr[_vid] += float(
                        coef[idx]) * m._iter_lambda[(k, _cid)]

                expr = lhs_expr[_vid] >= 0
                if not type(expr) is bool:
                    projections[k].projection4a[_vid] = expr
                else:
                    projections[k].projection4a[_vid] = Constraint.Skip

            # constraint (83b)
            projections[k].projection4b = ComplementarityList()
            for _vid in c_vars.keys():
                projections[k].projection4b.add(
                    complements(m._iter_c[(k, _vid)] >= 0,
                                lhs_expr[_vid] >= 0))

            # constraint (84a)
            projections[k].projection5a = Constraint(sub_cons.keys())
            for _cid in sub_cons.keys():
                projections[k].projection5a[_cid] = 1 - m._iter_lambda[
                    (k, _cid)] >= 0

            # constraint (84b)
            projections[k].projection5b = ComplementarityList()
            for _cid in sub_cons.keys():
                projections[k].projection5b.add(
                    complements(m._iter_t[(k, _cid)] >= 0,
                                1 - m._iter_lambda[(k, _cid)] >= 0))

            # constraint (85)
            projections[k].projection6 = ComplementarityList()
            for _cid in sub_cons.keys():
                projections[k].projection6.add(
                    complements(
                        m._iter_lambda[(k, _cid)] >= 0, rhs_expr_proj[_cid] -
                        lhs_expr_proj[_cid] + m._iter_t[(k, _cid)] >= 0))

            k = k + 1
            # Step 7. Loop
            if UB - LB < xi:
                # print(UB)
                # print(LB)
                if self._instance.solutions.solutions:
                    self._instance.solutions.select(0, ignore_fixed_vars=True)
                stop_time = time.time()
                self.wall_time = stop_time - start_time
                self.results_obj = self._setup_results_obj()
                return pyutilib.misc.Bunch(rc=getattr(opt, '_rc', None),
                                           log=getattr(opt, '_log', None))
Exemple #7
0
    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
                    y >= 0
                    A2*x + B2*y <= b2

        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
        #
        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 canonical representation
            #
            o_terms = generate_canonical_repn(odata.expr, compute_values=False)
            for i in range(len(o_terms.variables)):
                var = o_terms.variables[i]
                if var.parent_component().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[i]
                if not id_ in sids_set:
                    sids_set.add(id_)
                    sids_list.append(id_)
            # 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.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_canonical_repn(cdata.body, compute_values=False)
            for i in range(len(c_terms.variables)):
                var = c_terms.variables[i]
                if var.parent_component().name in self._fixed_upper_vars:
                    continue
                id_ = id(var)
                B2.setdefault(id_, {}).setdefault(id(cdata), c_terms.linear[i])
                if not id_ in sids_set:
                    sids_set.add(id_)
                    sids_list.append(id_)
        #
        # 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].cname()
                               if utmp[x][1] is None else utmp[x][1].cname())
            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
Exemple #8
0
def create_model_replacing_LL_with_kkt(repn):
    """
    TODO - Document this transformation
    """
    U = repn.U
    LL = repn.U.LL
    N = len(LL)

    #
    # Create Pyomo model
    #
    M = pe.ConcreteModel()
    M.U = pe.Block()
    M.L = pe.Block(range(N))
    M.kkt = pe.Block(range(N))

    # upper- and lower-level variables
    pyomo_util.add_variables(M.U, U)
    for i in range(N):
        L = LL[i]
        # lower-level variables
        pyomo_util.add_variables(M.L[i], L)
        # dual variables
        M.kkt[i].lam = pe.Var(range(len(L.b)))  # equality constraints
        M.kkt[i].nu = pe.Var(range(len(L.x)),
                             within=pe.NonNegativeReals)  # variable bounds

    # objective
    e = pyomo_util.dot(U.c[U], U.x, num=1) + U.d
    for i in range(N):
        L = LL[i]
        e += pyomo_util.dot(U.c[L], L.x, num=1)
    M.o = pe.Objective(expr=e)

    # upper-level constraints
    pyomo_util.add_linear_constraints(M.U, U.A, U, L, U.b, U.inequalities)
    for i in range(N):
        # lower-level constraints
        L = LL[i]
        pyomo_util.add_linear_constraints(M.L[i], L.A, U, L, L.b,
                                          L.inequalities)

    for i in range(N):
        L = LL[i]
        # stationarity
        M.kkt[i].stationarity = pe.ConstraintList()
        # L_A_L' * lam
        L_A_L_T = L.A[L].transpose().todok()
        X = pyomo_util.dot(L_A_L_T, M.kkt[i].lam)
        if L.c[L] is not None:
            for k in range(len(L.c[L])):
                M.kkt[i].stationarity.add(L.c[L][k] + X[k] -
                                          M.kkt[i].nu[k] == 0)

    for i in range(N):
        # complementarity slackness - variables
        M.kkt[i].slackness = ComplementarityList()
        for j in M.kkt[i].nu:
            M.kkt[i].slackness.add(
                complements(M.L[i].xR[j] >= 0, M.kkt[i].nu[j] >= 0))

    return M