1

我一直在玩ojAlgo,到目前爲止我都非常興奮。我已經通過一些研究工作,但我遇到了這個problem described in this article的問題。ojAlgo - 在優化中將變量表示爲邊界?

我使用的是Kotlin而不是Java,但這不應該引起任何問題。我卡住試圖向我的模型中輸入一個表達式,但綁定在一個變量而不是文字數值。我如何輸入?

這是到目前爲止我的工作:

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 
} 

回答

2

對於未來的讀者,這裏是我提出的完整工作解決方案。

import org.ojalgo.optimisation.ExpressionsBasedModel 
import org.ojalgo.optimisation.Variable 
import java.math.RoundingMode 


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(17.5).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(25).apply { 
     set(variables["Pork-Premium"], 1) 
     set(variables["Wheat-Premium"], 1) 
     set(variables["Starch-Premium"], 1) 
    } 

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

    // Pe >= 0.6(Pp + Wp + Sp) 
    model.addExpression("PremiumPorkRatio").upper(0.0).apply { 
     set(variables["Pork-Premium"], -0.4) 
     set(variables["Wheat-Premium"], .6) 
     set(variables["Starch-Premium"], .6) 
    } 

    // Se <= .25(Pe + We + Se) 
    // Sp <= .25(Pp + Wp + Sp) 
    sausageTypes.values.forEach { 
     model.addExpression("${it}StarchRestriction").lower(0.0).apply { 
      set(variables["Pork-$it"], .25) 
      set(variables["Wheat-$it"], .25) 
      set(variables["Starch-$it"], -0.75) 
     } 
    } 

    // Pe + Pp <= 30 
    // We + Wp <= 20 
    // Se + Sp <= 17 
    ingredients.values.forEach { ingredient -> 
     model.addExpression("${ingredient}SupplyConstraint").upper(ingredient.availability).apply { 
      sausageTypes.values.forEach { sausageType -> 
       set(variables["$ingredient-$sausageType"], 1) 
      } 
     } 
    } 

    // Pe + Pp >= 23 
    model.addExpression("ContractPorkRestriction").lower(23).apply { 
     set(variables["Pork-Economy"], 1) 
     set(variables["Pork-Premium"], 1) 
    } 


    // go! 
    val result = model.minimise() 

    println("OPTIMIZED COST: ${result.value}") 


    model.variables.asSequence() 
      .map { it.name } 
      .zip(result.asSequence().map { it.setScale(3, RoundingMode.HALF_DOWN) }) 
      .forEach(::println) 

} 

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

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 
} 

OUTPUT:

OPTIMIZED COST: 140.955 
(Pork-Economy, 8.000) 
(Pork-Premium, 15.000) 
(Wheat-Economy, 5.125) 
(Wheat-Premium, 3.750) 
(Starch-Economy, 4.375) 
(Starch-Premium, 6.250)