Skip to content

Commit

Permalink
Merge branch 'release/1.1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
danielgodard committed Mar 14, 2017
2 parents a4f9974 + fae621c commit f343d47
Show file tree
Hide file tree
Showing 9 changed files with 922 additions and 15 deletions.
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ scientific papers. For instance, a constraint such as:

can be written as:

```
#!scala
```scala
model.add(sum (for (i <- 1 to n) yield a(i) * x(i)) <= c(j))
```

Expand All @@ -19,7 +18,7 @@ To get up to speed, the easiest way to start with this library is to study the e
* src/examples/mp: examples of optimization models based on mathematical programming
* src/examples/cp: examples of optimization models based on constraint programming

This library has been tested using IBM ILOG CPLEX 12.6.1, 12.6.2, 12.6.3, 12.7.0 and scala 2.11.8.
This library has been tested using IBM ILOG CPLEX 12.6.1, 12.6.2, 12.6.3, 12.7.0, Scala 2.11.8 and Java JDK 1.8.0_121.

To build the library install gradle 2.10 and set the environment variable `CPLEX_STUDIO_HOME` (e.g.
on windows `C:\IBM\ILOG\CPLEX_Studio1263`).
Expand All @@ -39,12 +38,12 @@ To run the tests, do:
$ gradle test
```

Reports are generated in directory `\build\reports\tests`
Reports are generated in directory `build/reports/tests`.

To generate the scala docs, do:

```
$ gradle scaladoc
```

The scala documentation is generated in directory `build/docs/scaladoc`
The scala documentation is generated in directory `build/docs/scaladoc`.
5 changes: 1 addition & 4 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
group 'com.decisionbrain'
version '1.0.1'
version '1.1.0'

apply plugin: 'java'
apply plugin: 'scala'
Expand All @@ -24,9 +24,6 @@ else if (file(mavenFilename).exists()) {
println("Importing gradle file $filename")
apply from: filename
}
else {
println("Warning: File $mavenFilename not found")
}


//
Expand Down
180 changes: 180 additions & 0 deletions src/examples/scala/com/decisionbrain/cplex/cp/SchedCalendar.scala
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 src/examples/scala/com/decisionbrain/cplex/cp/SchedTime.scala
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()
}

}
Loading

0 comments on commit f343d47

Please sign in to comment.