Skip to content

Commit ca0e50c

Browse files
committed
add BLAS verbosity
1 parent f937f2a commit ca0e50c

File tree

9 files changed

+560
-175
lines changed

9 files changed

+560
-175
lines changed

docs/src/basics/common_solver_opts.md

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,16 @@ The following verbosity levels are available:
4444

4545
#### Individual Settings
4646
These settings are meant for individual settings within a category. These can also be used to set all of the individual settings in a group to the same value.
47-
- Verbosity.None() - Suppress all messages
48-
- Verbosity.Info() - Show message as log message at info level
49-
- Verbosity.Warn() - Show warnings (default for most settings)
50-
- Verbosity.Error() - Throw errors instead of warnings
51-
- Verbosity.Level(n) - Show messages with a log level setting of n
47+
- SciMLLogging.None() - Suppress all messages
48+
- SciMLLogging.Info() - Show message as log message at info level
49+
- SciMLLogging.Warn() - Show warnings (default for most settings)
50+
- SciMLLogging.Error() - Throw errors instead of warnings
51+
- SciMLLogging.Level(n) - Show messages with a log level setting of n
5252

5353
#### Group Settings
5454
These settings are meant for controlling a group of settings.
55-
- Verbosity.Default() - Use the default settings
56-
- Verbosity.All() - Show all possible messages
55+
- SciMLLogging.Default() - Use the default settings
56+
- SciMLLogging.All() - Show all possible messages
5757

5858
### Basic Usage
5959

@@ -63,16 +63,16 @@ These settings are meant for controlling a group of settings.
6363
using LinearSolve
6464

6565
# Suppress all messages
66-
verbose = LinearVerbosity(Verbosity.None())
66+
verbose = LinearVerbosity(SciMLLogging.None())
6767
prob = LinearProblem(A, b)
6868
sol = solve(prob; verbose=verbose)
6969

7070
# Show all messages
71-
verbose = LinearVerbosity(Verbosity.All())
71+
verbose = LinearVerbosity(SciMLLogging.All())
7272
sol = solve(prob; verbose=verbose)
7373

7474
# Use default settings
75-
verbose = LinearVerbosity(Verbosity.Default())
75+
verbose = LinearVerbosity(SciMLLogging.Default())
7676
sol = solve(prob; verbose=verbose)
7777
```
7878

@@ -81,9 +81,9 @@ sol = solve(prob; verbose=verbose)
8181
```julia
8282
# Customize by category
8383
verbose = LinearVerbosity(
84-
error_control = Verbosity.Warn(), # Show warnings for error control related issues
85-
performance = Verbosity.None(), # Suppress performance messages
86-
numerical = Verbosity.Info() # Show all numerical related log messages at info level
84+
error_control = SciMLLogging.Warn(), # Show warnings for error control related issues
85+
performance = SciMLLogging.None(), # Suppress performance messages
86+
numerical = SciMLLogging.Info() # Show all numerical related log messages at info level
8787
)
8888

8989
sol = solve(prob; verbose=verbose)
@@ -95,10 +95,10 @@ The verbosity settings for the toggles are automatically passed to the group obj
9595
```julia
9696
# Set specific message types
9797
verbose = LinearVerbosity(
98-
default_lu_fallback = Verbosity.Info(), # Show info when LU fallback is used
99-
KrylovJL_verbosity = Verbosity.Warn(), # Show warnings from KrylovJL
100-
no_right_preconditioning = Verbosity.None(), # Suppress right preconditioning messages
101-
KrylovKit_verbosity = Verbosity.Level(KrylovKit.WARN_LEVEL) # Set KrylovKit verbosity level using KrylovKit's own verbosity levels
98+
default_lu_fallback = SciMLLogging.Info(), # Show info when LU fallback is used
99+
KrylovJL_verbosity = SciMLLogging.Warn(), # Show warnings from KrylovJL
100+
no_right_preconditioning = SciMLLogging.None(), # Suppress right preconditioning messages
101+
KrylovKit_verbosity = SciMLLogging.Level(KrylovKit.WARN_LEVEL) # Set KrylovKit verbosity level using KrylovKit's own verbosity levels
102102
)
103103

104104
sol = solve(prob; verbose=verbose)

ext/LinearSolveBLISExt.jl

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ using LinearSolve
99
using LinearAlgebra: BlasInt, LU
1010
using LinearAlgebra.LAPACK: require_one_based_indexing, chkfinite, chkstride1,
1111
@blasfunc, chkargsok
12-
using LinearSolve: ArrayInterface, BLISLUFactorization, @get_cacheval, LinearCache, SciMLBase, LinearVerbosity
12+
using LinearSolve: ArrayInterface, BLISLUFactorization, @get_cacheval, LinearCache, SciMLBase, LinearVerbosity, get_blas_operation_info, blas_info_msg
13+
using SciMLLogging: SciMLLogging, @SciMLMessage
1314
using SciMLBase: ReturnCode
1415

1516
const global libblis = blis_jll.blis
@@ -220,12 +221,45 @@ function SciMLBase.solve!(cache::LinearCache, alg::BLISLUFactorization;
220221
kwargs...)
221222
A = cache.A
222223
A = convert(AbstractMatrix, A)
224+
verbose = cache.verbose
223225
if cache.isfresh
224226
cacheval = @get_cacheval(cache, :BLISLUFactorization)
225227
res = getrf!(A; ipiv = cacheval[1].ipiv, info = cacheval[2])
226228
fact = LU(res[1:3]...), res[4]
227229
cache.cacheval = fact
228230

231+
info_value = res[3]
232+
233+
if info_value != 0
234+
if !isa(verbose.blas_info, SciMLLogging.Silent) || !isa(verbose.blas_errors, SciMLLogging.Silent) ||
235+
!isa(verbose.blas_invalid_args, SciMLLogging.Silent)
236+
op_info = get_blas_operation_info(:dgetrf, A, cache.b, condition = !isa(verbose.condition_number, SciMLLogging.Silent))
237+
@SciMLMessage(cache.verbose, :condition_number) do
238+
if op_info[:condition_number] === nothing
239+
return "Matrix condition number calculation failed."
240+
else
241+
return "Matrix condition number: $(round(op_info[:condition_number], sigdigits=4)) for $(size(A, 1))×$(size(A, 2)) matrix in dgetrf"
242+
end
243+
end
244+
verb_option, message = blas_info_msg(
245+
:dgetrf, info_value; extra_context = op_info)
246+
@SciMLMessage(message, verbose, verb_option)
247+
end
248+
else
249+
@SciMLMessage(cache.verbose, :blas_success) do
250+
op_info = get_blas_operation_info(:dgetrf, A, cache.b,
251+
condition = !isa(verbose.condition_number, SciMLLogging.Silent))
252+
@SciMLMessage(cache.verbose, :condition_number) do
253+
if op_info[:condition_number] === nothing
254+
return "Matrix condition number calculation failed."
255+
else
256+
return "Matrix condition number: $(round(op_info[:condition_number], sigdigits=4)) for $(size(A, 1))×$(size(A, 2)) matrix in dgetrf"
257+
end
258+
end
259+
return "BLAS LU factorization (dgetrf) completed successfully for $(op_info[:matrix_size]) matrix"
260+
end
261+
end
262+
229263
if !LinearAlgebra.issuccess(fact[1])
230264
return SciMLBase.build_linear_solution(
231265
alg, cache.u, nothing, cache; retcode = ReturnCode.Failure)

ext/LinearSolveForwardDiffExt.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ function __dual_init(
137137
abstol = LinearSolve.default_tol(real(eltype(prob.b))),
138138
reltol = LinearSolve.default_tol(real(eltype(prob.b))),
139139
maxiters::Int = length(prob.b),
140-
verbose = LinearVerbosity(Verbosity.None()),
140+
verbose = LinearVerbosity(SciMLLogging.None()),
141141
Pl = nothing,
142142
Pr = nothing,
143143
assumptions = OperatorAssumptions(issquare(prob.A)),

ext/LinearSolveHYPREExt.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ function SciMLBase.init(prob::LinearProblem, alg::HYPREAlgorithm,
6565
eltype(prob.A)),
6666
# TODO: Implement length() for HYPREVector in HYPRE.jl?
6767
maxiters::Int = prob.b isa HYPREVector ? 1000 : length(prob.b),
68-
verbose = LinearVerbosity(Verbosity.None()),
68+
verbose = LinearVerbosity(SciMLLogging.None()),
6969
Pl = LinearAlgebra.I,
7070
Pr = LinearAlgebra.I,
7171
assumptions = OperatorAssumptions(),
@@ -118,9 +118,9 @@ function SciMLBase.init(prob::LinearProblem, alg::HYPREAlgorithm,
118118
if verbose
119119
verbose = LinearVerbosity()
120120
else
121-
verbose = LinearVerbosity(Verbosity.None())
121+
verbose = LinearVerbosity(SciMLLogging.None())
122122
end
123-
elseif verbose isa Verbosity.Type
123+
elseif verbose isa SciMLLogging.Type
124124
verbose = LinearVerbosity(verbose)
125125
end
126126

src/LinearSolve.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ using SciMLBase: SciMLBase, LinearAliasSpecifier, AbstractSciMLOperator,
2121
using SciMLOperators: SciMLOperators, AbstractSciMLOperator, IdentityOperator,
2222
MatrixOperator,
2323
has_ldiv!, issquare
24-
using SciMLLogging: Verbosity, @SciMLMessage, verbosity_to_int, AbstractVerbositySpecifier
24+
using SciMLLogging: SciMLLogging, @SciMLMessage, verbosity_to_int, AbstractVerbositySpecifier, LogLevel, VerbosityPreset
2525
using Setfield: @set, @set!
2626
using UnPack: @unpack
2727
using DocStringExtensions: DocStringExtensions
@@ -363,6 +363,7 @@ const BLASELTYPES = Union{Float32, Float64, ComplexF32, ComplexF64}
363363
function defaultalg_symbol end
364364

365365
include("verbosity.jl")
366+
include("blas_logging.jl")
366367
include("generic_lufact.jl")
367368
include("common.jl")
368369
include("extension_algs.jl")

src/blas_logging.jl

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
2+
"""
3+
interpret_blas_code(func::Symbol, info::Integer)
4+
5+
Interpret BLAS/LAPACK return codes (info parameter) to provide human-readable error messages.
6+
Returns a tuple of (category::Symbol, message::String, details::String)
7+
"""
8+
function interpret_blas_code(func::Symbol, info::Integer)
9+
if info == 0
10+
return (:success, "Operation completed successfully", "")
11+
elseif info < 0
12+
return (:invalid_argument,
13+
"Invalid argument error",
14+
"Argument $(-info) had an illegal value")
15+
else
16+
# info > 0 means different things for different functions
17+
return interpret_positive_info(func, info)
18+
end
19+
end
20+
21+
function interpret_positive_info(func::Symbol, info::Integer)
22+
func_str = string(func)
23+
24+
# LU factorization routines
25+
if occursin("getrf", func_str)
26+
return (:singular_matrix,
27+
"Matrix is singular",
28+
"U($info,$info) is exactly zero. The factorization has been completed, but U is singular and division by U will produce infinity.")
29+
30+
# Cholesky factorization routines
31+
elseif occursin("potrf", func_str)
32+
return (:not_positive_definite,
33+
"Matrix is not positive definite",
34+
"The leading minor of order $info is not positive definite, and the factorization could not be completed.")
35+
36+
# QR factorization routines
37+
elseif occursin("geqrf", func_str) || occursin("geqrt", func_str)
38+
return (:numerical_issue,
39+
"Numerical issue in QR factorization",
40+
"Householder reflector $info could not be formed properly.")
41+
42+
# SVD routines
43+
elseif occursin("gesdd", func_str) || occursin("gesvd", func_str)
44+
return (:convergence_failure,
45+
"SVD did not converge",
46+
"The algorithm failed to compute singular values. $info off-diagonal elements of an intermediate bidiagonal form did not converge to zero.")
47+
48+
# Symmetric/Hermitian eigenvalue routines
49+
elseif occursin("syev", func_str) || occursin("heev", func_str)
50+
return (:convergence_failure,
51+
"Eigenvalue computation did not converge",
52+
"$info off-diagonal elements of an intermediate tridiagonal form did not converge to zero.")
53+
54+
# Bunch-Kaufman factorization
55+
elseif occursin("sytrf", func_str) || occursin("hetrf", func_str)
56+
return (:singular_matrix,
57+
"Matrix is singular",
58+
"D($info,$info) is exactly zero. The factorization has been completed, but the block diagonal matrix D is singular.")
59+
60+
# Solve routines (should not have positive info)
61+
elseif occursin("getrs", func_str) || occursin("potrs", func_str) ||
62+
occursin("sytrs", func_str) || occursin("hetrs", func_str)
63+
return (:unexpected_error,
64+
"Unexpected positive return code from solve routine",
65+
"Solve routine $func returned info=$info which should not happen.")
66+
67+
# General eigenvalue problem
68+
elseif occursin("ggev", func_str) || occursin("gges", func_str)
69+
if info <= size
70+
return (:convergence_failure,
71+
"QZ iteration failed",
72+
"The QZ iteration failed to compute all eigenvalues. Elements 1:$(info-1) converged.")
73+
else
74+
return (:unexpected_error,
75+
"Unexpected error in generalized eigenvalue problem",
76+
"Info value $info is unexpected for $func.")
77+
end
78+
79+
# LDLT factorization
80+
elseif occursin("ldlt", func_str)
81+
return (:singular_matrix,
82+
"Matrix is singular",
83+
"The $(info)-th pivot is zero. The factorization has been completed but division will produce infinity.")
84+
85+
# Default case
86+
else
87+
return (:unknown_error,
88+
"Unknown positive return code",
89+
"Function $func returned info=$info. Consult LAPACK documentation for details.")
90+
end
91+
end
92+
93+
94+
95+
"""
96+
blas_info_msg(func::Symbol, info::Integer, verbose::LinearVerbosity;
97+
extra_context::Dict{Symbol,Any} = Dict())
98+
99+
Log BLAS/LAPACK return code information with appropriate verbosity level.
100+
"""
101+
function blas_info_msg(func::Symbol, info::Integer;
102+
extra_context::Dict{Symbol, Any} = Dict())
103+
category, message, details = interpret_blas_code(func, info)
104+
105+
verbosity_field = if category in [:singular_matrix, :not_positive_definite, :convergence_failure]
106+
:blas_errors
107+
elseif category == :invalid_argument
108+
:blas_invalid_args
109+
else
110+
:blas_info
111+
end
112+
113+
# Build structured message components
114+
msg_main = "BLAS/LAPACK $func: $message"
115+
msg_details = !isempty(details) ? details : nothing
116+
msg_info = info
117+
118+
# Build complete message with all details
119+
full_msg = if !isempty(extra_context) || msg_details !== nothing
120+
parts = String[msg_main]
121+
if msg_details !== nothing
122+
push!(parts, "Details: $msg_details")
123+
end
124+
push!(parts, "Return code (info): $msg_info")
125+
if !isempty(extra_context)
126+
for (key, value) in extra_context
127+
push!(parts, "$key: $value")
128+
end
129+
end
130+
join(parts, "\n ")
131+
else
132+
"$msg_main (info=$msg_info)"
133+
end
134+
135+
verbosity_field, full_msg
136+
end
137+
138+
139+
function get_blas_operation_info(func::Symbol, A, b; condition = false)
140+
info = Dict{Symbol, Any}()
141+
142+
# Matrix properties
143+
info[:matrix_size] = size(A)
144+
info[:matrix_type] = typeof(A)
145+
info[:element_type] = eltype(A)
146+
147+
# Condition number (based on verbosity setting)
148+
if condition && size(A, 1) == size(A, 2)
149+
try
150+
cond_num = cond(A)
151+
info[:condition_number] = cond_num
152+
153+
# Log the condition number if enabled
154+
cond_msg = "Matrix condition number: $(round(cond_num, sigdigits=4)) for $(size(A, 1))×$(size(A, 2)) matrix in $func"
155+
156+
catch
157+
# Skip if condition number computation fails
158+
info[:condition_number] = nothing
159+
end
160+
end
161+
162+
# RHS properties if provided
163+
if b !== nothing
164+
info[:rhs_size] = size(b)
165+
info[:rhs_type] = typeof(b)
166+
end
167+
168+
# Memory usage estimate
169+
mem_bytes = prod(size(A)) * sizeof(eltype(A))
170+
info[:memory_usage_MB] = round(mem_bytes / 1024^2, digits = 2)
171+
172+
return info
173+
end

src/common.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ function __init(prob::LinearProblem, alg::SciMLLinearSolveAlgorithm,
332332
else
333333
verbose_spec = LinearVerbosity{false}()
334334
end
335-
elseif verbose isa Verbosity.VerbosityPreset
335+
elseif verbose isa SciMLLogging.VerbosityPreset
336336
verbose_spec = LinearVerbosity(verbose)
337337
else
338338
verbose_spec = verbose

0 commit comments

Comments
 (0)