Skip to content

Example 8: Production Problem

Problem Description

There are several pieces of equipment and several products. Each product has a corresponding profit, each piece of equipment has a corresponding quantity, and producing each product consumes a certain amount of man-hours from each piece of equipment.

Product AProduct BProduct CProduct DProduct E
Value12394105132118
Equipment AEquipment BEquipment CEquipment D
Quantity121486
Product AProduct BProduct CProduct DProduct E
Equipment A0.23h0.44h0.17h0.08h0.36h
Equipment B0.13h0.20h0.37h0.19h
Equipment C0.25h0.34h0.18h
Equipment D0.55h0.72h0.61h

Determine the production quantity of each product to maximize total profit, while satisfying the following condition:

  1. The man-hours for each piece of equipment are less than 2000h.

Mathematical Model

Variables

xp: Production quantity of product p.

Intermediate Values

1. Total Profit

Profit=pPProfitpxp

2. Total Man-Hours per Equipment

ManHourse=pPCostepxp,eE

Objective Function

1. Maximize Total Profit

maxProfit

Constraints

1. Equipment Man-Hours Cannot Exceed Maximum

s.t.ManHourseAmounteManHoursMax,eE

Expected Results

Produce 0 of product A, 0 of product B, 18771 of product C, 19672 of product D, and 53431 of product E.

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 Product(
    val profit: Flt64
) : AutoIndexed(Product::class)

data class Equipment(
    val amount: UInt64,
    val manHours: Map<Product, Flt64>
) : AutoIndexed(Equipment::class)

private val maxManHours = Flt64(2000)
private val products: List<Product> = ... // Product list
private val equipments: List<Equipment> = ... // Equipment list

// Create model instance
val metaModel = LinearMetaModel("demo8")

// Define variables
val x = UIntVariable1("x", Shape1(products.size))
for (p in products) {
    x[p].name = "${x.name}_${p.index}"
}
metaModel.add(x)

// Define intermediate values
val profit = LinearExpressionSymbol(sum(products.map { p ->
    p.profit * x[p]
}), "profit")
metaModel.add(profit)

val manHours = LinearIntermediateSymbols1(
    "man_hours",
    Shape1(equipments.size)
) { i, _ ->
    val e = equipments[i]
    LinearExpressionSymbol(
        sum(products.mapNotNull { p -> e.manHours[p]?.let { it * x[p] } }),
        "man_hours_${e.index}"
    )
}
metaModel.add(manHours)

// Define objective function
metaModel.maximize(profit, "profit")

// Define constraints
for (e in equipments) {
    metaModel.addConstraint(
        manHours[e] leq e.amount.toFlt64() * maxManHours,
        "eq_man_hours_${e.index}"
    )
}

// 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 solution = HashMap<Product, UInt64>()
for (token in metaModel.tokens.tokens) {
    if (token.result!! neq Flt64.one && token.variable.belongsTo(x)) {
        solution[products[token.variable.vectorView[0]]] = token.result!!.round().toUInt64()
    }
}

Complete Implementation Reference: