Example 8: Production under equipment man-hours
1. Overview
This bounded context chooses non-negative integer production quantities to maximize product profit under equipment man-hour capacities. The page follows the current Demo8 data and model; the code tabs are model-building fragments whose data is supplied by the source files.
The product profits are
| Equipment | Amount | P1 | P2 | P3 | P4 | P5 |
|---|---|---|---|---|---|---|
| A | 12 | .23 | .44 | .17 | .08 | .36 |
| B | 14 | .13 | – | .20 | .37 | .19 |
| C | 8 | – | .25 | .34 | – | .18 |
| D | 6 | .55 | .72 | – | .61 | – |
Each equipment unit supplies at most
1. Dependent Contexts
- None. No external domain context is required.
2. Concepts / Entities
1. Product
A product can be produced in an integer quantity and contributes a fixed profit per unit.
2. Equipment Type
An equipment type has several available units and consumes a product-specific number of man-hours for every produced unit.
3. Variables
1. Decision Variables
UIntVariable1; the Rust source uses VariableCombination1D<UInteger>.
2. Auxiliary Variables
There are no separately declared auxiliary decision variables. Profit and ManHours_e are registered derived symbols.
4. Predicates
1. Equipment Usage Predicate
This predicate identifies coefficients that participate in a particular equipment expression.
Uses(e,p): true when the source has a non-missing e.manHours[p] coefficient. A missing map entry is omitted from the sum; the Rust data represents it with 0.00 and filters zero coefficients.
5. Sets
1. Product and Equipment Categories
2. Entity Pairs / Relations
6. Intermediate Values
1. Total Profit
Description: Total profit generated by all production quantities.
2. Equipment Man-Hours
Description: Total man-hours consumed on equipment type
3. Equipment Capacity
Description: Total available man-hours of equipment type
Capacity_e is a documentation-level derived value; the current source writes the product directly in each constraint.
7. Assertions
1. Missing Coefficients Consume No Modeled Hours
Description: A product absent from an equipment map does not occur in that equipment's expression.
2. Capacity Is Non-Negative
Description: Equipment amounts and the per-unit maximum are non-negative, so every equipment capacity is non-negative.
3. Historical Solver Observation
Description: A previous run reported
8. Constraints
1. Equipment Man-Hour Capacity (设备工时容量约束)
Description: Production assigned to an equipment type cannot consume more hours than all available units provide.
2. Production Domain (产量定义域约束)
Description: Every product quantity is a non-negative integer.
9. Objective Function (if applicable)
Description: Maximize total product profit.
10. Algorithm References
No standalone algorithm document is referenced. The model is registered as linear expressions and solved through the current Kotlin LinearMetaModel/SCIP path or Rust MetaModel/solver path.
| Algorithm Name | File Path | Referenced In | Brief Description |
|---|---|---|---|
| None | — | — | No standalone algorithm is needed. |
11. Ubiquitous Language
| Term | Symbol | Definition |
|---|---|---|
| Product | Item whose integer production quantity is selected. | |
| Equipment | Resource type with a finite number of units and hours. | |
| Profit | Total value of production. | |
| Man-hours | Hours consumed on equipment type | |
| Capacity | Hours supplied by equipment type |
12. Design Decisions
| Decision | Alternatives | Rationale | Date |
|---|---|---|---|
| Keep production integral and unbounded above except through capacities | Continuous production or hand-added product bounds | Matches UIntVariable1/UInteger in the current sources | 2026-09-08 |
| Omit missing equipment coefficients | Treat every dash as an explicit business penalty | The Kotlin map omits the key and Rust filters zero coefficients | 2026-09-08 |
Preserve the name ManHours | Rename it to Cost | The coefficient has time units; Cost would misstate the physical meaning | 2026-09-08 |
Minimal current model-building snippets
products and equipments/equipment come from Demo8.kt and demo8.rs; the converter and solver setup are also source-owned. These fragments show the registered symbols and constraints only.
// `products`, `equipments`, `maxManHours`, and `flt64Converter` come from Demo8.kt.
val metaModel = LinearMetaModel<Flt64>("demo8", converter = flt64Converter)
val x = UIntVariable1("x", Shape1(products.size))
val profit = LinearExpressionSymbol(
sum(products.map { p -> p.profit * x[p] }),
name = "profit"
)
val manHours = LinearIntermediateSymbols1<Flt64>(
"man_hours",
Shape1(equipments.size)
) { i, _ ->
val e = equipments[i]
LinearExpressionSymbol(
sum(products.mapNotNull { p -> e.manHours[p]?.let { it * x[p] } }),
name = "man_hours_${e.index}"
)
}
metaModel.add(x)
metaModel.add(profit)
metaModel.add(manHours)
metaModel.maximize(profit, "profit")
for (e in equipments) {
metaModel.addConstraint(
manHours[e] leq e.amount.toFlt64() * maxManHours,
name = "eq_man_hours_${e.index}"
)
}// `products` and `equipments` come from demo8.rs.
let mut model = MetaModel::<f64>::new("demo8");
let x: VariableCombination1D<UInteger> =
VariableCombination1D::new(Shape::new([products.len()]), "x");
let x_idx = model.register_combination(&x)?;
let profit = flat_map1_indexed("profit", products, |i, product| {
ospf_rust_core::symbol::flatten::Linear::new(
vec![ospf_rust_core::symbol::flatten::LinearMonomial::new(
product.profit, x_idx[i],
)],
0.0,
)
}, |_, product| product.name.clone());
model.add_symbol_combination(&profit)?;
let man_hours = flat_map1("man_hours", equipments, |equipment| {
let monomials = products.iter().enumerate().filter_map(|(p, _)| {
let value = equipment.man_hours_by_product[p];
(value != 0.0).then(||
ospf_rust_core::symbol::flatten::LinearMonomial::new(value, x_idx[p])
)
}).collect();
ospf_rust_core::symbol::flatten::Linear::new(monomials, 0.0)
}, |_, equipment| equipment.name.clone());
model.add_symbol_combination(&man_hours)?;
let profit_coeffs = extract_coeffs(&profit[0]);
model.add_linear_objective(&profit_coeffs, "profit");
model.set_objective_category(ObjectiveCategory::Maximum);
for (e, equipment) in equipments.iter().enumerate() {
model.add_linear_constraint(
&extract_coeffs(&man_hours[e]), ConstraintRelation::LessEqual,
equipment.amount * max_man_hours, &format!("equipment_{}_{}", e, equipment.name),
)?;
}13. Change Log
| Version | Change | Reason |
|---|---|---|
| 2026-09-08 | Reorganized the page into the domain-model template; added quantified intermediate values, bilingual constraint names, and Kotlin/Rust tabs | Align documentation with the current Demo8 implementations |