Summary
eliminate can return a projected polytope whose bounding box is noticeably larger than the true projection, by up to ~2.5e-4 on some instances. Since a projection preserves the extent in the kept dimensions, bounding_box(eliminate(p, ids)) should equal bounding_box(p) in those dimensions. On degenerate random polytopes it does not, and the gap is far above the solver tolerance.
Reproduction
using PolyDAQP, Random
Random.seed!(1305)
A, b = randn(6,40), rand(40)
p = Polyhedron(A, b)
ub, lb = bounding_box(p) # exact bounding box of p (all 6 dims)
pn = eliminate(p, [4,5,6]) # project onto dims 1,2,3
ubn, lbn = bounding_box(pn)
ubn[3] - ub[3] # 0.00025336583177260064, should be ~0
The eliminated polytope is a strict over-approximation (ubn > ub, never smaller): a facet that should bound the projection is missing.
Root cause
The over-approximation comes from the combinatorial pruning in eliminate, in two parts.
1. The sum(Hcand) > level+1 Chernikov filter drops real facets. That cardinality bound (a combination's history has at most level+1 original constraints) only holds under non-degeneracy. Random polytopes are degenerate, so a genuine facet of the projection can have a larger history and is discarded. On seed 1305 (error in the dim-3 upper bound):
| variant |
error |
#constraints |
| current (cardinality filter) |
2.5e-4 |
116 |
| subset-adjacency filter |
5.9e-8 |
129 |
| no combinatorial filter |
6.7e-16 |
4658 |
Disabling the filter entirely makes it exact, which confirms the filter is what drops the facet, but that blows the intermediate constraint count up about 40x, so it is not a usable fix.
A cheap adjacency filter is not sufficient either. Replacing the cardinality test with the standard subset-adjacency test (skip a combination only if some other constraint's history is a subset of the combined one) is never worse than the current code over 2000 random seeds and fixes the single worst case, but it still leaves 8/2000 failures and a 5.5e-5 worst case, because the subset test also has false positives on degenerate instances.
2. A second, smaller source is minrep. Even with no combinatorial filter, a residual around 1e-7 remains, from the redundancy check running at DAQP's default primal_tol = 1e-6: a constraint protruding by about 1e-7 is classified as redundant and dropped.
Bottom line
The accuracy eliminate can currently deliver on degenerate inputs is around 1e-4 with the fast filter. The elimination testset in test/runtests.jl asserts this to δ = 1e-8, so it fails whenever CI lands on one of these instances (8 of 2000 random seeds here). The follow-up comment identifies the fix: replacing the cardinality filter with history-dedup removes the macroscopic errors (machine epsilon on most seeds). A residual around 1e-7 remains from LP-based redundancy removal on the degenerate intermediate polytopes, so the test's δ should also be relaxed to about 1e-6.
This is what turns the PolyDAQP.jl check red on darnstrom/daqp#160, but the two are unrelated: that PR is a division-to-reciprocal rewrite in the DAQP LDP transform, and on arm64 it produces bit-identical bounding boxes to master across all 2000 seeds here. The over-approximation is present on unmodified master.
Summary
eliminatecan return a projected polytope whose bounding box is noticeably larger than the true projection, by up to ~2.5e-4 on some instances. Since a projection preserves the extent in the kept dimensions,bounding_box(eliminate(p, ids))should equalbounding_box(p)in those dimensions. On degenerate random polytopes it does not, and the gap is far above the solver tolerance.Reproduction
The eliminated polytope is a strict over-approximation (
ubn > ub, never smaller): a facet that should bound the projection is missing.Root cause
The over-approximation comes from the combinatorial pruning in
eliminate, in two parts.1. The
sum(Hcand) > level+1Chernikov filter drops real facets. That cardinality bound (a combination's history has at mostlevel+1original constraints) only holds under non-degeneracy. Random polytopes are degenerate, so a genuine facet of the projection can have a larger history and is discarded. On seed 1305 (error in the dim-3 upper bound):Disabling the filter entirely makes it exact, which confirms the filter is what drops the facet, but that blows the intermediate constraint count up about 40x, so it is not a usable fix.
A cheap adjacency filter is not sufficient either. Replacing the cardinality test with the standard subset-adjacency test (skip a combination only if some other constraint's history is a subset of the combined one) is never worse than the current code over 2000 random seeds and fixes the single worst case, but it still leaves 8/2000 failures and a 5.5e-5 worst case, because the subset test also has false positives on degenerate instances.
2. A second, smaller source is
minrep. Even with no combinatorial filter, a residual around 1e-7 remains, from the redundancy check running at DAQP's defaultprimal_tol = 1e-6: a constraint protruding by about 1e-7 is classified as redundant and dropped.Bottom line
The accuracy
eliminatecan currently deliver on degenerate inputs is around 1e-4 with the fast filter. The elimination testset intest/runtests.jlasserts this toδ = 1e-8, so it fails whenever CI lands on one of these instances (8 of 2000 random seeds here). The follow-up comment identifies the fix: replacing the cardinality filter with history-dedup removes the macroscopic errors (machine epsilon on most seeds). A residual around 1e-7 remains from LP-based redundancy removal on the degenerate intermediate polytopes, so the test'sδshould also be relaxed to about 1e-6.Relation to darnstrom/daqp#160
This is what turns the
PolyDAQP.jlcheck red on darnstrom/daqp#160, but the two are unrelated: that PR is a division-to-reciprocal rewrite in the DAQP LDP transform, and on arm64 it produces bit-identical bounding boxes to master across all 2000 seeds here. The over-approximation is present on unmodified master.