ojAlgo - 在优化中将变量表示为边界?
ojAlgo - Expressing Variables as Boundaries in Optimization?
我一直在研究 ojAlgo,到目前为止我对它非常兴奋。我已经对它进行了一些研究,但我遇到了这个 problem described in this article 的问题。
我使用的是 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
}
你不能那样做。您不能直接建模 expr1 >= expr2
。相反,您必须建模 (expr1 - expr2) >= 0
。 ojAlgo wiki 上有一个示例描述了如何对类似问题建模:https://github.com/optimatika/ojAlgo/wiki/The-Diet-Problem
对于未来的读者,这里是我想出的完整的工作解决方案。
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
}
输出:
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)
我一直在研究 ojAlgo,到目前为止我对它非常兴奋。我已经对它进行了一些研究,但我遇到了这个 problem described in this article 的问题。
我使用的是 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
}
你不能那样做。您不能直接建模 expr1 >= expr2
。相反,您必须建模 (expr1 - expr2) >= 0
。 ojAlgo wiki 上有一个示例描述了如何对类似问题建模:https://github.com/optimatika/ojAlgo/wiki/The-Diet-Problem
对于未来的读者,这里是我想出的完整的工作解决方案。
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
}
输出:
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)