def _f(m: Block): m.lmbda = Var(vertices, domain=NonNegativeReals) # 非负 m.y = Var(simplices, domain=Binary) # 二进制 m.a0 = Constraint(dimensions, rule=lambda m, d: sum(m.lmbda[v] * pointsT[d][v] for v in vertices) == input[d]) if bound == 'eq': m.a1 = Constraint(expr=output == sum(m.lmbda[v] * values[v] for v in vertices)) elif bound == 'lb': m.a1 = Constraint(expr=output <= sum(m.lmbda[v] * values[v] for v in vertices)) elif bound == 'ub': m.a1 = Constraint(expr=output >= sum(m.lmbda[v] * values[v] for v in vertices)) else: raise RuntimeError("bound值错误!bound=" + bound) m.b = Constraint(expr=sum(m.lmbda[v] for v in vertices) == 1) # generate a map from vertex index to simplex index, # which avoids an n^2 lookup when generating the # constraint vertex_to_simplex = [[] for _ in vertices] for s, simplex in enumerate(tri.simplices): for v in simplex: vertex_to_simplex[v].append(s) m.c0 = Constraint(vertices, rule=lambda m, v: m.lmbda[v] <= sum(m.y[s] for s in vertex_to_simplex[v])) m.c1 = Constraint(expr=sum(m.y[s] for s in simplices) == 1) return m
def dlog(m: Block, tri: qhull.Delaunay, values: List[float], input: List[SimpleVar] = None, output: SimpleVar = None, bound: str = 'eq', **kw): values = np.array(values).tolist() ndim = len(input) nsimplices = len(tri.simplices) npoints = len(tri.points) pointsT = list(zip(*tri.points)) # create index objects dimensions = list(range(ndim)) simplices = list(range(nsimplices)) # 跟单纯形 数量一致 vertices = list(range(npoints)) bound = bound.lower() L = int(math.ceil(math.log2(nsimplices))) L_Range = list(range(L)) vp = [0, 1, 2] # m.lmbda = Var(simplices, vp, domain=NonNegativeReals) # 非负 m.a0 = Constraint( dimensions, rule=lambda m, d: sum(m.lmbda[s, v] * pointsT[d][tri.simplices[s][v]] for s in simplices for v in vp) == input[d]) if bound == 'eq': m.a1 = Constraint(expr=output == sum( m.lmbda[s, v] * values[tri.simplices[s][v]] for s in simplices for v in vp)) elif bound == 'lb': m.a1 = Constraint( expr=output <= sum(m.lmbda[s, v] * values[tri.simplices[s][v]] for s in simplices for v in vp)) elif bound == 'ub': m.a1 = Constraint( expr=output >= sum(m.lmbda[s, v] * values[tri.simplices[s][v]] for s in simplices for v in vp)) else: raise RuntimeError("bound值错误!bound=" + bound) m.b1 = Constraint(expr=sum(m.lmbda[s, v] for s in simplices for v in vp) == 1) m.y = Var(L_Range, domain=Binary) # 二进制 m.c0 = Constraint(L_Range, rule=lambda m, l: sum(m.lmbda[s, v] for s in simplices if bin(s)[2:].zfill(L)[l] == '1' for v in vp) <= m.y[l]) m.c1 = Constraint(L_Range, rule=lambda m, l: sum(m.lmbda[s, v] for s in simplices if bin(s)[2:].zfill(L)[l] == '0' for v in vp) <= 1 - m.y[l]) return m
def cc(m: Block, tri: qhull.Delaunay, values: List[float], input: List[SimpleVar] = None, output: SimpleVar = None, bound: str = 'eq', **kw): values = np.array(values).tolist() ndim = len(input) nsimplices = len(tri.simplices) npoints = len(tri.points) pointsT = list(zip(*tri.points)) # create index objects dimensions = list(range(ndim)) simplices = list(range(nsimplices)) # 跟单纯形 数量一致 vertices = list(range(npoints)) bound = bound.lower() m.lmbda = Var(vertices, domain=NonNegativeReals) # 非负 m.y = Var(simplices, domain=Binary) # 二进制 # m.y = Var(simplices, domain=NonNegativeReals, bounds=(0, 1)) # 二进制 m.a0 = Constraint(dimensions, rule=lambda m, d: sum(m.lmbda[v] * pointsT[d][v] for v in vertices) == input[d]) if bound == 'eq': m.a1 = Constraint(expr=output == sum(m.lmbda[v] * values[v] for v in vertices)) elif bound == 'lb': m.a1 = Constraint(expr=output <= sum(m.lmbda[v] * values[v] for v in vertices)) elif bound == 'ub': m.a1 = Constraint(expr=output >= sum(m.lmbda[v] * values[v] for v in vertices)) else: raise RuntimeError("bound值错误!bound=" + bound) m.b = Constraint(expr=sum(m.lmbda[v] for v in vertices) == 1) # generate a map from vertex index to simplex index, # which avoids an n^2 lookup when generating the # constraint vertex_to_simplex = [[] for _ in vertices] for s, simplex in enumerate(tri.simplices): for v in simplex: vertex_to_simplex[v].append(s) m.c0 = Constraint( vertices, rule=lambda m, v: m.lmbda[v] <= sum(m.y[s] for s in vertex_to_simplex[v])) m.c1 = Constraint(expr=sum(m.y[s] for s in simplices) == 1) return m
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