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