Table of Contents
PADM for Mxied-integer Bilevel Optimization
export_data_from_ggplot_ecdf = function(plot, xmax = NULL) {
plot_data = ggplot_build(plot)$data[[1]]
plot_data = plot_data %>% filter(is.finite(x))
if (is.null(xmax)) {
xmax = max(plot_data$x, na.rm = TRUE)
}
ecdf_wide = plot_data %>%
select(x, y, group) %>%
pivot_wider(names_from = group, values_from = y) %>%
arrange(x) %>%
slice(1:n()-1)
final_row = ecdf_wide %>%
select(-x) %>%
summarise(across(everything(), ~ max(.x, na.rm = TRUE))) %>%
mutate(x = xmax) %>%
select(x, everything())
ecdf_wide = bind_rows(ecdf_wide, final_row)
return(ecdf_wide)
}
export_data_from_ggplot_ecdf_csv = function(plot, filename) {
result = export_data_from_ggplot_ecdf(plot)
write.csv(result, file = filename, na = "")
}
Loading BOBILib
bobilib_columns = c("instance", "class", "type", "n_upper_vars", "n_upper_integer_vars", "n_upper_binary_vars", "n_lower_vars", "n_lower_integer_vars", "n_lower_binary_vars", "n_linking_vars", "n_linking_integer_vars", "n_linking_binary_vars", "n_linking_continuous_vars", "n_upper_ctrs", "n_lower_ctrs", "n_coupling_ctrs", "best_known_obj", "bobilib_status", "difficulty")
bobilib = read.csv("bobilib.csv", header = FALSE, col.names = bobilib_columns)
bobilib = bobilib %>% mutate(n_vars = as.numeric(n_upper_vars + n_lower_vars),
n_ctrs = as.numeric(n_upper_ctrs + n_lower_ctrs))
paged_table(bobilib)
Loading Results
# Function to read results obtained on the HPC
get_results_from_hpc = function(method) {
result = read.csv(paste0("results/results-", method, ".csv"), header = FALSE, sep = ",")
if (method == "PADM") {
colnames(result) = c("tag", "instance", "method", "update_rule", "factor", "initial_penalty_parameter", "time", "status", "reason", "best_obj", "outer_loops", "inner_loops", "lambda", "pi_lb", "pi_ub")
result[result$status != "Feasible",]$time = 3600
result = result %>% mutate(method = paste0(method, "-", initial_penalty_parameter))
} else if (method == "PADM-interdiction") {
colnames(result) = c("tag", "instance", "method", "update_rule", "factor", "initial_penalty_parameter", "time", "status", "reason", "best_obj", "outer_loops", "inner_loops")
result[result$status != "Feasible",]$time = 3600
result = result %>% mutate(method = paste0(method, "-", initial_penalty_parameter))
} else {
colnames(result) = c("tag", "instance", "method", "time", "status", "reason", "best_bound", "best_obj", "lambda", "pi_lb", "pi_ub")
}
result = result %>% mutate(instance = basename(instance), instance = gsub("\\.aux$", "", instance))
result = result %>% mutate(time = ifelse(time > 3600, 3600, time))
return(result)
}
# Function to read results obtained by Johannes for BOBILib
get_results_from_boblib = function(method) {
result = read.csv(paste0("results-bobilib/", method, ".csv"), header = FALSE, sep = ",")
colnames(result) = c("instance", "method", "time", "best_obj")
result = result %>% mutate(time = ifelse(time > 3600, 3600, time))
return(result)
}
# Reading all results
columns = c("instance", "method", "time", "best_obj")
mibs = get_results_from_boblib("MibS") %>% select(all_of(columns))
filemosi = get_results_from_boblib("filemosi") %>% select(all_of(columns))
milp = get_results_from_hpc("MILP") %>% select(all_of(columns))
milp_first = get_results_from_hpc("MILP-first") %>% select(all_of(columns))
#nlp = get_results_from_hpc("NLP") %>% select(all_of(columns))
padm = get_results_from_hpc("PADM") %>% select(all_of(columns))
padm_interdiction = get_results_from_hpc("PADM-interdiction") %>% select(all_of(columns))
# Selecting only those results fulfilling our assumptions
instances = unique(unlist(lapply(list(milp,padm), function(df) df$instance)))
add_missing_results = function(t_data) {
result = t_data
missing_instances = setdiff(instances, t_data$instance)
if (length(missing_instances) > 0) {
method = unique(t_data$method)
missing_results = data.frame(
instance = missing_instances,
method = method,
time = 3600,
best_obj = NA
)
result = rbind(result, missing_results)
}
result = result %>% filter(instance %in% instances)
return(result)
}
mibs = add_missing_results(mibs)
filemosi = add_missing_results(filemosi)
milp = add_missing_results(milp)
#nlp = add_missing_results(nlp)
padm = add_missing_results(padm)
milp_first = add_missing_results(milp_first)
# Aggregating all results
all_results = rbind(
mibs, milp, milp_first, padm, padm_interdiction
)
all_results = all_results %>%
left_join(bobilib %>% select(instance, bobilib_status, best_known_obj, class, type, n_vars, n_ctrs), by = "instance") %>%
mutate(
best_known_obj = na_if(best_known_obj, "None"),
best_obj = na_if(best_obj, "None")
) %>%
mutate(
best_known_obj = as.numeric(best_known_obj),
best_obj = as.numeric(best_obj)
)
paged_table(all_results)
all_results %>%
distinct(instance, .keep_all = TRUE) %>%
count(bobilib_status)
## bobilib_status n
## 1 infeasible 29
## 2 open 285
## 3 open with feasible point 1132
## 4 optimal 770
all_results %>%
distinct(instance, .keep_all = TRUE) %>%
count(class, type)
## class type n
## 1 general-bilevel mixed-integer 166
## 2 general-bilevel pure-integer 92
## 3 interdiction assignment 24
## 4 interdiction clique 219
## 5 interdiction generalized 90
## 6 interdiction knapsack 599
## 7 interdiction multidimensional-knapsack 954
## 8 interdiction network 72
PADM
Computational Times
# Step 0: Compute size per unique instance and assign quantile bins as factors
instance_sizes <- all_results %>%
distinct(instance, n_vars, n_ctrs) %>%
mutate(
size = n_vars + n_ctrs,
size_group = ntile(size, 5),
size_group = factor(size_group) # convert to factor here
)
# Step 1: Create readable interval labels for each bin, keeping size_group as factor
group_labels <- instance_sizes %>%
group_by(size_group) %>%
summarise(
min_size = min(size),
max_size = max(size),
.groups = "drop"
) %>%
mutate(
label = paste0(min_size, "--", max_size),
size_group = factor(size_group) # factor again for safety
)
# Step 2: Join size and group info into main data (filter methods first)
all_results_with_size <- all_results %>%
filter(method %in% c("MibS", "MILP", "MILP-first", "PADM-1000")) %>%
left_join(instance_sizes %>% select(instance, size_group, size), by = "instance") %>%
left_join(group_labels, by = "size_group") %>%
mutate(size_group_label = factor(label, levels = group_labels$label)) # preserve order
# Step 3: Count total instances per group (unique problems)
total_counts <- all_results_with_size %>%
distinct(instance, size_group_label) %>%
count(size_group_label, name = "count") %>%
mutate(type = "Total")
# Step 4: Count solved instances per method
solved_counts <- all_results_with_size %>%
filter(time < 3600) %>%
group_by(size_group_label, method) %>%
summarise(count = n(), .groups = "drop") %>%
rename(type = method)
# Step 4.1: Fill in missing combinations (show 0 count bars)
all_methods <- unique(all_results_with_size$method)
all_groups <- unique(all_results_with_size$size_group_label)
solved_counts <- solved_counts %>%
complete(size_group_label = all_groups, type = all_methods, fill = list(count = 0))
# Step 5: Combine and plot
plot_data <- bind_rows(total_counts, solved_counts)
ggplot(plot_data, aes(x = size_group_label, y = count, fill = type)) +
geom_bar(stat = "identity", position = "dodge") +
labs(
title = "Instance and Solved Counts by Size Interval",
x = "Size Interval",
y = "Count",
fill = "Type"
) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
wide_data = plot_data %>%
pivot_wider(
names_from = type,
values_from = count,
values_fill = 0 # Fill missing combinations with 0
)
#write.csv(wide_data, "barplot_solved.csv", row.names = FALSE, quote = FALSE)
plot_ecdf_of_time = function(data, log = FALSE) {
p = ggplot(data, aes(x = time, color = method)) +
stat_ecdf(geom = "step", linewidth = 1) +
labs(
title = "ECDF of Time",
x = "Time (s)",
y = "Instances (%)"
) +
theme_minimal() +
scale_color_brewer(palette = "Set1")
if (log) {
p = p + scale_x_log10()
}
return(p)
}
p = plot_ecdf_of_time(all_results %>% filter(!startsWith(method, "PADM-interdiction")))
p
export_data_from_ggplot_ecdf_csv(p, "ecdf_time_all.csv")
padm_raw = get_results_from_hpc("PADM") %>% filter(method == "PADM-100")
ggplot(padm_raw, aes(x = lambda, color = method)) +
stat_ecdf(geom = "step", linewidth = 1) +
scale_x_log10() +
labs(
title = "ECDF of Lambda bound",
x = "Value",
y = "Instances (%)"
) +
theme_minimal() +
scale_color_brewer(palette = "Set1")
summary(padm_raw$lambda)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.000e+00 2.582e+03 1.727e+04 4.650e+18 1.196e+05 1.000e+20
# padm_raw %>% filter(lambda > 1e10)
Optimality Gap
plot_ecdf_of_gap_wrt_best_known = function(data) {
results_with_gaps = data %>%
filter(!is.na(best_known_obj)) %>%
filter(bobilib_status == "optimal") %>%
filter(time < 3600) %>%
mutate(gap = abs(best_obj - best_known_obj) / (1e-10 + abs(best_known_obj))) %>%
mutate(gap = ifelse(gap < 1e-4, 1e-4, gap))
#print(results_with_gaps %>% filter(method == "MILP-first" & gap > .8 & gap < 1.2 ))
p = ggplot(results_with_gaps, aes(x = gap, color = method)) +
stat_ecdf(geom = "step", linewidth = 1) +
#scale_x_log10() +
coord_cartesian(xlim = c(1e-4, 2e1)) +
labs(
title = "ECDF of Gap",
x = "Gap (%)",
y = "Instances (%)"
) +
theme_minimal() +
scale_color_brewer(palette = "Set1")
return(p)
}
plot_ecdf_of_gap_wrt_best_known(all_results %>% filter(method == "PADM-100" | method == "PADM-1000"))
solved_by_both = solved_by_both = all_results %>% filter(method == "PADM-100" | method == "PADM-1000" | method == "MILP-first") %>%
group_by(instance) %>%
filter(all(time < 3600 & time >= 0)) %>%
ungroup()
p = plot_ecdf_of_gap_wrt_best_known(solved_by_both)
p
export_data_from_ggplot_ecdf_csv(p, "ecdf_gap_all.csv")
Status change in BOBILib
all_results_with_status = all_results %>%
filter(method == "PADM-1000" & time < 3600) %>%
mutate(
new_bobilib_status = ifelse(bobilib_status == "open with feasible point" & best_obj < best_known_obj - 1e-6, "was open with feasible point\n and is improving", bobilib_status),
new_bobilib_status = ifelse(bobilib_status == "optimal" & abs(best_obj - best_known_obj) < 1e-6, "was optimal\n and found optimal point", new_bobilib_status),
new_bobilib_status = ifelse(bobilib_status == "open", "was open", new_bobilib_status),
new_bobilib_status = ifelse(bobilib_status == "optimal" & abs(best_obj - best_known_obj) > 1e-6, "was optimal\n and found feasible point", new_bobilib_status),
new_bobilib_status = ifelse(new_bobilib_status == "open with feasible point" & best_obj >= best_known_obj, "was open with feasible point\n and found non-improving\n feasible point", new_bobilib_status)
)
padm_found_feasible_point = all_results %>%
filter(method == "PADM-1000" & time < 3600)
was_open_and_is_improving = length(padm_found_feasible_point %>% filter(bobilib_status == "open with feasible point" & best_obj < best_known_obj - 1e-6))
was_optimal_and_found_optimal = length(padm_found_feasible_point %>% filter(bobilib_status == "optimal" & abs(best_obj - best_known_obj) < 1e-6))
was_open_and_found_feasible = length(padm_found_feasible_point %>% filter( bobilib_status == "open" ))
was_optimal_and_found_feasible = length(padm_found_feasible_point %>% filter(bobilib_status == "optimal" & abs(best_obj - best_known_obj) > 1e-6))
ggplot(all_results_with_status, aes(x = new_bobilib_status)) +
geom_bar(fill = "steelblue", width = .9) +
geom_text(stat = "count", aes(label = after_stat(count)), vjust = -0.5) +
labs(title = "Status change in BOBILib",
x = "Status",
y = "Count") +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 0, hjust = .5, vjust = 1, lineheight = 1)
)
all_results %>%
filter(method == "PADM-1000" | method == "MILP-first") %>%
filter(time < 3600) %>%
filter( (bobilib_status == "open") ) %>%
select(instance) %>%
unique() %>%
count()
## n
## 1 66
PADM interdiction
Computational time
all_results_common_to_padm_and_padm_interdiction =
solved_by_both = all_results %>% filter(method == "PADM-1000" | method == "PADM-interdiction-1000") %>%
group_by(instance) %>%
filter(all(time >= 0)) %>%
ungroup()
solved_by_both = solved_by_both = all_results %>% filter(method == "PADM-1000" | method == "PADM-interdiction-1000") %>%
group_by(instance) %>%
filter(all(time < 3600 & time >= 0)) %>%
ungroup()
p = plot_ecdf_of_time(all_results_common_to_padm_and_padm_interdiction)
p
export_data_from_ggplot_ecdf_csv(p, "ecdf_time_interdiction.csv")
Optimality gap
plot_ecdf_of_gap_wrt_best_known(all_results %>% filter(method == "PADM-interdiction-1000"))
p = plot_ecdf_of_gap_wrt_best_known(solved_by_both)
p
export_data_from_ggplot_ecdf_csv(p, "ecdf_gap_interdiction.csv")
#to_check = read.csv("to_check.csv", col.names = c("tag", "instance", "check", "reason"))
#to_check = to_check %>% mutate(instance = basename(instance), instance = gsub("\\.aux$", "", instance))
#all_results %>% filter(method == "MILP-first" & instance %in% to_check$instance & time < 3600)
This document is automatically generated after every
git push
action on the public repository
hlefebvr/hlefebvr.github.io
using rmarkdown and Github
Actions. This ensures the reproducibility of our data manipulation. The
last compilation was performed on the 13/06/25 14:24:04.