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

Factory state Ready