I'm trying to build a model to optimize steel production. The objective is to reduce the amount of waste (leftover material after the steel has been cut). The code below is what I have so far. It is lacking a constraint to make sure that each item in work_tbl is fulfilled from a single item in inventory_tbl (but several items from work_tbl can be fulfilled by one item in inventory_tbl, given that there is enough length available).
code/reproducible example:
# Load required packages
library(dplyr)
library(ompr)
library(ompr.roi)
library(ROI)
# Define the problem data
work_tbl <- data.frame(length = c(2500, 500, 700, 1200, 1500, 2000, 2500, 3000, 4000, 5250))
inventory_tbl <-
data.frame(length = c(1300, 2000, 1800, 2600, 3000, 2000, 5000, 6000, 7000, 9000, 2000, 500, 4000, 12000, 7400, 13000))
# Define variables to be used
work_count <- nrow(work_tbl)
invent_count <- nrow(inventory_tbl)
big_M <- sum(work_tbl$length) * 1.1
# Initialize the model
steel_model <- ompr::MIPModel() %>%
# Binary decision variable - steel to be cut
add_variable(steel_cut[work, inventory],
work = 1:work_count,
inventory = 1:invent_count,
type = "binary") %>%
# Binary decision variable: Take new item from inventory?
add_variable(take_item[inventory],
inventory = 1:invent_count,
type = "binary") %>%
# Constraint 1: Each item in work_tbl must be cut
add_constraint(sum_over(steel_cut[work, inventory],
inventory = 1:invent_count) == 1,
work = 1:work_count) %>%
# Constraint 2: The sum of each item used to cut from needs to be equal to or smaller than the work item
add_constraint(sum_over(steel_cut[work, inventory] * work_tbl$length[work],
work = 1:work_count) <= inventory_tbl$length[inventory],
inventory = 1:invent_count, work = 1:work_count) %>%
# Constraint 3: big_M constraint to activate take_item whenever a length is cut
add_constraint(
sum_over(steel_cut[work, inventory],
work = 1:work_count) <= big_M * take_item[inventory],
inventory = 1:invent_count
) %>%
# Set objective function to minimize scrap / waste
set_objective(
sum_over(
take_item[inventory] * inventory_tbl$length[inventory],
inventory = 1:invent_count
) - sum_over(steel_cut[work, inventory] * work_tbl$length[work],
work = 1:work_count,
inventory = 1:invent_count), sense = "min"
)
# View the model
steel_model
# Solve the model
solution <- ompr::solve_model(steel_model, with_ROI(solver = "glpk", verbose = TRUE))
# Check objective value
solution$objective_value
# Get the solution
steel_model_soln <-
ompr::get_solution(solution, steel_cut[work, inventory]) %>% filter(value > 0) %>%
mutate(cut_length = work_tbl$length[work])
# View the solution
steel_model_soln