From e9b6db55b7eb36da77550111625b0dc35aabda2b Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Fri, 27 Feb 2026 16:05:31 +1300 Subject: [PATCH 1/2] Fix error message for LDL factorization failures --- ext/MathOptInterfaceLDLFactorizationsExt.jl | 2 +- .../Bridges/Constraint/test_QuadtoSOCBridge.jl | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/ext/MathOptInterfaceLDLFactorizationsExt.jl b/ext/MathOptInterfaceLDLFactorizationsExt.jl index 9211069776..d6bba480ad 100644 --- a/ext/MathOptInterfaceLDLFactorizationsExt.jl +++ b/ext/MathOptInterfaceLDLFactorizationsExt.jl @@ -21,7 +21,7 @@ function MOI.Bridges.Constraint.compute_sparse_sqrt_fallback( ) where {F<:MOI.ScalarQuadraticFunction,S<:MOI.AbstractSet} n = LinearAlgebra.checksquare(Q) factor = LDLFactorizations.ldl(Q) - if minimum(factor.D) < 0 + if !LDLFactorizations.factorized(factor) || minimum(factor.D) < 0 msg = """ Unable to transform a quadratic constraint into a SecondOrderCone constraint because the quadratic constraint is not convex. diff --git a/test/Bridges/Constraint/test_QuadtoSOCBridge.jl b/test/Bridges/Constraint/test_QuadtoSOCBridge.jl index 00b64a3ff1..7e13c7af60 100644 --- a/test/Bridges/Constraint/test_QuadtoSOCBridge.jl +++ b/test/Bridges/Constraint/test_QuadtoSOCBridge.jl @@ -378,12 +378,18 @@ function test_compute_sparse_sqrt_edge_cases() end @test isapprox(A, U' * U; atol = 1e-10) end - A = [-1.0 0.0; 0.0 1.0] - B = SparseArrays.sparse(A) - @test_throws( - MOI.UnsupportedConstraint{typeof(f),typeof(s)}, - MOI.Bridges.Constraint.compute_sparse_sqrt(B, f, s), - ) + # Test failures + for A in [ + [-1.0 0.0; 0.0 1.0], + # Found from test_quadratic_nonconvex_constraint_basic + [0.0 -1.0; -1.0 0.0], + ] + B = SparseArrays.sparse(A) + @test_throws( + MOI.UnsupportedConstraint{typeof(f),typeof(s)}, + MOI.Bridges.Constraint.compute_sparse_sqrt(B, f, s), + ) + end return end From b6487a2113683695ab26c297b7d34db621a2c8fa Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Mon, 2 Mar 2026 08:32:52 +1300 Subject: [PATCH 2/2] Update --- ext/MathOptInterfaceLDLFactorizationsExt.jl | 6 +++++- test/Bridges/Constraint/test_QuadtoSOCBridge.jl | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ext/MathOptInterfaceLDLFactorizationsExt.jl b/ext/MathOptInterfaceLDLFactorizationsExt.jl index d6bba480ad..c3b5c7a31c 100644 --- a/ext/MathOptInterfaceLDLFactorizationsExt.jl +++ b/ext/MathOptInterfaceLDLFactorizationsExt.jl @@ -21,7 +21,11 @@ function MOI.Bridges.Constraint.compute_sparse_sqrt_fallback( ) where {F<:MOI.ScalarQuadraticFunction,S<:MOI.AbstractSet} n = LinearAlgebra.checksquare(Q) factor = LDLFactorizations.ldl(Q) - if !LDLFactorizations.factorized(factor) || minimum(factor.D) < 0 + # Ideally we should use `LDLFactorizations.factorized(factor)` here, but it + # has some false negatives. Instead we check that the factorization appeared + # to work. This is a heuristic. There might be other cases where check is + # insufficient. + if minimum(factor.D) < 0 || any(issubnormal, factor.D) msg = """ Unable to transform a quadratic constraint into a SecondOrderCone constraint because the quadratic constraint is not convex. diff --git a/test/Bridges/Constraint/test_QuadtoSOCBridge.jl b/test/Bridges/Constraint/test_QuadtoSOCBridge.jl index 7e13c7af60..d34cfe5093 100644 --- a/test/Bridges/Constraint/test_QuadtoSOCBridge.jl +++ b/test/Bridges/Constraint/test_QuadtoSOCBridge.jl @@ -363,11 +363,11 @@ function test_compute_sparse_sqrt_edge_cases() [1.0 0.0; 0.0 2.0], # Cholesky works, with pivoting [1.0 0.0 1.0; 0.0 1.0 1.0; 1.0 1.0 3.0], - # Cholesky fails due to 0 eigen value + # Cholesky fails due to 0 eigen value. LDL works [1.0 1.0; 1.0 1.0], # Cholesky succeeds, even though 0 eigen value [2.0 2.0; 2.0 2.0], - # Cholesky fails because of 0 column/row + # Cholesky fails because of 0 column/row. LDL works [2.0 0.0; 0.0 0.0], ] B = SparseArrays.sparse(A)