Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 1 addition & 9 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,13 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[weakdeps]
LDLFactorizations = "40e66cde-538c-5869-a4ad-c39174c6795b"

[extensions]
MathOptInterfaceLDLFactorizationsExt = "LDLFactorizations"

[compat]
BenchmarkTools = "1"
CodecBzip2 = "0.6, 0.7, 0.8"
CodecZlib = "0.6, 0.7"
ForwardDiff = "1"
JSON = "0.21, 1"
JSONSchema = "1"
LDLFactorizations = "0.10"
LinearAlgebra = "1"
MutableArithmetics = "1"
NaNMath = "0.3, 1"
Expand All @@ -45,9 +38,8 @@ Test = "1"
julia = "1.10"

[extras]
LDLFactorizations = "40e66cde-538c-5869-a4ad-c39174c6795b"
JSONSchema = "7d188eb4-7ad8-530c-ae41-71a32a6d4692"
ParallelTestRunner = "d3525ed8-44d0-4b2c-a655-542cee43accc"

[targets]
test = ["LDLFactorizations", "JSONSchema", "ParallelTestRunner"]
test = ["JSONSchema", "ParallelTestRunner"]
50 changes: 0 additions & 50 deletions ext/MathOptInterfaceLDLFactorizationsExt.jl

This file was deleted.

35 changes: 7 additions & 28 deletions src/Bridges/Constraint/bridges/QuadtoSOCBridge.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,25 +60,6 @@ end
const QuadtoSOC{T,OT<:MOI.ModelLike} =
SingleBridgeOptimizer{QuadtoSOCBridge{T},OT}

function compute_sparse_sqrt_fallback(Q, ::F, ::S) where {F,S}
msg = """
Unable to transform a quadratic constraint into a SecondOrderCone
constraint because the quadratic constraint is not strongly convex and
our Cholesky decomposition failed.

If the constraint is convex but not strongly convex, you can work-around
this issue by manually installing and loading `LDLFactorizations.jl`:
```julia
import Pkg; Pkg.add("LDLFactorizations")
using LDLFactorizations
```

LDLFactorizations.jl is not included by default because it is licensed
under the LGPL.
"""
return throw(MOI.AddConstraintNotAllowed{F,S}(msg))
end

function compute_sparse_sqrt(Q, func, set)
# There's a big try-catch here because Cholesky can fail even if
# `check = false`. As one example, it currently (v1.12) fails with
Expand All @@ -88,22 +69,20 @@ function compute_sparse_sqrt(Q, func, set)
# The try-catch isn't a performance concern because the alternative is not
# being able to reformulate the problem.
try
factor = LinearAlgebra.cholesky(Q; check = false)
if !LinearAlgebra.issuccess(factor)
return compute_sparse_sqrt_fallback(Q, func, set)
end
factor = LinearAlgebra.cholesky(Q)
L, p = SparseArrays.sparse(factor.L), factor.p
# We have Q = P' * L * L' * P. We want to find Q = U' * U, so U = L' * P
# First, compute L'. Note I and J are reversed
J, I, V = SparseArrays.findnz(L)
# Then, we want to permute the columns of L'. The rows stay in the same
# order.
return I, p[J], V
catch err
if err isa MOI.AddConstraintNotAllowed
rethrow(err)
end
msg = "There was an error computing a matrix square root"
catch
msg = """
Unable to transform a quadratic constraint into a SecondOrderCone
constraint because the quadratic constraint is not strongly convex and
our Cholesky decomposition failed.
"""
throw(MOI.UnsupportedConstraint{typeof(func),typeof(set)}(msg))
end
end
Expand Down
38 changes: 12 additions & 26 deletions test/Bridges/Constraint/test_QuadtoSOCBridge.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import LinearAlgebra
import SparseArrays
using Test

import LDLFactorizations
import MathOptInterface as MOI

function runtests()
Expand Down Expand Up @@ -345,13 +344,16 @@ function test_semidefinite_cholesky_fail()
model = MOI.Bridges.Constraint.QuadtoSOC{Float64}(inner)
x = MOI.add_variables(model, 2)
f = 0.5 * x[1] * x[1] + 1.0 * x[1] * x[2] + 0.5 * x[2] * x[2]
c = MOI.add_constraint(model, f, MOI.LessThan(1.0))
F, S = MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone
ci = only(MOI.get(inner, MOI.ListOfConstraintIndices{F,S}()))
g = MOI.get(inner, MOI.ConstraintFunction(), ci)
y = MOI.get(inner, MOI.ListOfVariableIndices())
sum_y = 1.0 * y[1] + 1.0 * y[2]
@test isapprox(g, MOI.Utilities.vectorize([1.0, 1.0, sum_y, 0.0]))
@test_throws(
MOI.UnsupportedConstraint,
MOI.add_constraint(model, f, MOI.LessThan(1.0)),
)
# F, S = MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone
# ci = only(MOI.get(inner, MOI.ListOfConstraintIndices{F,S}()))
# g = MOI.get(inner, MOI.ConstraintFunction(), ci)
# y = MOI.get(inner, MOI.ListOfVariableIndices())
# sum_y = 1.0 * y[1] + 1.0 * y[2]
# @test isapprox(g, MOI.Utilities.vectorize([1.0, 1.0, sum_y, 0.0]))
return
end

Expand All @@ -361,12 +363,8 @@ 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. 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. LDL works
[2.0 0.0; 0.0 0.0],
]
B = SparseArrays.sparse(A)
f = zero(MOI.ScalarQuadraticFunction{eltype(A)})
Expand All @@ -381,6 +379,8 @@ function test_compute_sparse_sqrt_edge_cases()
# Test failures
for A in Any[
[-1.0 0.0; 0.0 1.0],
[1.0 1.0; 1.0 1.0],
[2.0 0.0; 0.0 0.0],
# Found from test_quadratic_nonconvex_constraint_basic
[0.0 -1.0; -1.0 0.0],
# Different element type. We could potentially make this work in future,
Expand All @@ -400,20 +400,6 @@ function test_compute_sparse_sqrt_edge_cases()
return
end

function test_compute_sparse_sqrt_fallback()
# Test the default fallback that is hit when LDLFactorizations isn't loaded.
# We could put the test somewhere else so it runs before this file is
# loaded, but that's pretty flakey for a long-term solution. Instead, we're
# going to abuse the lack of a strong type signature to hit it:
f = zero(MOI.ScalarAffineFunction{Float64})
A = SparseArrays.sparse([-1.0 0.0; 0.0 1.0])
@test_throws(
MOI.AddConstraintNotAllowed{typeof(f),MOI.GreaterThan{Float64}},
MOI.Bridges.Constraint.compute_sparse_sqrt(A, f, MOI.GreaterThan(0.0)),
)
return
end

end # module

TestConstraintQuadToSOC.runtests()
Loading