The scenario is a truck optimization procedure that will maximize the total truck weight, while staying under the max allowable weight and also observing item level stacking constraints.
For an example, consider a small shipping container with space for 12 boxes (6 boxes in the bottom "foot prints", and 6 boxes stacked on top of those). The total weight cannot exceed 12 lbs. Each item (box) has a specific rule that needs to be considered, which indicates whether it can support any weight on top of it, and if so, how much.
library(GA)
# Item-Level Data
item_dat <- data.frame(order = c(rep("A",2), rep("B",3), rep("C",3)),
weight = c(4,5,1,1,1,2,2,2),
ft.print = c(1,1,1,1,1,1,1,1),
rule = c(rep("supports nothing",5), rep("supports <= own weight",2), "no constraint"))
> item_dat
order weight ft.print rule
1 A 4 1 supports nothing
2 A 5 1 supports nothing
3 B 1 1 supports nothing
4 B 1 1 supports nothing
5 B 1 1 supports nothing
6 C 2 1 supports <= own weight
7 C 2 1 supports <= own weight
8 C 2 1 no constraint
I have been able to write code to optimize at the aggregated header level, but unsuccessful in writing a fitness function that can evaluate at the item level taking into account each item's stacking rule.
# Aggregated Item-Level Data
header_dat <- aggregate(cbind(weight, ft.print) ~ order, item_dat, sum)
> header_dat
order weight ft.print
1 A 9 2
2 B 3 3
3 C 6 3
# Constraints
max_weight <- 12
max_ft_prints <- 12
# Fitness function
eval_func <- function(x) {
cur_wgt <- x %*% header_dat$weight
cur_ft_print <- x %*% header_dat$ft.print
# If overweight or over foot print space
if(cur_wgt > max_weight | cur_ft_print > max_ft_prints) {
return(0)
} else {
return(cur_wgt)
}
}
ga(type = "binary", nBits = nrow(header_dat), fitness = eval_func, min = 1, max = nrow(header_dat))@solution[1, ]
GA | iter = 100
Mean = 11.1 | Best = 12.0
x1 x2 x3
1 1 0
The output is correctly assigning Orders A and B together using only weights and footprints from the header level, but if it were able to consider item stacking rules it would disqualify the grouping due to none of the items being able to support any weight.
The correct output would be Orders B & C grouped together, and A alone.
I've tried writing a second item level fitness function to be passed to the header level "eval_func", but so far nothing that comes even close. I'm unclear how to have it consider items within each Order based on how much weight they can support, while at the same time maximizing the total truck weight.
Update
After some additional research, I'm starting to think the solution will fall under the class of multi-objective combinatorial optimization. The problem is more in the domain of the traveling salesman or shift scheduling type of task, than it is of the knapsack type above. The additional objectives (constraints) seem to require another call to a separate function or some kind of lookup inclusion in the header-level eval function with each iteration.
Still hoping there's somewhere out there that can point me in a direction. Thank you.