CP-SAT - Steel Mill Slab
Problem: pack steel orders onto large steel slabs while minimizing unused steel.
Show code
import { CpSat, setWorkerBridgeEnabled, type SatParameters } from 'or-tools-wasm/cp-sat';
type Order = { width: number; color: number };
type Column = { orders: number[]; load: number; loss: number };
type CpModelInput = {
name: string;
variables: Array<{ name: string; domain: number[] }>;
constraints: Array<{ name?: string; linear: { vars: number[]; coeffs: number[]; domain: number[] } }>;
objective: { vars: number[]; coeffs: number[] };
};
const capacities = [0, 17, 44];
const orders: Order[] = [
{ width: 4, color: 1 },
{ width: 22, color: 2 },
{ width: 9, color: 3 },
{ width: 5, color: 1 },
{ width: 8, color: 2 },
{ width: 3, color: 3 },
];
function lossForLoad(load: number) {
const capacity = capacities.find((value) => value >= load) ?? capacities[capacities.length - 1];
return capacity - load;
}
function enumerateColumns(): Column[] {
const maxCapacity = Math.max(...capacities);
const columns: Column[] = [];
const visit = (nextOrder: number, selected: number[], load: number, colors: Set<number>) => {
columns.push({ orders: [...selected], load, loss: lossForLoad(load) });
for (let orderId = nextOrder; orderId < orders.length; ++orderId) {
const order = orders[orderId];
const nextColors = new Set(colors).add(order.color);
if (load + order.width > maxCapacity || nextColors.size > 2) continue;
visit(orderId + 1, [...selected, orderId], load + order.width, nextColors);
}
};
visit(0, [], 0, new Set());
return columns.filter((column) => column.orders.length > 0);
}
function buildSteelMillModel(): CpModelInput {
const columns = enumerateColumns();
const variables = columns.map((_, index) => ({ name: `use_column_${index}`, domain: [0, 1] }));
const constraints = orders.map((_, orderId) => {
const coveringColumns = columns
.map((column, columnId) => (column.orders.includes(orderId) ? columnId : -1))
.filter((columnId) => columnId >= 0);
return {
name: `cover_order_${orderId}`,
linear: {
vars: coveringColumns,
coeffs: Array(coveringColumns.length).fill(1),
domain: [1, 1],
},
};
});
return {
name: 'steel_mill_slab',
variables,
constraints,
objective: {
vars: columns.map((_, index) => index),
coeffs: columns.map((column) => column.loss),
},
};
}
setWorkerBridgeEnabled(true);
const model = await CpSat.createModel(buildSteelMillModel());
const validation = await CpSat.validate(model);
if (!validation.ok) throw new Error(validation.message);
const params: SatParameters = { numWorkers: 4, logSearchProgress: true };
const result = await CpSat.solve(model, params);
console.log(result.response?.objectiveValue, result.response?.solution);
- A slab is a large steel blank that can be used to satisfy several orders.
- Each order consumes some slab width and belongs to a color group.
- Slabs come in fixed sizes; unused width on a chosen slab is loss.
- Each slab may contain orders from at most two color groups.
- The solver assigns every order to exactly one slab while minimizing wasted width.
- The solver menu compares three CP-SAT formulations of the same packing problem.
- The drawing shows each used slab as colored order segments plus hatched waste.
Solution
No solution yet.
Status / Response: