6060const QuadtoSOC{T,OT<: MOI.ModelLike } =
6161 SingleBridgeOptimizer{QuadtoSOCBridge{T},OT}
6262
63+ abstract type AbstractExt end
64+
65+ is_defined (:: AbstractExt ) = false
66+
67+ struct CliqueTrees <: AbstractExt end
68+
69+ struct LinearAlgebraExt <: AbstractExt end
70+
71+ is_defined (:: LinearAlgebraExt ) = true
72+
73+ function compute_sparse_sqrt (:: LinearAlgebraExt , Q:: AbstractMatrix )
74+ factor = LinearAlgebra. cholesky (Q; check = false )
75+ if ! LinearAlgebra. issuccess (factor)
76+ return nothing
77+ end
78+ L, p = SparseArrays. sparse (factor. L), factor. p
79+ # We have Q = P' * L * L' * P. We want to find Q = U' * U, so U = L' * P
80+ # First, compute L'. Note I and J are reversed
81+ J, I, V = SparseArrays. findnz (L)
82+ # Then, we want to permute the columns of L'. The rows stay in the same
83+ # order.
84+ return I, p[J], V
85+ end
86+
87+ """
88+ compute_sparse_sqrt(Q::AbstractMatrix)
89+
90+ Attempts to compute a sparse square root such that `Q = A' * A`.
91+
92+ ## Return value
93+
94+ If successful, this function returns an `(I, J, V)` triplet of the sparse `A`
95+ matrix.
96+
97+ If unsuccessful, this function returns `nothing`.
98+
99+ ## Extensions
100+
101+ By default, this function attempts to use a Cholesky decomposition. If that
102+ fails, it may optionally use various extension packages.
103+
104+ These extension packages must be loaded before calling `compute_sparse_sqrt`.
105+
106+ The extensions currently supported are:
107+
108+ * The pivoted Cholesky in `CliqueTrees.jl`
109+ """
110+ function compute_sparse_sqrt (Q:: AbstractMatrix )
111+ # There's a big try-catch here because Cholesky can fail even if
112+ # `check = false`. The try-catch isn't a performance concern because the
113+ # alternative is not being able to reformulate the problem.
114+ for ext in (LinearAlgebraExt (), CliqueTrees ())
115+ if is_defined (ext)
116+ try
117+ if (ret = compute_sparse_sqrt (ext, Q)) != = nothing
118+ return ret
119+ end
120+ catch
121+ end
122+ end
123+ end
124+ return nothing
125+ end
126+
127+ function _get_sqrt_error_message (is_clique_trees_defined:: Bool )
128+ msg = """
129+ ## SecondOrderCone reformulation
130+
131+ We tried to reformulate the quadratic constraint into a SecondOrderCone,
132+ but this failed because the quadratic constraint is not strongly convex
133+ and our matrix factorization failed.
134+ """
135+ if is_clique_trees_defined
136+ return msg
137+ end
138+ clique_trees = """
139+
140+ ## CliqueTrees.jl
141+
142+ If the constraint is convex but not strongly convex, you can work-around
143+ this issue by manually installing and loading `CliqueTrees.jl`:
144+ ```julia
145+ import Pkg; Pkg.add("CliqueTrees")
146+ using CliqueTrees
147+ ```
148+ CliqueTrees.jl is not included by default because it contains a number of
149+ heavy dependencies.
150+ """
151+ return msg * clique_trees
152+ end
153+
63154function bridge_constraint (
64155 :: Type{QuadtoSOCBridge{T}} ,
65156 model,
@@ -84,43 +175,9 @@ function bridge_constraint(
84175 MOI. ScalarAffineTerm (scale * term. coefficient, term. variable),
85176 ) for term in func. affine_terms
86177 ]
87- sqrt_ret = MOI . Utilities . compute_sparse_sqrt (LinearAlgebra. Symmetric (Q))
178+ sqrt_ret = compute_sparse_sqrt (LinearAlgebra. Symmetric (Q))
88179 if sqrt_ret === nothing
89- msg = """
90- ## SecondOrderCone reformulation
91-
92- We tried to reformulate the quadratic constraint into a SecondOrderCone,
93- but this failed because the quadratic constraint is not strongly convex
94- and our matrix factorization failed.
95-
96- ## Package extensions
97-
98- If the constraint is convex but not strongly convex, you can work-around
99- this issue by manually installing and loading one of the following
100- packages.
101-
102- ### LDLFactorizations.jl
103-
104- Currently active: $(MOI. Utilities. is_defined (MOI. Utilities. LDLFactorizationsExt ()))
105-
106- ```julia
107- import Pkg; Pkg.add("LDLFactorizations")
108- using LDLFactorizations
109- ```
110- LDLFactorizations.jl is not included by default because it is licensed
111- under the LGPL.
112-
113- ### CliqueTrees.jl
114-
115- Currently active: $(MOI. Utilities. is_defined (MOI. Utilities. CliqueTreesExt ()))
116-
117- ```julia
118- import Pkg; Pkg.add("CliqueTrees")
119- using CliqueTrees
120- ```
121- CliqueTrees.jl is not included by default because it contains a number of
122- heavy dependencies.
123- """
180+ msg = _get_sqrt_error_message (is_defined (CliqueTrees ()))
124181 return throw (MOI. UnsupportedConstraint {typeof(func),typeof(set)} (msg))
125182 end
126183 for (i, j, v) in zip (sqrt_ret[1 ], sqrt_ret[2 ], sqrt_ret[3 ])
0 commit comments