Skip to content

Instantly share code, notes, and snippets.

@thomasnield
Last active December 25, 2017 04:04
Show Gist options
  • Select an option

  • Save thomasnield/50b18274ee9af0df2b98bc53e44d1d1c to your computer and use it in GitHub Desktop.

Select an option

Save thomasnield/50b18274ee9af0df2b98bc53e44d1d1c to your computer and use it in GitHub Desktop.
Integer programming abstract example
package org.nield
import org.ojalgo.optimisation.ExpressionsBasedModel
import org.ojalgo.optimisation.Variable
import java.util.concurrent.atomic.AtomicInteger
// declare ojAlgo Model
val model = ExpressionsBasedModel()
// custom DSL for model expression inputs, eliminate naming and adding
val funcId = AtomicInteger(0)
val variableId = AtomicInteger(0)
fun variable() = Variable(variableId.incrementAndGet().toString().let { "Variable$it" }).apply(model::addVariable)
fun addExpression() = funcId.incrementAndGet().let { "Func$it"}.let { model.addExpression(it) }
fun main(args: Array<String>) {
Letter.values().forEach { it.addConstraints() }
Number.values().forEach { it.addConstraints() }
// C must be greater than or equal to THREE
addExpression().level(1).apply {
Letter.C.slots.asSequence().filter { it.number.value >= 3 }.forEach {
set(it.occupied, 1)
}
}
// D must be less than or equal to TWO
addExpression().level(1).apply {
Letter.D.slots.asSequence().filter { it.number.value <= 2 }.forEach {
set(it.occupied, 1)
}
}
model.minimise().apply(::println)
Letter.values().forEach {
println(it.slots)
}
}
enum class Letter(val slotsNeeded: Int = 1) {
A,
B(slotsNeeded = 2),
C,
D,
E;
val slots by lazy {
Slot.all.filter { it.letter == this }.sortedBy { it.number.value }
}
fun addConstraints() {
// constrain each LETTER to number of slots needed
addExpression().level(slotsNeeded).apply {
slots.forEach {
set(it.occupied, 1)
}
}
// constrain slots to be consecutive if more than one are needed
// this is tricky because slots need to be "batched" in groups of needed slot size
// and another binary needs to be shared across all the batches
// x1 + x2 + .. xi - Sb <= 0
// Where xi is slot binaries, S is number of slots needed, and b is shared binary across all groups
if (slotsNeeded > 1) {
val grouper = AtomicInteger(-1)
val allGroupSlots = addExpression().level(1)
slots.asSequence().groupBy { (grouper.incrementAndGet() / slotsNeeded).apply(::println) }
.values.forEach { group ->
val slotForGroup = variable().binary()
allGroupSlots.set(slotForGroup, 1)
addExpression().upper(0).apply {
group.forEach {
set(it.occupied,1)
}
set(slotForGroup, -1 * slotsNeeded)
}
}
}
}
}
enum class Number(val value: Int) {
ONE(1),
TWO(2),
THREE(3),
FOUR(4),
FIVE(5),
SIX(6);
val slots by lazy {
Slot.all.filter { it.number == this }.sortedBy { it.letter }
}
fun addConstraints() {
// constrain each NUMBER to only be assigned one slot
addExpression().level(1).apply {
slots.forEach {
set(it.occupied, 1)
}
}
}
}
data class Slot(val letter: Letter, val number: Number) {
val occupied = variable().binary()
companion object {
val all = Letter.values().asSequence().flatMap { letter ->
Number.values().asSequence().map { number -> Slot(letter, number) }
}.toList()
}
override fun toString() = "$letter-$number: ${occupied?.value?.toInt()}"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment