PySCIPOpt  4.3.0
Python Interface for the SCIP Optimization Suite
piecewise.py
Go to the documentation of this file.
1 
3 """
4 Approaches:
5  - mult_selection: multiple selection model
6  - convex_comb_sos: model with SOS2 constraints
7  - convex_comb_dis: convex combination with binary variables (disaggregated model)
8  - convex_comb_dis_log: convex combination with a logarithmic number of binary variables
9  - convex_comb_agg: convex combination with binary variables (aggregated model)
10  - convex_comb_agg_log: convex combination with a logarithmic number of binary variables
11 
12 Copyright (c) by Joao Pedro PEDROSO and Mikio KUBO, 2012
13 """
14 import math
15 import random
16 from pyscipopt import Model, quicksum, multidict
17 
18 def mult_selection(model,a,b):
19  """mult_selection -- add piecewise relation with multiple selection formulation
20  Parameters:
21  - model: a model where to include the piecewise linear relation
22  - a[k]: x-coordinate of the k-th point in the piecewise linear relation
23  - b[k]: y-coordinate of the k-th point in the piecewise linear relation
24  Returns the model with the piecewise linear relation on added variables X, Y, and z.
25  """
26 
27  K = len(a)-1
28  w,z = {},{}
29  for k in range(K):
30  w[k] = model.addVar(lb=-model.infinity()) # do not name variables for avoiding clash
31  z[k] = model.addVar(vtype="B")
32  X = model.addVar(lb=a[0], ub=a[K], vtype="C")
33  Y = model.addVar(lb=-model.infinity())
34 
35  for k in range(K):
36  model.addCons(w[k] >= a[k]*z[k])
37  model.addCons(w[k] <= a[k+1]*z[k])
38 
39  model.addCons(quicksum(z[k] for k in range(K)) == 1)
40  model.addCons(X == quicksum(w[k] for k in range(K)))
41 
42  c = [float(b[k+1]-b[k])/(a[k+1]-a[k]) for k in range(K)]
43  d = [b[k]-c[k]*a[k] for k in range(K)]
44  model.addCons(Y == quicksum(d[k]*z[k] + c[k]*w[k] for k in range(K)))
45 
46  return X,Y,z
47 
48 
49 def convex_comb_sos(model,a,b):
50  """convex_comb_sos -- add piecewise relation with gurobi's SOS constraints
51  Parameters:
52  - model: a model where to include the piecewise linear relation
53  - a[k]: x-coordinate of the k-th point in the piecewise linear relation
54  - b[k]: y-coordinate of the k-th point in the piecewise linear relation
55  Returns the model with the piecewise linear relation on added variables X, Y, and z.
56  """
57  K = len(a)-1
58  z = {}
59  for k in range(K+1):
60  z[k] = model.addVar(lb=0, ub=1, vtype="C")
61  X = model.addVar(lb=a[0], ub=a[K], vtype="C")
62  Y = model.addVar(lb=-model.infinity(), vtype="C")
63 
64  model.addCons(X == quicksum(a[k]*z[k] for k in range(K+1)))
65  model.addCons(Y == quicksum(b[k]*z[k] for k in range(K+1)))
66 
67  model.addCons(quicksum(z[k] for k in range(K+1)) == 1)
68  model.addConsSOS2([z[k] for k in range(K+1)])
69 
70  return X,Y,z
71 
72 
73 def convex_comb_dis(model,a,b):
74  """convex_comb_dis -- add piecewise relation with convex combination formulation
75  Parameters:
76  - model: a model where to include the piecewise linear relation
77  - a[k]: x-coordinate of the k-th point in the piecewise linear relation
78  - b[k]: y-coordinate of the k-th point in the piecewise linear relation
79  Returns the model with the piecewise linear relation on added variables X, Y, and z.
80  """
81  K = len(a)-1
82  wL,wR,z = {},{},{}
83  for k in range(K):
84  wL[k] = model.addVar(lb=0, ub=1, vtype="C")
85  wR[k] = model.addVar(lb=0, ub=1, vtype="C")
86  z[k] = model.addVar(vtype="B")
87  X = model.addVar(lb=a[0], ub=a[K], vtype="C")
88  Y = model.addVar(lb=-model.infinity(), vtype="C")
89 
90  model.addCons(X == quicksum(a[k]*wL[k] + a[k+1]*wR[k] for k in range(K)))
91  model.addCons(Y == quicksum(b[k]*wL[k] + b[k+1]*wR[k] for k in range(K)))
92  for k in range(K):
93  model.addCons(wL[k] + wR[k] == z[k])
94 
95  model.addCons(quicksum(z[k] for k in range(K)) == 1)
96 
97  return X,Y,z
98 
99 
100 def gray(i):
101  """returns i^int(i/2)"""
102  return i^(int(i/2))
103 
104 
105 def convex_comb_dis_log(model,a,b):
106  """convex_comb_dis_log -- add piecewise relation with a logarithmic number of binary variables
107  using the convex combination formulation.
108  Parameters:
109  - model: a model where to include the piecewise linear relation
110  - a[k]: x-coordinate of the k-th point in the piecewise linear relation
111  - b[k]: y-coordinate of the k-th point in the piecewise linear relation
112  Returns the model with the piecewise linear relation on added variables X, Y, and z.
113  """
114  K = len(a)-1
115  G = int(math.ceil((math.log(K)/math.log(2)))) # number of required bits
116  N = 1<<G # number of required variables
117  # print("K,G,N:",K,G,N
118  wL,wR,z = {},{},{}
119  for k in range(N):
120  wL[k] = model.addVar(lb=0, ub=1, vtype="C")
121  wR[k] = model.addVar(lb=0, ub=1, vtype="C")
122  X = model.addVar(lb=a[0], ub=a[K], vtype="C")
123  Y = model.addVar(lb=-model.infinity(), vtype="C")
124 
125  g = {}
126  for j in range(G):
127  g[j] = model.addVar(vtype="B")
128 
129  model.addCons(X == quicksum(a[k]*wL[k] + a[k+1]*wR[k] for k in range(K)))
130  model.addCons(Y == quicksum(b[k]*wL[k] + b[k+1]*wR[k] for k in range(K)))
131  model.addCons(quicksum(wL[k] + wR[k] for k in range(K)) == 1)
132 
133  # binary variables setup
134  for j in range(G):
135  ones = []
136  zeros = []
137  for k in range(K):
138  if k & (1<<j):
139  ones.append(k)
140  else:
141  zeros.append(k)
142  model.addCons(quicksum(wL[k] + wR[k] for k in ones) <= g[j])
143  model.addCons(quicksum(wL[k] + wR[k] for k in zeros) <= 1-g[j])
144 
145  return X,Y,wL,wR
146 
147 
148 
149 def convex_comb_agg(model,a,b):
150  """convex_comb_agg -- add piecewise relation convex combination formulation -- non-disaggregated.
151  Parameters:
152  - model: a model where to include the piecewise linear relation
153  - a[k]: x-coordinate of the k-th point in the piecewise linear relation
154  - b[k]: y-coordinate of the k-th point in the piecewise linear relation
155  Returns the model with the piecewise linear relation on added variables X, Y, and z.
156  """
157  K = len(a)-1
158  w,z = {},{}
159  for k in range(K+1):
160  w[k] = model.addVar(lb=0, ub=1, vtype="C")
161  for k in range(K):
162  z[k] = model.addVar(vtype="B")
163  X = model.addVar(lb=a[0], ub=a[K], vtype="C")
164  Y = model.addVar(lb=-model.infinity(), vtype="C")
165 
166  model.addCons(X == quicksum(a[k]*w[k] for k in range(K+1)))
167  model.addCons(Y == quicksum(b[k]*w[k] for k in range(K+1)))
168  model.addCons(w[0] <= z[0])
169  model.addCons(w[K] <= z[K-1])
170  for k in range(1,K):
171  model.addCons(w[k] <= z[k-1]+z[k])
172  model.addCons(quicksum(w[k] for k in range(K+1)) == 1)
173  model.addCons(quicksum(z[k] for k in range(K)) == 1)
174  return X,Y,z
175 
176 
177 
178 def convex_comb_agg_log(model,a,b):
179  """convex_comb_agg_log -- add piecewise relation with a logarithmic number of binary variables
180  using the convex combination formulation -- non-disaggregated.
181  Parameters:
182  - model: a model where to include the piecewise linear relation
183  - a[k]: x-coordinate of the k-th point in the piecewise linear relation
184  - b[k]: y-coordinate of the k-th point in the piecewise linear relation
185  Returns the model with the piecewise linear relation on added variables X, Y, and z.
186  """
187  K = len(a)-1
188  G = int(math.ceil((math.log(K)/math.log(2)))) # number of required bits
189  w,g = {},{}
190  for k in range(K+1):
191  w[k] = model.addVar(lb=0, ub=1, vtype="C")
192  for j in range(G):
193  g[j] = model.addVar(vtype="B")
194  X = model.addVar(lb=a[0], ub=a[K], vtype="C")
195  Y = model.addVar(lb=-model.infinity(), vtype="C")
196 
197  model.addCons(X == quicksum(a[k]*w[k] for k in range(K+1)))
198  model.addCons(Y == quicksum(b[k]*w[k] for k in range(K+1)))
199  model.addCons(quicksum(w[k] for k in range(K+1)) == 1)
200 
201  # binary variables setup
202  for j in range(G):
203  zeros,ones = [0],[]
204  # print(j,"\tinit zeros:",zeros,"ones:",ones
205  for k in range(1,K+1):
206  # print(j,k,"\t>zeros:",zeros,"ones:",ones
207  if (1 & gray(k)>>j) == 1 and (1 & gray(k-1)>>j) == 1:
208  ones.append(k)
209  if (1 & gray(k)>>j) == 0 and (1 & gray(k-1)>>j) == 0:
210  zeros.append(k)
211  # print(j,k,"\tzeros>:",zeros,"ones:",ones
212 
213  # print(j,"\tzeros:",zeros,"ones:",ones
214  model.addCons(quicksum(w[k] for k in ones) <= g[j])
215  model.addCons(quicksum(w[k] for k in zeros) <= 1-g[j])
216 
217  return X,Y,w
218 
219 
220 if __name__ == "__main__":
221  # random.seed(1)
222 
223  a = [ -10, 10, 15, 25, 30, 35, 40, 45, 50, 55, 60, 70]
224  b = [ -20,-20, 15, -21, 0, 50, 18, 0, 15, 24, 10, 15]
225 
226  print("\n\n\npiecewise: multiple selection")
227  model = Model("multiple selection")
228  X,Y,z = mult_selection(model,a,b) # X,Y --> piecewise linear replacement of x,f(x) based on points a,b
229  # model using X and Y (and possibly other variables)
230  u = model.addVar(vtype="C", name="u")
231 
232  A = model.addCons(3*X + 4*Y <= 250, "A")
233  B = model.addCons(7*X - 2*Y + 3*u == 170, "B")
234  model.setObjective(2*X + 15*Y + 5*u, "maximize")
235  model.optimize()
236  print("X:",model.getVal(X))
237  print("Y:",model.getVal(Y))
238  print("u:",model.getVal(u))
239 
240  print("\n\n\npiecewise: disaggregated convex combination")
241  model = Model("disaggregated convex combination")
242  X,Y,z = convex_comb_dis(model,a,b)
243  u = model.addVar(vtype="C", name="u")
244 
245  A = model.addCons(3*X + 4*Y <= 250, "A")
246  B = model.addCons(7*X - 2*Y + 3*u == 170, "B")
247  model.setObjective(2*X + 15*Y + 5*u, "maximize")
248  model.optimize()
249  print("X:",model.getVal(X))
250  print("Y:",model.getVal(Y))
251  print("u:",model.getVal(u))
252 
253  print("\n\n\npiecewise: disaggregated convex combination, logarithmic number of variables")
254  model = Model("disaggregated convex combination (log)")
255  X,Y,z = convex_comb_dis(model,a,b)
256  u = model.addVar(vtype="C", name="u")
257 
258  A = model.addCons(3*X + 4*Y <= 250, "A")
259  B = model.addCons(7*X - 2*Y + 3*u == 170, "B")
260  model.setObjective(2*X + 15*Y + 5*u, "maximize")
261  model.optimize()
262  print("X:",model.getVal(X))
263  print("Y:",model.getVal(Y))
264  print("u:",model.getVal(u))
265 
266  print("\n\n\npiecewise: SOS2 constraint")
267  model = Model("SOS2")
268  X,Y,w = convex_comb_sos(model,a,b)
269  u = model.addVar(vtype="C", name="u")
270 
271  A = model.addCons(3*X + 4*Y <= 250, "A")
272  B = model.addCons(7*X - 2*Y + 3*u == 170, "B")
273  model.setObjective(2*X + 15*Y + 5*u, "maximize")
274  model.optimize()
275  print("X:",model.getVal(X))
276  print("Y:",model.getVal(Y))
277  print("u:",model.getVal(u))
278 
279  print("\n\n\npiecewise: aggregated convex combination")
280  model = Model("aggregated convex combination")
281  X,Y,z = convex_comb_agg(model,a,b)
282  u = model.addVar(vtype="C", name="u")
283 
284  A = model.addCons(3*X + 4*Y <= 250, "A")
285  B = model.addCons(7*X - 2*Y + 3*u == 170, "B")
286  model.setObjective(2*X + 15*Y + 5*u, "maximize")
287  model.optimize()
288  print("X:",model.getVal(X))
289  print("Y:",model.getVal(Y))
290  print("u:",model.getVal(u))
291 
292  print("\n\n\npiecewise: aggregated convex combination, logarithmic number of variables")
293  model = Model("aggregated convex combination (log)")
294  X,Y,w = convex_comb_agg_log(model,a,b)
295  u = model.addVar(vtype="C", name="u")
296 
297  A = model.addCons(3*X + 4*Y <= 250, "A")
298  B = model.addCons(7*X - 2*Y + 3*u == 170, "B")
299  model.setObjective(2*X + 15*Y + 5*u, "maximize")
300  model.optimize()
301  print("X:",model.getVal(X))
302  print("Y:",model.getVal(Y))
303  print("u:",model.getVal(u))
piecewise.gray
def gray(i)
Definition: piecewise.py:100
pyscipopt.expr.quicksum
def quicksum(termlist)
Definition: expr.pxi:357
piecewise.mult_selection
def mult_selection(model, a, b)
Definition: piecewise.py:18
piecewise.convex_comb_dis
def convex_comb_dis(model, a, b)
Definition: piecewise.py:73
piecewise.convex_comb_sos
def convex_comb_sos(model, a, b)
Definition: piecewise.py:49
piecewise.convex_comb_agg_log
def convex_comb_agg_log(model, a, b)
Definition: piecewise.py:178
piecewise.convex_comb_dis_log
def convex_comb_dis_log(model, a, b)
Definition: piecewise.py:105
piecewise.convex_comb_agg
def convex_comb_agg(model, a, b)
Definition: piecewise.py:149