-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
922 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
180 changes: 180 additions & 0 deletions
180
src/examples/scala/com/decisionbrain/cplex/cp/SchedCalendar.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
/* | ||
* Source file provided under Apache License, Version 2.0, January 2004, | ||
* http://www.apache.org/licenses/ | ||
* (c) Copyright DecisionBrain SAS 2016,2017 | ||
*/ | ||
|
||
package com.decisionbrain.cplex.cp | ||
|
||
import com.decisionbrain.cplex.cp.CpModel._ | ||
import ilog.cp.IloCP | ||
|
||
/** | ||
* This is a problem of building five houses. The masonry, roofing, painting, etc. must be scheduled. Some tasks must | ||
* necessarily take place before others and these requirements are expressed through precedence constraints. | ||
* | ||
* There are two workers and each task requires a specific worker. The worker has a calendar of days off that must be | ||
* taken into account. The objective is to minimize the overall completion date. | ||
*/ | ||
object SchedCalendar { | ||
|
||
val nbWorkers = 3 | ||
|
||
val houses = List(31, 0, 90, 120, 90) // 5 houses with different release dates | ||
|
||
val tasks = List( | ||
("masonry", 35), // pair of task name and duration | ||
("carpentry", 15), | ||
("plumbing", 40), | ||
("ceiling", 15), | ||
("roofing", 5), | ||
("painting", 10), | ||
("windows", 5), | ||
("facade", 10), | ||
("garden", 5), | ||
("moving", 5) | ||
) | ||
|
||
|
||
|
||
implicit var model: CpModel = _ | ||
|
||
var allTaskVars: Map[String, IntervalVar] = _ | ||
var endVars: List[IntExpr] = _ | ||
var joeTaskVars: List[IntervalVar] = _ | ||
var jimTaskVars: List[IntervalVar] = _ | ||
|
||
var joeCalendar: NumToNumStepFunction = _ | ||
var jimCalendar: NumToNumStepFunction = _ | ||
|
||
def makeHouse(id: Int, rd: Int): (IntExpr, Iterable[IntervalVar], Iterable[IntervalVar], Iterable[IntervalVar]) = { | ||
|
||
val taskVars: Map[String, IntervalVar] = (for (task <- tasks; (tname, tduration) = task) | ||
yield (tname , model.intervalVar(sizeMin = tduration, sizeMax = tduration, name = "H" + id + "-" + tname)))(collection.breakOut) | ||
|
||
// The lines below are equivalent | ||
model.add(taskVars("masonry") < taskVars("carpentry")) | ||
model.add(taskVars("masonry") < taskVars("plumbing")) | ||
model.add(taskVars("masonry") < taskVars("ceiling")) | ||
model.add(taskVars("carpentry") < taskVars("roofing")) | ||
model.add(taskVars("ceiling") < taskVars("painting")) | ||
model.add(taskVars("roofing") < taskVars("windows")) | ||
model.add(taskVars("roofing") < taskVars("facade")) | ||
model.add(taskVars("plumbing") < taskVars("facade")) | ||
model.add(taskVars("roofing") < taskVars("garden")) | ||
model.add(taskVars("plumbing") < taskVars("garden")) | ||
model.add(taskVars("windows") < taskVars("moving")) | ||
model.add(taskVars("facade") < taskVars("moving")) | ||
model.add(taskVars("garden") < taskVars("moving")) | ||
model.add(taskVars("painting") < taskVars("moving")) | ||
|
||
taskVars("masonry").setStartMin(rd) | ||
|
||
val joeTaskVars = List(taskVars("masonry") | ||
, taskVars("carpentry") | ||
, taskVars("roofing") | ||
, taskVars("facade") | ||
, taskVars("garden") | ||
) | ||
|
||
val jimTaskVars = taskVars.values.filterNot(v => joeTaskVars.contains(v)) | ||
|
||
|
||
(model.endOf(taskVars("moving")), taskVars.values, joeTaskVars, jimTaskVars) | ||
} | ||
|
||
def build(): CpModel = { | ||
|
||
model = CpModel("SchedCumul") | ||
|
||
val results = for ((rd, index) <- houses.zipWithIndex) yield makeHouse(index, rd) | ||
|
||
endVars = results.map(_._1) | ||
allTaskVars = results.flatMap(_._2).map(v => (v.getName().getOrElse(""), v)).toMap | ||
joeTaskVars = results.flatMap(_._3) | ||
jimTaskVars = results.flatMap(_._4) | ||
|
||
model.add(noOverlap(joeTaskVars)) | ||
model.add(noOverlap(jimTaskVars)) | ||
|
||
|
||
joeCalendar = model.numToNumStepFunction | ||
joeCalendar.setValue(0, 2 * 365, 100) | ||
jimCalendar = model.numToNumStepFunction | ||
jimCalendar.setValue(0, 2 * 365, 100) | ||
|
||
/* WEEK ENDS. */ | ||
for (w <- 0 until 2 * 52) { | ||
joeCalendar.setValue(5 + (7 * w), 7 + (7 * w), 0) | ||
jimCalendar.setValue(5 + (7 * w), 7 + (7 * w), 0) | ||
} | ||
|
||
/* HOLIDAYS. */ | ||
joeCalendar.setValue(5, 12, 0) | ||
joeCalendar.setValue(124, 131, 0) | ||
joeCalendar.setValue(215, 236, 0) | ||
joeCalendar.setValue(369, 376, 0) | ||
joeCalendar.setValue(495, 502, 0) | ||
joeCalendar.setValue(579, 600, 0) | ||
jimCalendar.setValue(26, 40, 0) | ||
jimCalendar.setValue(201, 225, 0) | ||
jimCalendar.setValue(306, 313, 0) | ||
jimCalendar.setValue(397, 411, 0) | ||
jimCalendar.setValue(565, 579, 0) | ||
|
||
for (i <- joeTaskVars.indices) { | ||
joeTaskVars(i).setIntensity(joeCalendar) | ||
model.add(forbidStart(joeTaskVars(i), joeCalendar)) | ||
model.add(forbidEnd(joeTaskVars(i), joeCalendar)) | ||
} | ||
for (i <- jimTaskVars.indices) { | ||
jimTaskVars(i).setIntensity(jimCalendar) | ||
model.add(forbidStart(jimTaskVars(i), jimCalendar)) | ||
model.add(forbidEnd(jimTaskVars(i), jimCalendar)) | ||
} | ||
|
||
|
||
model.add(minimize(max(endVars))) | ||
|
||
model | ||
} | ||
|
||
def solve(): Boolean = { | ||
|
||
println(s"Solving model $model....") | ||
|
||
// model.exportModel("SchedCalendar.cpo") | ||
|
||
model.cp.setParameter(IloCP.IntParam.FailLimit, 10000) | ||
|
||
// val status = model.solve(timeLimit=20, logPeriod=3000) | ||
val status = model.solve() | ||
|
||
if (status) { | ||
println(s"Solution status: $status") | ||
println("Solution with objective " + model.getObjectiveValue()) | ||
println("Joe Calendar: " + joeCalendar) | ||
println("Jim Calendar: " + jimCalendar) | ||
println("Joe Schedule:") | ||
for (v <- SchedCalendar.joeTaskVars) | ||
println("\t" + model.getDomain(v)) | ||
println("Jim Schedule:") | ||
for (v <- SchedCalendar.jimTaskVars) | ||
println("\t" + model.getDomain(v)) | ||
} | ||
|
||
status | ||
} | ||
|
||
def run(): Boolean = { | ||
val model = build() | ||
val status = solve() | ||
model.end() | ||
status | ||
} | ||
|
||
def main(args: Array[String]): Unit = { | ||
run() | ||
} | ||
|
||
} |
129 changes: 129 additions & 0 deletions
129
src/examples/scala/com/decisionbrain/cplex/cp/SchedTime.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
/* | ||
* Source file provided under Apache License, Version 2.0, January 2004, | ||
* http://www.apache.org/licenses/ | ||
* (c) Copyright DecisionBrain SAS 2016,2017 | ||
*/ | ||
|
||
package com.decisionbrain.cplex.cp | ||
|
||
import com.decisionbrain.cplex.cp.CpModel._ | ||
import ilog.concert.IloNumToNumSegmentFunction | ||
import ilog.cp.IloCP | ||
|
||
/** | ||
* This is a problem of building a house. The masonry, roofing, painting, etc. must be scheduled. Some tasks must | ||
* necessarily take place before others and these requirements are expressed through precedence constraints. | ||
* | ||
* Moreover, there are earliness and tardiness costs associated with some tasks. The objective is to minimize these | ||
* costs. | ||
*/ | ||
object SchedTime { | ||
|
||
val nbTasks = 10 | ||
|
||
val tasks = List( | ||
("masonry", 35), // pair of task name and duration | ||
("carpentry", 15), | ||
("plumbing", 40), | ||
("ceiling", 15), | ||
("roofing", 5), | ||
("painting", 10), | ||
("windows", 5), | ||
("facade", 10), | ||
("garden", 5), | ||
("moving", 5) | ||
) | ||
|
||
def computeEarlinessCostExp(task: IntervalVar, rd: Double, weight: Double, useFunction: Boolean): NumExpr = { | ||
if (useFunction) { | ||
val arrX = Array(rd) | ||
val arrV = Array(-weight, 0.0) | ||
val f: NumToNumSegmentFunction = model.piecewiseLinearFunction(arrX, arrV, rd, 0.0) | ||
return startEval(task,f) | ||
} else { | ||
return weight * max(.0, rd - startOf(task)) | ||
} | ||
} | ||
|
||
def computeTardinessCostExp(task: IntervalVar, dd: Double, weight: Double, useFunction: Boolean): NumExpr = { | ||
if (useFunction) { | ||
val arrX = Array(dd) | ||
val arrV = Array(0.0, weight) | ||
val f = model.piecewiseLinearFunction(arrX, arrV, dd, 0.0) | ||
return endEval(task,f) | ||
} else { | ||
return weight * max(.0, endOf(task) - dd) | ||
} | ||
} | ||
|
||
implicit var model: CpModel = _ | ||
|
||
var taskVars: Map[String, IntervalVar] = _ | ||
|
||
def build(): CpModel = { | ||
|
||
model = CpModel("SchedCumul") | ||
|
||
taskVars = (for (task <- tasks; (tname, tduration) = task) | ||
yield (tname , model.intervalVar(sizeMin = tduration, sizeMax = tduration, name = tname))).toMap | ||
|
||
// precedence constraints | ||
model.add(taskVars("masonry") < taskVars("carpentry")) | ||
model.add(taskVars("masonry") < taskVars("plumbing")) | ||
model.add(taskVars("masonry") < taskVars("ceiling")) | ||
model.add(taskVars("carpentry") < taskVars("roofing")) | ||
model.add(taskVars("ceiling") < taskVars("painting")) | ||
model.add(taskVars("roofing") < taskVars("windows")) | ||
model.add(taskVars("roofing") < taskVars("facade")) | ||
model.add(taskVars("plumbing") < taskVars("facade")) | ||
model.add(taskVars("roofing") < taskVars("garden")) | ||
model.add(taskVars("plumbing") < taskVars("garden")) | ||
model.add(taskVars("windows") < taskVars("moving")) | ||
model.add(taskVars("facade") < taskVars("moving")) | ||
model.add(taskVars("garden") < taskVars("moving")) | ||
model.add(taskVars("painting") < taskVars("moving")) | ||
|
||
val useFunction = true | ||
val costExpr = (computeEarlinessCostExp(taskVars("masonry"), 25, 200.0, useFunction) | ||
+ computeEarlinessCostExp(taskVars("carpentry"), 75, 300.0, useFunction) | ||
+ computeEarlinessCostExp(taskVars("ceiling"), 75, 100.0, useFunction) | ||
+ computeTardinessCostExp(taskVars("moving"), 100, 400.0, useFunction)) | ||
|
||
|
||
model.add(minimize(costExpr)) | ||
|
||
model | ||
} | ||
|
||
def solve(): Boolean = { | ||
|
||
println(s"Solving model $model....") | ||
|
||
// model.exportModel("SchedTime.cpo") | ||
|
||
// val status = model.solve(timeLimit=20, logPeriod=3000) | ||
val status = model.solve() | ||
|
||
if (status) { | ||
println(s"Solution status: $status") | ||
println("Solution with objective " + model.getObjectiveValue()) | ||
for ((name, task) <- taskVars) { | ||
println(model.getDomain(task)) | ||
} | ||
} | ||
|
||
true | ||
} | ||
|
||
def run(): Boolean = { | ||
val model = build() | ||
val status = solve() | ||
model.end() | ||
status | ||
} | ||
|
||
def main(args: Array[String]): Unit = { | ||
run() | ||
} | ||
|
||
} |
Oops, something went wrong.