Example 9: Facility Location Problem
Problem Description
In a city divided into regular blocks along east-west and north-south directions, residential points are scattered across different blocks. Use
Select a location for building a post office to minimize the total distance from residential points to the post office. Distance is measured using Manhattan distance.
| Residential Point A | Residential Point B | Residential Point C | Residential Point D | Residential Point E | Residential Point F | |
|---|---|---|---|---|---|---|
Mathematical Model
Variables
Intermediate Values
1. East-West Distance
2. North-South Distance
3. Distance
Objective Function
1. Minimize Sum of Distances
Expected Results
The post office should be located at
Code Implementation
kotlin
import fuookami.ospf.kotlin.utils.math.*
import fuookami.ospf.kotlin.utils.concept.*
import fuookami.ospf.kotlin.utils.functional.*
import fuookami.ospf.kotlin.utils.multi_array.*
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.*
data class Settlement(
val x: Flt64,
val y: Flt64
) : AutoIndexed(Settlement::class)
val settlements: List<Settlement> = ... // Residential point list
// Create model instance
val metaModel = LinearMetaModel("demo9")
// Define variables
val x = IntVar("x")
val y = IntVar("y")
metaModel.add(x)
metaModel.add(y)
// Define intermediate values
val dx = LinearIntermediateSymbols1("dx", Shape1(settlements.size)) { i, _ ->
SlackFunction(
type = UInteger,
x = LinearPolynomial(x),
y = LinearPolynomial(settlements[i].x),
name = "dx_$i"
)
}
metaModel.add(dx)
val dy = LinearIntermediateSymbols1("dy", Shape1(settlements.size)) { i, _ ->
SlackFunction(
type = UInteger,
x = LinearPolynomial(y),
y = LinearPolynomial(settlements[i].y),
name = "dy_$i"
)
}
metaModel.add(dy)
val distance = LinearIntermediateSymbols1("distance", Shape1(settlements.size)) { i, _ ->
LinearExpressionSymbol(
dx[i] + dy[i],
name = "distance_$i"
)
}
metaModel.add(distance)
// Define objective function
metaModel.minimize(sum(distance[_a]))
// Call solver to solve
val solver = ScipLinearSolver()
when (val ret = solver(metaModel)) {
is Ok -> {
metaModel.tokens.setSolution(ret.value.solution)
}
is Failed -> {}
}
// Parse results
val position = ArrayList<Flt64>()
for (token in metaModel.tokens.tokens) {
if (token.variable.belongsTo(x)) {
position.add(token.result!!)
}
}
for (token in metaModel.tokens.tokens) {
if (token.variable.belongsTo(y)) {
position.add(token.result!!)
}
}Complete Implementation Reference: