-
Notifications
You must be signed in to change notification settings - Fork 28
/
Gurobi.scala
288 lines (249 loc) · 9.19 KB
/
Gurobi.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
package optimus.optimization
/*
* /\\\\\
* /\\\///\\\
* /\\\/ \///\\\ /\\\\\\\\\ /\\\ /\\\
* /\\\ \//\\\ /\\\/////\\\ /\\\\\\\\\\\ \/// /\\\\\ /\\\\\ /\\\ /\\\ /\\\\\\\\\\
* \/\\\ \/\\\ \/\\\\\\\\\\ \////\\\//// /\\\ /\\\///\\\\\///\\\ \/\\\ \/\\\ \/\\\//////
* \//\\\ /\\\ \/\\\////// \/\\\ \/\\\ \/\\\ \//\\\ \/\\\ \/\\\ \/\\\ \/\\\\\\\\\\
* \///\\\ /\\\ \/\\\ \/\\\_/\\ \/\\\ \/\\\ \/\\\ \/\\\ \/\\\ \/\\\ \////////\\\
* \///\\\\\/ \/\\\ \//\\\\\ \/\\\ \/\\\ \/\\\ \/\\\ \//\\\\\\\\\ /\\\\\\\\\\
* \///// \/// \///// \/// \/// \/// \/// \///////// \//////////
*
* Copyright (C) 2014 Evangelos Michelioudakis, Anastasios Skarlatidis
*
* Optimus is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Optimus is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/lgpl-3.0.en.html>.
*/
import optimus.algebra._
import optimus.optimization.PreSolve.PreSolve
import optimus.optimization.ProblemStatus.ProblemStatus
import gurobi._
/**
* Gurobi solver.
*/
final class Gurobi extends AbstractMPSolver {
var nbRows = 0
var nbCols = 0
var solution = Array[Double]()
var objectiveValue = 0.0
var status = ProblemStatus.NOT_SOLVED
var env = new GRBEnv()
var model = new GRBModel(env)
/**
* Problem builder, should configure the solver and append
* mathematical model variables.
*
* @param nbRows rows in the model
* @param nbCols number of variables in the model
*/
def buildProblem(nbRows: Int, nbCols: Int) {
println {
""" _________ ______ _____ """ + "\n" +
""" __ ____/___ _________________ /____(_) """ + "\n" +
""" _ / __ _ / / /_ ___/ __ \_ __ \_ / """ + "\n" +
""" / /_/ / / /_/ /_ / / /_/ / /_/ / / """ + "\n" +
""" \____/ \__._/ /_/ \____//_____//_/ """ + "\n"
}
println("Model gurobi: " + nbRows + "x" + nbCols)
model.getEnv.set(GRB.IntParam.OutputFlag, 0)
this.nbRows = nbRows
this.nbCols = nbCols
val cols = (1 to nbCols).toArray
model.addVars(cols map (i => 0.0), cols map (i => GRB.INFINITY),
cols map (i => 0.0), cols map (i => GRB.CONTINUOUS), cols map ("x" + _))
model.update()
}
/**
* Get value of the variable in the specified position. Solution
* should exist in order for a value to exist.
*
* @param colId position of the variable
* @return the value of the variable in the solution
*/
def getValue(colId: Int): Double = solution(colId)
/**
* Set bounds of variable in the specified position.
*
* @param colId position of the variable
* @param lower domain lower bound
* @param upper domain upper bound
*/
def setBounds(colId: Int, lower: Double, upper: Double) {
val GRBVar = model.getVar(colId)
GRBVar.set(GRB.DoubleAttr.LB, lower)
GRBVar.set(GRB.DoubleAttr.UB, upper)
}
/**
* Set upper bound to unbounded (infinite)
*
* @param colId position of the variable
*/
def setUnboundUpperBound(colId: Int) {
model.getVar(colId).set(GRB.DoubleAttr.UB, GRB.INFINITY)
}
/**
* Set lower bound to unbounded (infinite)
*
* @param colId position of the variable
*/
def setUnboundLowerBound(colId: Int) {
model.getVar(colId).set(GRB.DoubleAttr.LB, -GRB.INFINITY)
}
/**
* Set the column/variable as an integer variable
*
* @param colId position of the variable
*/
def setInteger(colId: Int) {
model.getVar(colId).set(GRB.CharAttr.VType, GRB.INTEGER)
}
/**
* Set the column / variable as an binary integer variable
*
* @param colId position of the variable
*/
def setBinary(colId: Int) {
model.getVar(colId).set(GRB.CharAttr.VType, GRB.BINARY)
}
/**
* Set the column/variable as a float variable
*
* @param colId position of the variable
*/
def setFloat(colId: Int) {
model.getVar(colId).set(GRB.CharAttr.VType, GRB.CONTINUOUS)
}
/**
* Add objective expression to be optimized by the solver.
*
* @param objective the expression to be optimized
* @param minimize flag for minimization instead of maximization
*/
def addObjective(objective: Expression, minimize: Boolean) = {
objective.getOrder match {
case ExpressionOrder.HIGHER => throw new IllegalArgumentException("Higher than quadratic: " + objective)
case ExpressionOrder.QUADRATIC =>
val QExpression = new GRBQuadExpr
val iterator = objective.terms.iterator
while(iterator.hasNext) {
iterator.advance()
val indexes = decode(iterator.key)
if(indexes.length == 1) QExpression.addTerm(iterator.value, model.getVar(indexes.head))
else QExpression.addTerm(iterator.value, model.getVar(indexes.head), model.getVar(indexes(1)))
}
model.setObjective(QExpression, if (minimize) 1 else -1)
case ExpressionOrder.LINEAR =>
val LExpression = new GRBLinExpr
val variables = objective.terms.keys.map(code => model.getVar(decode(code).head))
LExpression.addTerms(objective.terms.values, variables)
model.setObjective(LExpression, if (minimize) 1 else -1)
case ExpressionOrder.CONSTANT =>
val CExpression = new GRBLinExpr
CExpression.addConstant(objective.constant)
model.setObjective(CExpression, if (minimize) 1 else -1)
}
model.update()
}
/**
* Add a mathematical programming constraint to the solver.
*
* @param mpConstraint the mathematical programming constraint
*/
def addConstraint(mpConstraint: MPConstraint) = {
nbRows += 1
val lhs = mpConstraint.constraint.lhs - mpConstraint.constraint.rhs
val rhs = -lhs.constant
val operator = mpConstraint.constraint.operator
val GRBOperator = operator match {
case ConstraintRelation.GE => GRB.GREATER_EQUAL
case ConstraintRelation.LE => GRB.LESS_EQUAL
case ConstraintRelation.EQ => GRB.EQUAL
}
lhs.getOrder match {
case ExpressionOrder.HIGHER => throw new IllegalArgumentException("Higher than quadratic: " + lhs)
case ExpressionOrder.QUADRATIC =>
val QExpression = new GRBQuadExpr
val iterator = lhs.terms.iterator
while(iterator.hasNext) {
iterator.advance()
val indexes = decode(iterator.key)
if(indexes.length == 1) QExpression.addTerm(iterator.value, model.getVar(indexes.head))
else QExpression.addTerm(iterator.value, model.getVar(indexes.head), model.getVar(indexes(1)))
}
model.addQConstr(QExpression, GRBOperator, rhs, "")
case ExpressionOrder.LINEAR =>
val LExpression = new GRBLinExpr
val iterator = lhs.terms.iterator
while(iterator.hasNext) {
iterator.advance()
LExpression.addTerm(iterator.value, model.getVar(decode(iterator.key).head))
}
model.addConstr(LExpression, GRBOperator, rhs, "")
}
}
/**
* Solve the problem.
*
* @return status code indicating the nature of the solution
*/
def solveProblem(preSolve: PreSolve = PreSolve.DISABLE): ProblemStatus = {
if(preSolve == PreSolve.CONSERVATIVE) model.getEnv.set(GRB.IntParam.Presolve, 1)
else if(preSolve == PreSolve.AGGRESSIVE) model.getEnv.set(GRB.IntParam.Presolve, 2)
model.update()
model.optimize()
var optimizationStatus = model.get(GRB.IntAttr.Status)
if (optimizationStatus == GRB.INF_OR_UNBD) {
model.getEnv.set(GRB.IntParam.Presolve, 0)
model.optimize()
optimizationStatus = model.get(GRB.IntAttr.Status)
ProblemStatus.UNBOUNDED
}
else if (optimizationStatus == GRB.OPTIMAL) {
solution = Array.tabulate(nbCols)(col => model.getVar(col).get(GRB.DoubleAttr.X))
objectiveValue = model.get(GRB.DoubleAttr.ObjVal)
ProblemStatus.OPTIMAL
}
else if (optimizationStatus == GRB.INFEASIBLE) {
model.computeIIS()
ProblemStatus.INFEASIBLE
}
else if (optimizationStatus == GRB.UNBOUNDED) {
ProblemStatus.UNBOUNDED
}
else {
solution = Array.tabulate(nbCols)(col => model.getVar(col).get(GRB.DoubleAttr.X))
println("Optimization stopped with status = " + optimizationStatus)
ProblemStatus.SUBOPTIMAL
}
}
/**
* Release memory associated to the problem and the environment as well as
* the gurobi license.
*/
def release() = {
model.dispose()
env.release()
env.dispose()
}
/**
* Set a time limit for solver optimization. After the limit
* is reached the solver stops running.
*
* @param limit the time limit
*/
def setTimeout(limit: Int) {
require(0 <= limit)
model.getEnv.set(GRB.DoubleParam.TimeLimit, limit.toDouble)
}
}