Search code examples
kotlinmathematical-optimizationlinear-programmingojalgo

ojAlgo - Expressing Variables as Boundaries in Optimization?


I've been playing around with ojAlgo and I've been pretty thrilled with it so far. I've worked through a few studies with it but I'm having trouble with this problem described in this article.

I'm using Kotlin instead of Java, but that shouldn't cause any issues. I'm stuck trying to input an expression into my model but bounding on a variable rather than a literal numeric value. How do I input that?

Here is my work so far:

import org.ojalgo.optimisation.ExpressionsBasedModel
import org.ojalgo.optimisation.Variable


fun main(args: Array<String>) {

    val model = ExpressionsBasedModel()

    val ingredients = sequenceOf(
            Ingredient("Pork", 4.32, 30),
            Ingredient("Wheat", 2.46, 20),
            Ingredient("Starch", 1.86, 17)
    ).map { it.name to it }
     .toMap()

    val sausageTypes = sequenceOf(
            SausageType("Economy", .40),
            SausageType("Premium", .60)
    ).map { it.description to it }
     .toMap()

    // Map concatenated string keys to variables
    val variables = ingredients.values.asSequence().flatMap { ingredient ->
        sausageTypes.values.asSequence()
                .map { type -> Combo(ingredient,type)}
    }.map { it.toString() to Variable.make(it.toString()).lower(0).weight(it.ingredient.cost) }
     .toMap()

    // add variables to model
    model.addVariables(variables.values)

    // Pe + We + Se = 350 * 0.05
    model.addExpression("EconomyDemand").level(350.0 * 0.05).apply {
        set(variables["Pork-Economy"], 1)
        set(variables["Wheat-Economy"], 1)
        set(variables["Starch-Economy"], 1)
    }

    // Pp + Wp + Sp = 500 * 0.05
    model.addExpression("PremiumDemand").level(500.0 * 0.05).apply {
        set(variables["Pork-Premium"], 1)
        set(variables["Wheat-Premium"], 1)
        set(variables["Starch-Premium"], 1)
    }

    // Pe >= 0.4(Pe + We + Se) 
    // compile error?
    model.addExpression("EconomyGovRestriction").upper(variables["Pork-Economy"]).apply {
        set(variables["Pork-Economy"], .4)
        set(variables["Wheat-Economy"], .4)
        set(variables["Starch-Economy"], .4)
    }
}

data class Combo(val ingredient: Ingredient, val sausageType: SausageType) {
    override fun toString() = "$sausageType-$ingredient"
}

data class SausageType(val description: String, val porkRequirement: Double) {
    override fun toString() = description
}

data class Ingredient(val name: String, val cost: Double, val availability: Int) {
    override fun toString() = name
}

Solution

  • 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