Example 2: Assignment Problem
Problem Description
There are several companies and products. Each company incurs a specific cost to produce each product.
Product A | Product B | Product C | Product D | |
---|---|---|---|---|
Co. A | ||||
Co. B | ||||
Co. C | ||||
Co. D |
The goal is to assign distinct companies to produce different products such that the total cost is minimized.
Mathematical Model
Variables
Intermediate Expressions
1. Total Cost
2. Company Assignment
3. Product Assignment
Objective Function
1. Minimize Total Cost
Constraints
1. Company Assignment Limit
2. Product Assignment Limit
Expected Result
Company A produces product C, company B produces product D, company C produces product A, and company D produces product B.
Code Implementation
kotlin
import fuookami.ospf.kotlin.utils.concept.*
import fuookami.ospf.kotlin.utils.functional.*
import fuookami.ospf.kotlin.utils.multi_array.*
import fuookami.ospf.kotlin.utils.math.*
import fuookami.ospf.kotlin.core.frontend.variable.*
import fuookami.ospf.kotlin.core.frontend.expression.monomial.*
import fuookami.ospf.kotlin.core.frontend.expression.polynomial.*
import fuookami.ospf.kotlin.core.frontend.expression.symbol.*
import fuookami.ospf.kotlin.core.frontend.inequality.*
import fuookami.ospf.kotlin.core.frontend.model.mechanism.*
import fuookami.ospf.kotlin.core.backend.plugins.scip.*
class Product : AutoIndexed(Product::class)
data class Company(
val cost: Map<Product, Flt64>
) : AutoIndexed(Company::class)
private val products: List<Product> = ... // product data
private val companies: List<Company> = ... // company data
// create a model instance
val metaModel = LinearMetaModel("demo2")
// define variables
val x = BinVariable2("x", Shape2(companies.size, products.size))
for (c in companies) {
for (p in products) {
x[c, p].name = "${x.name}_${c.index},${p.index}"
}
}
metaModel.add(x)
// define intermediate expressions
val cost = LinearExpressionSymbol(flatSum(companies) { c ->
products.map { p ->
c.cost[p]?.let { it * x[c, p] }
}
}, "cost")
metaModel.add(cost)
val assignmentCompany = LinearIntermediateSymbols(
"assignment_company",
Shape1(companies.size)
)
for (c in companies) {
assignmentCompany[c].asMutable() += sumVars(products) { p ->
c.cost[p]?.let { x[c, p] }
}
}
metaModel.add(assignmentCompany)
val assignmentProduct = flatMap(
"assignment_product",
products,
{ p -> sumVars(companies) { c -> c.cost[p]?.let { x[c, p] } } }
)
metaModel.add(assignmentProduct)
// define objective function
metaModel.minimize(cost)
// define constraints
for (c in companies) {
metaModel.addConstraint(assignmentCompany[c] leq 1)
}
for (p in products) {
metaModel.addConstraint(assignmentProduct[p] eq 1)
}
// solve the model
val solver = ScipLinearSolver()
when (val ret = solver(metaModel)) {
is Ok -> {
metaModel.tokens.setSolution(ret.value.solution)
}
is Failed -> {}
}
// parse results
val solution = ArrayList<Pair<Company, Product>>()
for (token in metaModel.tokens.tokens) {
if (token.result!! eq Flt64.one && token.variable.belongsTo(x)) {
val company = companies[token.variable.vectorView[0]]
val product = products[token.variable.vectorView[1]]
solution.add(company to product)
}
}
For the complete implementation, please refer to: