MathOpt - bakery floor planner with IncrementalSolver
A rolling MathOpt CP-SAT planner runs a persistent bakery simulation with shared ingredients, shared processors, display-shelf buffers, and live customer orders.
- Customer orders and per-product shelf targets create demand; finished goods wait on the display shelf until customers can be served.
- The pantry, fridge, sink, and packaging shelf emit separate raw-material tokens such as flour, water, yeast, butter, cream, fruit, and boxes.
- Mixers combine raw ingredients into dough or batter; proofers, ovens, decorators, laminators, and packers transform WIP through the recipe stages.
- A processor starts only after all inputs for the current job step have reached that processor cell.
- The production planner keeps one MathOpt IncrementalSolver alive, mutating bounds and objective terms as orders, WIP, stock, ingredients, and processor reservations change.
- A second short-horizon CP-SAT solve schedules conveyor dispatch timing so raw materials do not collide on shared directed lanes.
Show code
import { initMathOpt, MathOpt } from 'or-tools-wasm/mathopt';
await initMathOpt();
// Build the rolling production model once.
const model = MathOpt.Model('bakery_production');
const startBreadNow = model.addIntegerVariable({ lowerBound: 0, upperBound: 0, name: 'start_bread_now' });
const demand = model.addLinearConstraint({
upperBound: 0,
terms: [{ variable: startBreadNow, coefficient: 1 }],
});
model.maximize([{ variable: startBreadNow, coefficient: 100 }]);
// Keep this solver object alive between replans.
const solver = new MathOpt.IncrementalSolver(model, MathOpt.SolverType.CP_SAT, {
cpSat: { maxTimeInSeconds: 1, numWorkers: 4 },
});
async function replanAfterFactoryStateChanges(openBreadOrders: number, freeMixers: number) {
// Only mutate the existing model: the incremental solver receives a model update.
startBreadNow.upperBound = freeMixers;
demand.upperBound = openBreadOrders;
const result = await solver.solve();
if ((result.variableValues.start_bread_now ?? 0) > 0.5) {
startBreadJob();
}
...
// The example also solves a short conveyor traffic model for safe dispatch timing.
await planTrafficDispatches();
}
06:00