From 44e3032cdf0c9a3e20621c3bb33ddac8f4165dc6 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 20 Aug 2025 18:10:03 -0400 Subject: [PATCH 1/3] Add availability checks for MKL and OpenBLAS similar to AppleAccelerate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds runtime availability checks for MKL and OpenBLAS libraries to ensure proper error handling when the libraries are not available. The implementation follows the same pattern as AppleAccelerateLU: - Added __mkl_isavailable() function to check MKL library availability - Added __openblas_isavailable() function to check OpenBLAS library availability - Added error checks in all getrf!, getrs!, and solve! functions - Uses Libdl to check for library symbols at runtime This ensures that calls properly compile out when the binaries are missing and provides clear error messages when the libraries are not available. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/mkl.jl | 47 +++++++++++++++++++++++++++++++++++++++++++++++ src/openblas.jl | 46 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/src/mkl.jl b/src/mkl.jl index 0b216c119..0f582511c 100644 --- a/src/mkl.jl +++ b/src/mkl.jl @@ -1,3 +1,5 @@ +using Libdl + """ ```julia MKLLUFactorization() @@ -8,11 +10,38 @@ to avoid allocations and does not require libblastrampoline. """ struct MKLLUFactorization <: AbstractFactorization end +# Check if MKL is available and can be loaded +function __mkl_isavailable() + if !@isdefined(MKL_jll) + return false + end + if !MKL_jll.is_available() + return false + end + # Try to load the library and check for required symbols + try + mkl_hdl = Libdl.dlopen(MKL_jll.libmkl_rt) + if mkl_hdl == C_NULL + return false + end + # Check for a required symbol + if Libdl.dlsym_e(mkl_hdl, "dgetrf_") == C_NULL + Libdl.dlclose(mkl_hdl) + return false + end + Libdl.dlclose(mkl_hdl) + return true + catch + return false + end +end function getrf!(A::AbstractMatrix{<:ComplexF64}; ipiv = similar(A, BlasInt, min(size(A, 1), size(A, 2))), info = Ref{BlasInt}(), check = false) + __mkl_isavailable() || + error("Error, MKL binary is missing but solve is being called. Report this issue") require_one_based_indexing(A) check && chkfinite(A) chkstride1(A) @@ -33,6 +62,8 @@ function getrf!(A::AbstractMatrix{<:ComplexF32}; ipiv = similar(A, BlasInt, min(size(A, 1), size(A, 2))), info = Ref{BlasInt}(), check = false) + __mkl_isavailable() || + error("Error, MKL binary is missing but solve is being called. Report this issue") require_one_based_indexing(A) check && chkfinite(A) chkstride1(A) @@ -53,6 +84,8 @@ function getrf!(A::AbstractMatrix{<:Float64}; ipiv = similar(A, BlasInt, min(size(A, 1), size(A, 2))), info = Ref{BlasInt}(), check = false) + __mkl_isavailable() || + error("Error, MKL binary is missing but solve is being called. Report this issue") require_one_based_indexing(A) check && chkfinite(A) chkstride1(A) @@ -73,6 +106,8 @@ function getrf!(A::AbstractMatrix{<:Float32}; ipiv = similar(A, BlasInt, min(size(A, 1), size(A, 2))), info = Ref{BlasInt}(), check = false) + __mkl_isavailable() || + error("Error, MKL binary is missing but solve is being called. Report this issue") require_one_based_indexing(A) check && chkfinite(A) chkstride1(A) @@ -94,6 +129,8 @@ function getrs!(trans::AbstractChar, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat{<:ComplexF64}; info = Ref{BlasInt}()) + __mkl_isavailable() || + error("Error, MKL binary is missing but solve is being called. Report this issue") require_one_based_indexing(A, ipiv, B) LinearAlgebra.LAPACK.chktrans(trans) chkstride1(A, B, ipiv) @@ -119,6 +156,8 @@ function getrs!(trans::AbstractChar, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat{<:ComplexF32}; info = Ref{BlasInt}()) + __mkl_isavailable() || + error("Error, MKL binary is missing but solve is being called. Report this issue") require_one_based_indexing(A, ipiv, B) LinearAlgebra.LAPACK.chktrans(trans) chkstride1(A, B, ipiv) @@ -144,6 +183,8 @@ function getrs!(trans::AbstractChar, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat{<:Float64}; info = Ref{BlasInt}()) + __mkl_isavailable() || + error("Error, MKL binary is missing but solve is being called. Report this issue") require_one_based_indexing(A, ipiv, B) LinearAlgebra.LAPACK.chktrans(trans) chkstride1(A, B, ipiv) @@ -169,6 +210,8 @@ function getrs!(trans::AbstractChar, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat{<:Float32}; info = Ref{BlasInt}()) + __mkl_isavailable() || + error("Error, MKL binary is missing but solve is being called. Report this issue") require_one_based_indexing(A, ipiv, B) LinearAlgebra.LAPACK.chktrans(trans) chkstride1(A, B, ipiv) @@ -213,6 +256,8 @@ end function SciMLBase.solve!(cache::LinearCache, alg::MKLLUFactorization; kwargs...) + __mkl_isavailable() || + error("Error, MKL binary is missing but solve is being called. Report this issue") A = cache.A A = convert(AbstractMatrix, A) if cache.isfresh @@ -266,6 +311,8 @@ end function SciMLBase.solve!(cache::LinearCache, alg::MKL32MixedLUFactorization; kwargs...) + __mkl_isavailable() || + error("Error, MKL binary is missing but solve is being called. Report this issue") A = cache.A A = convert(AbstractMatrix, A) diff --git a/src/openblas.jl b/src/openblas.jl index ea6cb4210..1375faa9a 100644 --- a/src/openblas.jl +++ b/src/openblas.jl @@ -1,3 +1,5 @@ +using Libdl + """ ```julia OpenBLASLUFactorization() @@ -33,12 +35,38 @@ sol = solve(prob, OpenBLASLUFactorization()) """ struct OpenBLASLUFactorization <: AbstractFactorization end -# OpenBLAS methods - OpenBLAS_jll is always available as a standard library +# Check if OpenBLAS is available and can be loaded +function __openblas_isavailable() + if !@isdefined(OpenBLAS_jll) + return false + end + if !OpenBLAS_jll.is_available() + return false + end + # Try to load the library and check for required symbols + try + openblas_hdl = Libdl.dlopen(OpenBLAS_jll.libopenblas) + if openblas_hdl == C_NULL + return false + end + # Check for a required symbol + if Libdl.dlsym_e(openblas_hdl, "dgetrf_") == C_NULL + Libdl.dlclose(openblas_hdl) + return false + end + Libdl.dlclose(openblas_hdl) + return true + catch + return false + end +end function openblas_getrf!(A::AbstractMatrix{<:ComplexF64}; ipiv = similar(A, BlasInt, min(size(A, 1), size(A, 2))), info = Ref{BlasInt}(), check = false) + __openblas_isavailable() || + error("Error, OpenBLAS binary is missing but solve is being called. Report this issue") require_one_based_indexing(A) check && chkfinite(A) chkstride1(A) @@ -59,6 +87,8 @@ function openblas_getrf!(A::AbstractMatrix{<:ComplexF32}; ipiv = similar(A, BlasInt, min(size(A, 1), size(A, 2))), info = Ref{BlasInt}(), check = false) + __openblas_isavailable() || + error("Error, OpenBLAS binary is missing but solve is being called. Report this issue") require_one_based_indexing(A) check && chkfinite(A) chkstride1(A) @@ -79,6 +109,8 @@ function openblas_getrf!(A::AbstractMatrix{<:Float64}; ipiv = similar(A, BlasInt, min(size(A, 1), size(A, 2))), info = Ref{BlasInt}(), check = false) + __openblas_isavailable() || + error("Error, OpenBLAS binary is missing but solve is being called. Report this issue") require_one_based_indexing(A) check && chkfinite(A) chkstride1(A) @@ -99,6 +131,8 @@ function openblas_getrf!(A::AbstractMatrix{<:Float32}; ipiv = similar(A, BlasInt, min(size(A, 1), size(A, 2))), info = Ref{BlasInt}(), check = false) + __openblas_isavailable() || + error("Error, OpenBLAS binary is missing but solve is being called. Report this issue") require_one_based_indexing(A) check && chkfinite(A) chkstride1(A) @@ -120,6 +154,8 @@ function openblas_getrs!(trans::AbstractChar, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat{<:ComplexF64}; info = Ref{BlasInt}()) + __openblas_isavailable() || + error("Error, OpenBLAS binary is missing but solve is being called. Report this issue") require_one_based_indexing(A, ipiv, B) LinearAlgebra.LAPACK.chktrans(trans) chkstride1(A, B, ipiv) @@ -145,6 +181,8 @@ function openblas_getrs!(trans::AbstractChar, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat{<:ComplexF32}; info = Ref{BlasInt}()) + __openblas_isavailable() || + error("Error, OpenBLAS binary is missing but solve is being called. Report this issue") require_one_based_indexing(A, ipiv, B) LinearAlgebra.LAPACK.chktrans(trans) chkstride1(A, B, ipiv) @@ -170,6 +208,8 @@ function openblas_getrs!(trans::AbstractChar, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat{<:Float64}; info = Ref{BlasInt}()) + __openblas_isavailable() || + error("Error, OpenBLAS binary is missing but solve is being called. Report this issue") require_one_based_indexing(A, ipiv, B) LinearAlgebra.LAPACK.chktrans(trans) chkstride1(A, B, ipiv) @@ -195,6 +235,8 @@ function openblas_getrs!(trans::AbstractChar, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat{<:Float32}; info = Ref{BlasInt}()) + __openblas_isavailable() || + error("Error, OpenBLAS binary is missing but solve is being called. Report this issue") require_one_based_indexing(A, ipiv, B) LinearAlgebra.LAPACK.chktrans(trans) chkstride1(A, B, ipiv) @@ -239,6 +281,8 @@ end function SciMLBase.solve!(cache::LinearCache, alg::OpenBLASLUFactorization; kwargs...) + __openblas_isavailable() || + error("Error, OpenBLAS binary is missing but solve is being called. Report this issue") A = cache.A A = convert(AbstractMatrix, A) if cache.isfresh From b47ab29368d70b912c3a2b0a5a108f463124a90e Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 20 Aug 2025 18:16:16 -0400 Subject: [PATCH 2/3] Simplify availability checks - remove try/catch and symbol checking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per review feedback, simplified the availability checks: - Removed try/catch blocks as they're unnecessary - Removed Libdl symbol checking - if the binary exists via is_available(), it's fine - Just check if the JLL module is defined and is_available() returns true This makes the code cleaner and follows the principle that if the binary exists, we can trust it has the required symbols. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/mkl.jl | 24 ++---------------------- src/openblas.jl | 24 ++---------------------- 2 files changed, 4 insertions(+), 44 deletions(-) diff --git a/src/mkl.jl b/src/mkl.jl index 0f582511c..76faa31c4 100644 --- a/src/mkl.jl +++ b/src/mkl.jl @@ -1,5 +1,3 @@ -using Libdl - """ ```julia MKLLUFactorization() @@ -10,30 +8,12 @@ to avoid allocations and does not require libblastrampoline. """ struct MKLLUFactorization <: AbstractFactorization end -# Check if MKL is available and can be loaded +# Check if MKL is available function __mkl_isavailable() if !@isdefined(MKL_jll) return false end - if !MKL_jll.is_available() - return false - end - # Try to load the library and check for required symbols - try - mkl_hdl = Libdl.dlopen(MKL_jll.libmkl_rt) - if mkl_hdl == C_NULL - return false - end - # Check for a required symbol - if Libdl.dlsym_e(mkl_hdl, "dgetrf_") == C_NULL - Libdl.dlclose(mkl_hdl) - return false - end - Libdl.dlclose(mkl_hdl) - return true - catch - return false - end + return MKL_jll.is_available() end function getrf!(A::AbstractMatrix{<:ComplexF64}; diff --git a/src/openblas.jl b/src/openblas.jl index 1375faa9a..98faf1fdc 100644 --- a/src/openblas.jl +++ b/src/openblas.jl @@ -1,5 +1,3 @@ -using Libdl - """ ```julia OpenBLASLUFactorization() @@ -35,30 +33,12 @@ sol = solve(prob, OpenBLASLUFactorization()) """ struct OpenBLASLUFactorization <: AbstractFactorization end -# Check if OpenBLAS is available and can be loaded +# Check if OpenBLAS is available function __openblas_isavailable() if !@isdefined(OpenBLAS_jll) return false end - if !OpenBLAS_jll.is_available() - return false - end - # Try to load the library and check for required symbols - try - openblas_hdl = Libdl.dlopen(OpenBLAS_jll.libopenblas) - if openblas_hdl == C_NULL - return false - end - # Check for a required symbol - if Libdl.dlsym_e(openblas_hdl, "dgetrf_") == C_NULL - Libdl.dlclose(openblas_hdl) - return false - end - Libdl.dlclose(openblas_hdl) - return true - catch - return false - end + return OpenBLAS_jll.is_available() end function openblas_getrf!(A::AbstractMatrix{<:ComplexF64}; From 8a16d532948071a81d156c07036d8b5dcb7739dd Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 20 Aug 2025 18:24:51 -0400 Subject: [PATCH 3/3] Use @static if for compile-time availability checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed the availability check functions to use @static if for compile-time determination, matching the pattern used in AppleAccelerate: - Uses @static if to check @isdefined at compile time - Returns is_available() result statically when JLL is defined - Returns false statically when JLL is not defined This makes the checks more efficient as they're resolved at compile time rather than runtime. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/mkl.jl | 9 ++++----- src/openblas.jl | 9 ++++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/mkl.jl b/src/mkl.jl index 76faa31c4..d813887d3 100644 --- a/src/mkl.jl +++ b/src/mkl.jl @@ -9,11 +9,10 @@ to avoid allocations and does not require libblastrampoline. struct MKLLUFactorization <: AbstractFactorization end # Check if MKL is available -function __mkl_isavailable() - if !@isdefined(MKL_jll) - return false - end - return MKL_jll.is_available() +@static if !@isdefined(MKL_jll) + __mkl_isavailable() = false +else + __mkl_isavailable() = MKL_jll.is_available() end function getrf!(A::AbstractMatrix{<:ComplexF64}; diff --git a/src/openblas.jl b/src/openblas.jl index 98faf1fdc..9fa7951cc 100644 --- a/src/openblas.jl +++ b/src/openblas.jl @@ -34,11 +34,10 @@ sol = solve(prob, OpenBLASLUFactorization()) struct OpenBLASLUFactorization <: AbstractFactorization end # Check if OpenBLAS is available -function __openblas_isavailable() - if !@isdefined(OpenBLAS_jll) - return false - end - return OpenBLAS_jll.is_available() +@static if !@isdefined(OpenBLAS_jll) + __openblas_isavailable() = false +else + __openblas_isavailable() = OpenBLAS_jll.is_available() end function openblas_getrf!(A::AbstractMatrix{<:ComplexF64};