logo
Tags down

shadow

ojAlgo - Expressing Variables as Boundaries in Optimization?


By : Govind Pant
Date : October 18 2020, 01:08 AM
this one helps. You can't do that. You can't directly model expr1 >= expr2. Instead you have to model (expr1 - expr2) >= 0. There is an example on the ojAlgo wiki describing how to model a similar problem: https://github.com/optimatika/ojAlgo/wiki/The-Diet-Problem
code :


Share : facebook icon twitter icon

Do the values of static variables transfer across thread boundaries properly? What about DLL boundaries?


By : JB.YUN
Date : March 29 2020, 07:55 AM
help you fix your problem The static variable will be at the same address for all threads.
I recommend putting the static variable in a static function inside the DLL. The static function returns a reference to the static variable. This way you can control its initialization and other modules can access it easily.

OjAlgo : Is there a way to add/subtract a double from all elements of a PrimitiveDenseStore in ojAlgo?


By : Arvind93
Date : March 29 2020, 07:55 AM
this one helps. Looking for a function to add/subtract a double from all elements of a matrix or dense store. , Some alternatives:
code :
    matrixA.operateOnAll(ADD.second(scalarB)).supplyTo(matrixC);

    matrixC.fillMatching(matrixA, ADD, scalarB);

    matrixC.modifyAll(ADD.second(scalarB));

    matrixA.passMatching((from, i, j, to) -> {
        to.set(i, j, from.doubleValue(i, j) + scalarB);
    }, matrixC);

How to export optimization model in ojalgo


By : Charath Swaminathan
Date : March 29 2020, 07:55 AM
seems to work fine There is no way to generate/export an MPS file using ojAlgo.
If you want to verify solver results with some 3:d party solver, you can do that. I believe the community (free) version of cplex allows up to 1000 variables and constraints.

ojAlgo - Optimization issue with contiguous block logic?


By : Huntergn
Date : March 29 2020, 07:55 AM
I wish did fix the issue. I figured it out. I'll update this answer later with the full mathematical modeling explanation. Essentially for each 15 minute block I queried for slot groups that include that block, and declared the sum of all of them must be no more than one. This ended up being acceptably efficient as it runs in 30-60 seconds.
The code is here on GitHub, as well as below: https://github.com/thomasnield/optimized-scheduling-demo
code :
import org.ojalgo.optimisation.integer.IntegerSolver
import java.time.LocalDate
import java.time.LocalTime
import org.ojalgo.optimisation.ExpressionsBasedModel
import org.ojalgo.optimisation.Variable
import java.time.DayOfWeek
import java.time.LocalDateTime
import java.util.concurrent.atomic.AtomicInteger

// Any Monday through Friday date range will work
val operatingDates = LocalDate.of(2017,10,16)..LocalDate.of(2017,10,20)
val operatingDay = LocalTime.of(8,0)..LocalTime.of(17,0)


val breaks = listOf<ClosedRange<LocalTime>>(
        LocalTime.of(11,30)..LocalTime.of(13,0)
)


// classes
val scheduledClasses = listOf(
        ScheduledClass(id=1, name="Psych 101",hoursLength=1.0, repetitions=2),
        ScheduledClass(id=2, name="English 101", hoursLength=1.5, repetitions=3),
        ScheduledClass(id=3, name="Math 300", hoursLength=1.5, repetitions=2),
        ScheduledClass(id=4, name="Psych 300", hoursLength=3.0, repetitions=1),
        ScheduledClass(id=5, name="Calculus I", hoursLength=2.0, repetitions=2),
        ScheduledClass(id=6, name="Linear Algebra I", hoursLength=2.0, repetitions=3),
        ScheduledClass(id=7, name="Sociology 101", hoursLength=1.0, repetitions=2),
        ScheduledClass(id=8, name="Biology 101", hoursLength=1.0, repetitions=2)
)

fun main(args: Array<String>) {

    println("Job started at ${LocalTime.now()}")

    applyConstraints()

    model.countVariables().run { println("$this variables") }

    model.options.apply {
        //debug(IntegerSolver::class.java)
        iterations_suffice = 0
    }

    println(model.minimise())

    ScheduledClass.all.forEach {
        println("${it.name}- ${it.daysOfWeek.joinToString("/")} ${it.start.toLocalTime()}-${it.end.toLocalTime()}")
    }

    println("Job ended at ${LocalTime.now()}")

}



// declare model
val model = ExpressionsBasedModel()

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) }



data class Block(val dateTimeRange: ClosedRange<LocalDateTime>) {

    val timeRange = dateTimeRange.let { it.start.toLocalTime()..it.endInclusive.toLocalTime() }

    val available get() =  (breaks.all { timeRange.start !in it } && timeRange.start in operatingDay)

    //val cumulativeState = variable().apply { if (available) lower(0).upper(1) else level(0) }

    val slots by lazy {
        Slot.all.filter { it.block == this }
    }

    fun addConstraints() {
        if (available) {
            addExpression().lower(0).upper(1).apply {
                ScheduledClass.all.asSequence().flatMap { it.anchorOverlapFor(this@Block) }
                        .filter { it.block.available }
                        .forEach {
                            set(it.occupied, 1)
                        }
            }
        } else {
            ScheduledClass.all.asSequence().flatMap { it.anchorOverlapFor(this@Block) }
                    .forEach {
                        it.occupied.level(0)
                    }
        }
    }

    companion object {

        // Operating blocks
        val all by lazy {
            generateSequence(operatingDates.start.atStartOfDay()) {
                it.plusMinutes(15).takeIf { it.plusMinutes(15) <= operatingDates.endInclusive.atTime(23,59) }
            }.map { Block(it..it.plusMinutes(15)) }
             .toList()
        }

        fun applyConstraints() {
            all.forEach { it.addConstraints() }
        }
    }
}


data class ScheduledClass(val id: Int,
                          val name: String,
                          val hoursLength: Double,
                          val repetitions: Int,
                          val repetitionGapDays: Int = 2) {

    val repetitionGapSlots = repetitionGapDays * 24 * 4

    val slotsNeeded = (hoursLength * 4).toInt()

    val slots by lazy {
        Slot.all.asSequence().filter { it.session == this }.toList()
    }

    val batches by lazy {
        slots.rollingRecurrences(slotsNeeded = slotsNeeded, gapSize = repetitionGapSlots, recurrencesNeeded = repetitions)
    }

    fun anchorOverlapFor(block: Block) = batches.asSequence()
            .filter { it.flatMap { it }.any { it.block == block } }
            .map { it.first().first() }

    val start get() = slots.asSequence().filter { it.occupied.value.toInt() == 1 }.map { it.block.dateTimeRange.start }.min()!!
    val end get() = start.plusMinutes((hoursLength * 60.0).toLong())

    val daysOfWeek get() = (0..(repetitions-1)).asSequence().map { start.dayOfWeek.plus(it.toLong() * repetitionGapDays) }.sorted()

    fun addConstraints() {

        //sum of all boolean states for this session must be 1
        addExpression().level(1).apply {
            slots.forEach {
                set(it.occupied, 1)
            }
        }

        //guide Mon/Wed/Fri for three repetitions
        if (repetitions == 3) {
            addExpression().level(1).apply {
                slots.filter { it.block.dateTimeRange.start.dayOfWeek == DayOfWeek.MONDAY }
                        .forEach {
                            set(it.occupied, 1)
                        }
            }
        }

        //guide two repetitions to start on Mon, Tues, or Wed
        if (repetitions == 2) {
            addExpression().level(1).apply {
                slots.filter { it.block.dateTimeRange.start.dayOfWeek in DayOfWeek.MONDAY..DayOfWeek.WEDNESDAY }.forEach {
                    set(it.occupied, 1)
                }
            }
        }
    }

    companion object {
        val all by lazy { scheduledClasses }
    }
}



data class Slot(val block: Block, val session: ScheduledClass) {
    val occupied = variable().apply { if (block.available) binary() else level(0) }

    companion object {

        val all by lazy {
            Block.all.asSequence().flatMap { b ->
                ScheduledClass.all.asSequence().map { Slot(b,it) }
            }.toList()
        }
    }
}


fun applyConstraints() {
    Block.applyConstraints()
    ScheduledClass.all.forEach { it.addConstraints() }
}

fun <T> List<T>.rollingBatches(batchSize: Int) = (0..size).asSequence().map { i ->
    subList(i, (i + batchSize).let { if (it > size) size else it })
}.filter { it.size == batchSize }

fun <T> List<T>.rollingRecurrences(slotsNeeded: Int, gapSize: Int, recurrencesNeeded: Int) =
        (0..size).asSequence().map { i ->
            (1..recurrencesNeeded).asSequence().map { (it - 1) * gapSize  }
                    .filter { it + i < size}
                    .map { r ->
                        subList(i + r, (i + r + slotsNeeded).let { if (it > size) size else it })
                    }.filter { it.size == slotsNeeded }
                    .toList()
}.filter { it.size == recurrencesNeeded }

ojAlgo Linear Optimization - Preventing work shift overlaps?


By : CodeTipsTutorials
Date : March 29 2020, 07:55 AM
help you fix your problem It looks like I got this up and running, thanks to Erwin's help in the Math section. The key was a binary switch.
Here is the result. Driver 1 was scheduled 16:00-22:00, Driver 2 6:00-10:00, and Driver 3 10:00-16:00.
code :
import org.ojalgo.optimisation.ExpressionsBasedModel
import org.ojalgo.optimisation.Variable

// declare model
val model = ExpressionsBasedModel()

// parameters
val operatingDay = 6..22
val operatingDayLength = operatingDay.endInclusive - operatingDay.start
val allowableShiftSize = 4..6

// Map drivers by their ID for ad hoc retrieval
val drivers = sequenceOf(
        Driver(driverNumber = 1, rate =  10.0),
        Driver(driverNumber = 2, rate = 12.0, availability = 6..11),
        Driver(driverNumber = 3, rate =  14.0)
    ).map { it.driverNumber to it }
     .toMap()


fun main(args: Array<String>) {

    drivers.values.forEach { it.addToModel() }

    val result = model.minimise()

    println(result)
}

// Driver class will put itself into the Model
data class Driver(val driverNumber: Int,
                  val rate: Double,
                  val availability: IntRange? = null) {

    val shiftStart = Variable.make("${driverNumber}shiftStart").weight(rate).lower(6).upper(22).apply(model::addVariable)
    val shiftEnd = Variable.make("${driverNumber}shiftEnd").weight(rate).lower(6).upper(22).apply(model::addVariable)

    fun addToModel() {

        //constrain shift length
        model.addExpression("${driverNumber}shiftLength")
                .lower(allowableShiftSize.start)
                .upper(allowableShiftSize.endInclusive)
                .set(shiftEnd, 1)
                .set(shiftStart, -1)

        //ensure coverage of entire day
        model.addExpression("EnsureCoverage")
                .level(operatingDayLength)
                .apply {
                    drivers.values.forEach {
                        set(it.shiftEnd, 1)
                        set(it.shiftStart, -1)
                    }
                }

        //add specific driver availability
        availability?.let {
            model.addExpression("${driverNumber}StartAvailability")
                    .lower(it.start)
                    .upper(it.endInclusive)
                    .set(shiftStart, 1)

            model.addExpression("${driverNumber}EndAvailability")
                    .lower(it.start)
                    .upper(it.endInclusive)
                    .set(shiftEnd, 1)
        }

        //prevent shift overlap
        drivers.values.asSequence()
                .filter { it != this }
                .forEach { otherDriver ->

                    val occupied = Variable.make("${driverNumber}occupyStatus").lower(0).upper(1).integer(true).apply(model::addVariable)

                    model.addExpression("${driverNumber}to${otherDriver.driverNumber}Binary1")
                            .upper(0)
                            .set(otherDriver.shiftEnd, 1)
                            .set(occupied, operatingDayLength * - 1)
                            .set(shiftStart, -1)

                    model.addExpression("${driverNumber}to${otherDriver.driverNumber}Binary2")
                            .upper(operatingDayLength)
                            .set(shiftEnd, 1)
                            .set(occupied, operatingDayLength)
                            .set(otherDriver.shiftStart, -1)
                }
    }
}
OPTIMAL 936.0 @ [16.0, 22.0, 6.0, 10.0, 10.0, 16.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0]
Related Posts Related Posts :
shadow
Privacy Policy - Terms - Contact Us © soohba.com