Skip to content

Example 7: Transport Problem

Problem Description

Given a set of warehouses and stores, each warehouse has a supply quantity, and each store has a demand. The cost of shipping 1kg of goods from each warehouse to each store is specified.

Warehouse AWarehouse BWarehouse C
Storage510kg470kg520kg
Store AStore BStore CStore D
Demand200kg400kg600kg300kg
Store AStore BStore CStore D
Warehouse A1213217
Warehouse B1417818
Warehouse C1011915

Determine the transportation volume from each warehouse to each store that minimizes the total cost.

Mathematical Model

Variables

xws : shipment from warehouse w to store s.

Intermediate Expressions

1. Total Cost

Cost=wWsSCostwsxws

2. Shipment

Shipmentw=sSxws,wW

3. Purchase

Purchases=wWxws,sS

Objective Function

1. Minimize Cost

minCost

Constraints

1. Shipment Limit

s.t.ShipmentwStoragew,wW

2. Purchase Limit

s.t.PurchasesDemands,sS

Expected Result

Store AStore BStore CStore D
Warehouse A200kg10kg0kg300kg
Warehouse B0kg0kg470kg0kg
Warehouse C0kg390kg130kg0kg

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 Store(
    val demand: Flt64
) : AutoIndexed(Store::class)

data class Warehouse(
    val stowage: Flt64,
    val cost: Map<Store, Flt64>
) : AutoIndexed(Warehouse::class)

val stores: List<Store> = ... // store data
val warehouses: List<Warehouse> = ... // warehouse data

// create a model instance
val metaModel = LinearMetaModel("demo7")

// define variables
val x = UIntVariable2("x", Shape2(warehouses.size, stores.size))
for (w in warehouses) {
    for (s in stores) {
        x[w, s].name = "${x.name}_(${w.index},${s.index})"
    }
}
metaModel.add(x)

// define intermediate expressions
val cost = LinearExpressionSymbol(sum(warehouses.map { w ->
    sum(stores.filter { w.cost.contains(it) }.map { s ->
        w.cost[s]!! * x[w, s]
    })
}), "cost")
metaModel.add(cost)

val shipment = LinearIntermediateSymbo ls1(
    "shipment",
    Shape1(warehouses.size)
) { i, _ ->
    val w = warehouses[i]
    LinearExpressionSymbol(
        sum(stores.filter { w.cost.contains(it) }.map { s -> x[w, s] }),
        "shipment_${w.index}"
    )
}
metaModel.add(shipment)

val purchase = LinearIntermediateSymbols1(
    "purchase",
    Shape1(stores.size)
) { i, _ ->
    val s = stores[i]
    LinearExpressionSymbol(
        sum(warehouses.filter { w -> w.cost.contains(s) }.map { w -> x[w, s] }),
        "purchase_${s.index}"
    )
}
metaModel.add(purchase)

// define objective function
metaModel.minimize(cost, "cost")

// define constraints
for(w in warehouses){
    metaModel.addConstraint(
        shipment[w] leq w.stowage,
        "stowage_${w.index}"
    )
}

for(s in stores){
    metaModel.addConstraint(
        purchase[s] geq s.demand,
        "demand_${s.index}"
    )
}

// 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 = stores.associateWith { warehouses.associateWith { Flt64.zero }.toMutableMap() }
for (token in metaModel.tokens.tokens) {
    if (token.result!! geq Flt64.one && token.variable.belongsTo(x)) {
        val warehouse = warehouses[token.variable.vectorView[0]]
        val store = stores[token.variable.vectorView[1]]
        solution[store]!![warehouse] = solution[store]!![warehouse]!! + token.result!!
    }
}

For the complete implementation, please refer to: