Skip to content

Commit 1f5326f

Browse files
committed
Update Benchmarks
Include all benchmark results Update benchmarking script to separate out problematic examples Re-add `add_multiple_LP_lower_bound_kernel` Update list of all problems Update information in `master_list.jl` Create TagBot.yml
1 parent a0c032d commit 1f5326f

133 files changed

Lines changed: 1250282 additions & 239 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/TagBot.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
name: TagBot
2+
on:
3+
issue_comment:
4+
types:
5+
- created
6+
workflow_dispatch:
7+
jobs:
8+
TagBot:
9+
if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot'
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: JuliaRegistries/TagBot@v1
13+
with:
14+
token: ${{ secrets.GITHUB_TOKEN }}

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ A BibTeX entry is given below:
110110
author = {Robert X. Gottlieb, Dimitri Alston, and Matthew D. Stuber},
111111
journal = {Under Review},
112112
title = {Re-Architected PDLP for Batch Parallelization of Linear Programs},
113-
year = {2025},
113+
year = {2026},
114114
pages = {},
115115
doi = {},
116116
eprint = {},

benchmarks/2025-12-16 PDLP Test Results.txt

Lines changed: 0 additions & 120 deletions
This file was deleted.

benchmarks/BatchPDLP Benchmarking.jl

Lines changed: 94 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,10 @@ function case_generator(lbd, ubd, n, m)
6060
return all_lvbs, all_uvbs
6161
end
6262

63-
# Now, given a problem, I want to create the right objects to calculate
64-
# relaxations, create the optimization problems, and pass them to PDLP
63+
64+
# Given a problem, create the optimization problems and pass them to PDLP
6565
# and other comparison solvers (GLPK, Gurobi, HiGHS)
66-
function run_example(example::LoadedProblem, n_LPs::Int, n_cuts::Int; run_GLPK::Bool=true, run_Gurobi::Bool=true, run_HiGHS::Bool=true, run_2xGPU::Bool=false)
66+
function run_example(example::LoadedProblem, n_LPs::Int, n_cuts::Int; run_BatchPDLP::Bool=true, run_GLPK::Vector{Bool}=fill(true, 3), run_Gurobi::Vector{Bool}=fill(true, 4), run_HiGHS::Vector{Bool}=fill(true, 4), run_2xGPU::Bool=false)
6767
##############################################################################
6868
######### Step 1) Get Bounds
6969
##############################################################################
@@ -165,7 +165,7 @@ function run_example(example::LoadedProblem, n_LPs::Int, n_cuts::Int; run_GLPK::
165165

166166
# Feed in data to the PDLPData struct
167167
PDLP_data = PDLPData(n_LPs, example.nvars+1, 1+n_cuts*cut_height, sparsity=sparsity, iteration_limit=1000000)
168-
168+
169169
# Since the PDLP_GPU struct already has an allocated field for PDLP data, we
170170
# only need to update that field with the new LP to solve. The main PDLP
171171
# algorithm already resets all fields except for the original LP, so to prepare
@@ -237,14 +237,15 @@ function run_example(example::LoadedProblem, n_LPs::Int, n_cuts::Int; run_GLPK::
237237
PDLP_lowres_objectives = CUDA.zeros(Float64, n_LPs, 2)
238238

239239
# Solve the problems once for compilation
240-
try
241-
PDLP(PDLP_data, solutions=PDLP_solutions, objectives=PDLP_objectives, return_both_obj=true)
242-
catch
243-
nothing
240+
if run_BatchPDLP
241+
try
242+
PDLP(PDLP_data, solutions=PDLP_solutions, objectives=PDLP_objectives, return_both_obj=true)
243+
catch
244+
nothing
245+
end
244246
end
245247

246248
# Solve the problem once at higher tolerances (default is 1E-8 for abs,rel,primal/dual infeas)
247-
println("Starting BatchPDLP")
248249
GC.gc()
249250
GC.enable(false) # Disable garbage collection temporarily to not impact results
250251

@@ -255,19 +256,22 @@ function run_example(example::LoadedProblem, n_LPs::Int, n_cuts::Int; run_GLPK::
255256

256257
# Solve for the first time, but only do (at least) 95% of problems
257258
# (then reset and run all the problems as normal)
258-
iterations = Array(PDLP_data.iterations)
259-
stop_here = 0
260-
for i in sort(unique(iterations))
261-
if count(iterations .<= i) >= 0.95*n_LPs
262-
stop_here = i
263-
break
259+
if run_BatchPDLP
260+
println("Starting BatchPDLP")
261+
iterations = Array(PDLP_data.iterations)
262+
stop_here = 0
263+
for i in sort(unique(iterations))
264+
if count(iterations .<= i) >= 0.95*n_LPs
265+
stop_here = i
266+
break
267+
end
268+
end
269+
PDLP_data.parameters.iteration_limit = Int32(stop_here)
270+
try
271+
PDLP_95pct_solving_time[1] = @elapsed PDLP(PDLP_data, solutions=PDLP_solutions, objectives=PDLP_objectives, return_both_obj=true)
272+
catch
273+
PDLP_95pct_solving_time[1] = NaN
264274
end
265-
end
266-
PDLP_data.parameters.iteration_limit = Int32(stop_here)
267-
try
268-
PDLP_95pct_solving_time[1] = @elapsed PDLP(PDLP_data, solutions=PDLP_solutions, objectives=PDLP_objectives, return_both_obj=true)
269-
catch
270-
PDLP_95pct_solving_time[1] = NaN
271275
end
272276

273277
# Save objectives and termination status
@@ -281,10 +285,12 @@ function run_example(example::LoadedProblem, n_LPs::Int, n_cuts::Int; run_GLPK::
281285
PDLP_data.parameters.iteration_limit = Int32(1000000)
282286

283287
# Run PDLP with an iteration limit of 1E6 and tolerances of 1E-8
284-
try
285-
PDLP_solving_time[1] = @elapsed PDLP(PDLP_data, solutions=PDLP_solutions, objectives=PDLP_objectives, return_both_obj=true)
286-
catch
287-
PDLP_solving_time[1] = NaN
288+
if run_BatchPDLP
289+
try
290+
PDLP_solving_time[1] = @elapsed PDLP(PDLP_data, solutions=PDLP_solutions, objectives=PDLP_objectives, return_both_obj=true)
291+
catch
292+
PDLP_solving_time[1] = NaN
293+
end
288294
end
289295

290296
# Save objectives and termination statuses as CPU arrays
@@ -300,10 +306,12 @@ function run_example(example::LoadedProblem, n_LPs::Int, n_cuts::Int; run_GLPK::
300306
PDLP_data.parameters.termination_criteria.eps_dual_infeasible = 1E-8
301307

302308
# Solve again with lower tolerances
303-
try
304-
PDLP_lowres_solving_time[1] = @elapsed PDLP(PDLP_data, solutions=PDLP_lowres_solutions, objectives=PDLP_lowres_objectives, return_both_obj=true)
305-
catch
306-
PDLP_lowres_solving_time[1] = NaN
309+
if run_BatchPDLP
310+
try
311+
PDLP_lowres_solving_time[1] = @elapsed PDLP(PDLP_data, solutions=PDLP_lowres_solutions, objectives=PDLP_lowres_objectives, return_both_obj=true)
312+
catch
313+
PDLP_lowres_solving_time[1] = NaN
314+
end
307315
end
308316

309317
# Initialize 2xGPU fields
@@ -477,17 +485,19 @@ function run_example(example::LoadedProblem, n_LPs::Int, n_cuts::Int; run_GLPK::
477485
GC.gc()
478486

479487
# Print out some PDLP solve times
480-
println("BatchPDLP Solve Times:")
481-
println("============================================================")
482-
println("All problems (high-res): | $(round.(PDLP_solving_time[1], digits=6))")
483-
println("All problems (low-res): | $(round.(PDLP_lowres_solving_time[1], digits=6))")
484-
println(">95% of problems (high-res): | $(round.(PDLP_95pct_solving_time[1], digits=6))")
485-
println("===========================================================")
486-
if run_2xGPU
487-
println("All problems (high-res) (2xGPU): | $(round.(PDLP_2xGPU_solving_time[1], digits=6))")
488-
println("All problems (low-res) (2xGPU): | $(round.(PDLP_lowres_2xGPU_solving_time[1], digits=6))")
489-
println(">95% of problems (high-res) (2xGPU): | $(round.(PDLP_95pct_2xGPU_solving_time[1], digits=6))")
488+
if run_BatchPDLP
489+
println("BatchPDLP Solve Times:")
490+
println("============================================================")
491+
println("All problems (high-res): | $(round.(PDLP_solving_time[1], digits=6))")
492+
println("All problems (low-res): | $(round.(PDLP_lowres_solving_time[1], digits=6))")
493+
println(">95% of problems (high-res): | $(round.(PDLP_95pct_solving_time[1], digits=6))")
490494
println("===========================================================")
495+
if run_2xGPU
496+
println("All problems (high-res) (2xGPU): | $(round.(PDLP_2xGPU_solving_time[1], digits=6))")
497+
println("All problems (low-res) (2xGPU): | $(round.(PDLP_lowres_2xGPU_solving_time[1], digits=6))")
498+
println(">95% of problems (high-res) (2xGPU): | $(round.(PDLP_95pct_2xGPU_solving_time[1], digits=6))")
499+
println("===========================================================")
500+
end
491501
end
492502

493503
# Save constraints and the RHS for CPU solvers
@@ -613,17 +623,17 @@ function run_example(example::LoadedProblem, n_LPs::Int, n_cuts::Int; run_GLPK::
613623

614624
# Compile solvers
615625
for i in eachindex(solver_list)
616-
if !run_GLPK && i <= 3
617-
solving_times[:,i] .= NaN
618-
primal_objectives[i,:] .= NaN
619-
dual_objectives[i,:] .= NaN
620-
continue
621-
elseif ~run_Gurobi && (i >= 4 && i <= 8)
622-
solving_times[:,i] .= NaN
623-
primal_objectives[i,:] .= NaN
624-
dual_objectives[i,:] .= NaN
625-
continue
626-
elseif ~run_HiGHS && (i >= 9 && i <= 12)
626+
if (i==1 && !(run_GLPK[1])) ||
627+
(i==2 && !(run_GLPK[2])) ||
628+
(i==3 && !(run_GLPK[3])) ||
629+
(i==4 && !(run_Gurobi[1])) ||
630+
(i==5 && !(run_Gurobi[2])) ||
631+
(i==6 && !(run_Gurobi[3])) ||
632+
(i==7 && !(run_Gurobi[4])) ||
633+
(i==8 && !(run_HiGHS[1])) ||
634+
(i==9 && !(run_HiGHS[2])) ||
635+
(i==10 && !(run_HiGHS[3])) ||
636+
(i==11 && !(run_HiGHS[4]))
627637
solving_times[:,i] .= NaN
628638
primal_objectives[i,:] .= NaN
629639
dual_objectives[i,:] .= NaN
@@ -655,22 +665,31 @@ function run_example(example::LoadedProblem, n_LPs::Int, n_cuts::Int; run_GLPK::
655665
end
656666

657667
# Now solve all the LPs
668+
first = [findfirst(run_GLPK), findfirst(run_Gurobi), findfirst(run_HiGHS)]
669+
last = [findlast(run_GLPK), findlast(run_Gurobi), findlast(run_HiGHS)]
658670
for solver = eachindex(solver_list)
659-
if !run_GLPK && solver <= 3
660-
continue
661-
elseif ~run_Gurobi && (solver >= 4 && solver <= 7)
662-
continue
663-
elseif ~run_HiGHS && (solver >= 8 && solver <= 11)
664-
continue
665-
end
666-
current_solver = solver_list[solver]
667-
if solver==1
671+
if !isnothing(first[1]) && solver==first[1]
668672
println("Starting GLPK")
669-
elseif solver==4
673+
elseif !isnothing(first[2]) && solver==3+first[2]
670674
println("Starting Gurobi")
671-
elseif solver==8
675+
elseif !isnothing(first[3]) && solver==7+first[3]
672676
println("Starting HiGHS")
673677
end
678+
679+
if (solver==1 && !(run_GLPK[1])) ||
680+
(solver==2 && !(run_GLPK[2])) ||
681+
(solver==3 && !(run_GLPK[3])) ||
682+
(solver==4 && !(run_Gurobi[1])) ||
683+
(solver==5 && !(run_Gurobi[2])) ||
684+
(solver==6 && !(run_Gurobi[3])) ||
685+
(solver==7 && !(run_Gurobi[4])) ||
686+
(solver==8 && !(run_HiGHS[1])) ||
687+
(solver==9 && !(run_HiGHS[2])) ||
688+
(solver==10 && !(run_HiGHS[3])) ||
689+
(solver==11 && !(run_HiGHS[4]))
690+
continue
691+
end
692+
current_solver = solver_list[solver]
674693
for i = 1:n_LPs
675694
try
676695
# Empty the solver of previous run information
@@ -727,22 +746,22 @@ function run_example(example::LoadedProblem, n_LPs::Int, n_cuts::Int; run_GLPK::
727746
dual_objectives[i, solver] = NaN
728747
end
729748
end
730-
if solver==3
749+
if !isnothing(last[1]) && solver==last[1]
731750
println("GLPK Solve Times:")
732751
println("============================================================")
733752
println("Primal Simplex: | $(round(sum(solving_times[:,1]), digits=6))")
734753
println("Dual Simplex: | $(round(sum(solving_times[:,2]), digits=6))")
735754
println("Interior Point: | $(round(sum(solving_times[:,3]), digits=6))")
736755
println("===========================================================")
737-
elseif solver==7
756+
elseif !isnothing(last[2]) && solver==3+last[2]
738757
println("Gurobi Solve Times:")
739758
println("============================================================")
740759
println("Primal Simplex: | $(round(sum(solving_times[:,4]), digits=6))")
741760
println("Dual Simplex: | $(round(sum(solving_times[:,5]), digits=6))")
742761
println("Interior Point: | $(round(sum(solving_times[:,6]), digits=6))")
743762
println("PDLP: | $(round(sum(solving_times[:,7]), digits=6))")
744763
println("===========================================================")
745-
elseif solver==11
764+
elseif !isnothing(last[3]) && solver==7+last[3]
746765
println("HiGHS Solve Times:")
747766
println("============================================================")
748767
println("Primal Simplex: | $(round(sum(solving_times[:,8]), digits=6))")
@@ -860,7 +879,17 @@ for i = 1:size(included, 1)
860879

861880
# Load and run the problem
862881
ex = LoadedProblem(eval(Symbol(included[i,1])))
863-
data = run_example(ex, 10000, 3, run_2xGPU=true)
882+
if i==33
883+
data = run_example(ex, 10000, 3, run_2xGPU=true, run_GLPK=[false, true, true], run_HiGHS=[true, true, false, true]) # least, GLPK Primal Simplex crashes VSCode, HiGHS IPM takes >1hr
884+
elseif i==59
885+
data = run_example(ex, 10000, 3, run_2xGPU=true, run_GLPK=[false, true, true]) # ex7_3_2, GLPK Primal Simplex crashes VSCode
886+
elseif i==71
887+
data = run_example(ex, 10000, 3, run_2xGPU=true, run_HiGHS=[true, true, false, true]) # mathopt3, HiGHS IPM takes >overnight
888+
elseif i==102
889+
data = run_example(ex, 10000, 3, run_2xGPU=true, run_GLPK=[true, true, false]) # maxmin, GLPK IPM crashes Julia
890+
else
891+
data = run_example(ex, 10000, 3, run_2xGPU=true)
892+
end
864893

865894
# Write data to a CSV
866895
open("./benchmarking_results/rundata_$(included[i,1]).csv", "w") do io

benchmarks/Problem List.xlsx

-69.8 KB
Binary file not shown.

0 commit comments

Comments
 (0)