-
-
Notifications
You must be signed in to change notification settings - Fork 105
Linear Verbosity Specifiers #1049
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
84978db
72bf843
8d4ff8a
f69d5d3
91a421b
3373aaf
233818c
c2b2ad8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,298 @@ | ||
@data Verbosity begin | ||
None | ||
Info | ||
Warn | ||
Error | ||
Level(Int) | ||
Edge | ||
All | ||
Default | ||
end | ||
|
||
# Linear Verbosity | ||
|
||
linear_defaults = Dict( | ||
:default_lu_fallback => Verbosity.Warn(), | ||
:no_right_preconditioning => Verbosity.Warn(), | ||
:using_iterative_solvers => Verbosity.Warn(), | ||
:using_IterativeSolvers => Verbosity.Warn(), | ||
:IterativeSolvers_iterations => Verbosity.Warn(), | ||
:KrylovKit_verbosity => Verbosity.Warn(), | ||
:KrylovJL_verbosity => Verbosity.None() | ||
) | ||
Comment on lines
+14
to
+22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should live in LinearSolve |
||
mutable struct LinearErrorControlVerbosity | ||
default_lu_fallback::Verbosity.Type | ||
|
||
function LinearErrorControlVerbosity(; | ||
default_lu_fallback = linear_defaults[:default_lu_fallback]) | ||
new(default_lu_fallback) | ||
end | ||
|
||
function LinearErrorControlVerbosity(verbose::Verbosity.Type) | ||
@match verbose begin | ||
Verbosity.None() => new(fill( | ||
Verbosity.None(), length(fieldnames(LinearErrorControlVerbosity)))...) | ||
|
||
Verbosity.Info() => new(fill( | ||
Verbosity.Info(), length(fieldnames(LinearErrorControlVerbosity)))...) | ||
|
||
Verbosity.Warn() => new(fill( | ||
Verbosity.Warn(), length(fieldnames(LinearErrorControlVerbosity)))...) | ||
|
||
Verbosity.Error() => new(fill( | ||
Verbosity.Error(), length(fieldnames(LinearErrorControlVerbosity)))...) | ||
|
||
Verbosity.Default() => LinearErrorControlVerbosity() | ||
|
||
Verbosity.Edge() => LinearErrorControlVerbosity() | ||
|
||
_ => @error "Not a valid choice for verbosity." | ||
end | ||
end | ||
end | ||
|
||
|
||
mutable struct LinearPerformanceVerbosity | ||
no_right_preconditioning::Verbosity.Type | ||
|
||
function LinearPerformanceVerbosity(; | ||
no_right_preconditioning = linear_defaults[:no_right_preconditioning]) | ||
new(no_right_preconditioning) | ||
end | ||
|
||
function LinearPerformanceVerbosity(verbose::Verbosity.Type) | ||
@match verbose begin | ||
Verbosity.None() => new(fill( | ||
Verbosity.None(), length(fieldnames(LinearPerformanceVerbosity)))...) | ||
|
||
Verbosity.Info() => new(fill( | ||
Verbosity.Info(), length(fieldnames(LinearPerformanceVerbosity)))...) | ||
|
||
Verbosity.Warn() => new(fill( | ||
Verbosity.Warn(), length(fieldnames(LinearPerformanceVerbosity)))...) | ||
|
||
Verbosity.Error() => new(fill( | ||
Verbosity.Error(), length(fieldnames(LinearPerformanceVerbosity)))...) | ||
|
||
Verbosity.Default() => LinearPerformanceVerbosity() | ||
|
||
Verbosity.Edge() => LinearPerformanceVerbosity() | ||
|
||
_ => @error "Not a valid choice for verbosity." | ||
end | ||
end | ||
|
||
end | ||
|
||
mutable struct LinearNumericalVerbosity | ||
using_IterativeSolvers::Verbosity.Type | ||
IterativeSolvers_iterations::Verbosity.Type | ||
KrylovKit_verbosity::Verbosity.Type | ||
KrylovJL_verbosity::Verbosity.Type | ||
|
||
function LinearNumericalVerbosity(; | ||
using_IterativeSolvers = linear_defaults[:using_IterativeSolvers], | ||
IterativeSolvers_iterations = linear_defaults[:IterativeSolvers_iterations], | ||
KrylovKit_verbosity = linear_defaults[:KrylovKit_verbosity], | ||
KrylovJL_verbosity = linear_defaults[:KrylovJL_verbosity]) | ||
new(using_IterativeSolvers, IterativeSolvers_iterations, KrylovKit_verbosity, KrylovJL_verbosity) | ||
end | ||
|
||
function LinearNumericalVerbosity(verbose::Verbosity.Type) | ||
@match verbose begin | ||
Verbosity.None() => new(fill( | ||
Verbosity.None(), length(fieldnames(LinearNumericalVerbosity)))...) | ||
|
||
Verbosity.Info() => new(fill( | ||
Verbosity.Info(), length(fieldnames(LinearNumericalVerbosity)))...) | ||
|
||
Verbosity.Warn() => new(fill( | ||
Verbosity.Warn(), length(fieldnames(LinearNumericalVerbosity)))...) | ||
|
||
Verbosity.Error() => new(fill( | ||
Verbosity.Error(), length(fieldnames(LinearNumericalVerbosity)))...) | ||
|
||
Verbosity.Default() => LinearNumericalVerbosity() | ||
|
||
Verbosity.Edge() => LinearNumericalVerbosity() | ||
|
||
_ => @error "Not a valid choice for verbosity." | ||
end | ||
end | ||
end | ||
|
||
|
||
|
||
struct LinearVerbosity{T} <: AbstractVerbositySpecifier{T} | ||
error_control::LinearErrorControlVerbosity | ||
performance::LinearPerformanceVerbosity | ||
numerical::LinearNumericalVerbosity | ||
end | ||
|
||
function LinearVerbosity(verbose::Verbosity.Type) | ||
@match verbose begin | ||
Verbosity.Default() => LinearVerbosity{true}( | ||
LinearErrorControlVerbosity(Verbosity.Default()), | ||
LinearPerformanceVerbosity(Verbosity.Default()), | ||
LinearNumericalVerbosity(Verbosity.Default()) | ||
) | ||
|
||
Verbosity.None() => LinearVerbosity{false}( | ||
LinearErrorControlVerbosity(Verbosity.None()), | ||
LinearPerformanceVerbosity(Verbosity.None()), | ||
LinearNumericalVerbosity(Verbosity.None())) | ||
|
||
Verbosity.All() => LinearVerbosity{true}( | ||
LinearErrorControlVerbosity(Verbosity.Info()), | ||
LinearPerformanceVerbosity(Verbosity.Info()), | ||
LinearNumericalVerbosity(Verbosity.Info()) | ||
) | ||
|
||
_ => @error "Not a valid choice for LinearVerbosity. Available choices are `Default`, `None`, and `All`." | ||
end | ||
end | ||
|
||
function LinearVerbosity(; | ||
error_control = Verbosity.Default(), performance = Verbosity.Default(), | ||
numerical = Verbosity.Default(), kwargs...) | ||
if error_control isa Verbosity.Type | ||
error_control_verbosity = LinearErrorControlVerbosity(error_control) | ||
else | ||
error_control_verbosity = error_control | ||
end | ||
|
||
if performance isa Verbosity.Type | ||
performance_verbosity = LinearPerformanceVerbosity(performance) | ||
else | ||
performance_verbosity = performance | ||
end | ||
|
||
if numerical isa Verbosity.Type | ||
numerical_verbosity = LinearNumericalVerbosity(numerical) | ||
else | ||
numerical_verbosity = numerical | ||
end | ||
|
||
if !isempty(kwargs) | ||
for (key, value) in pairs(kwargs) | ||
if hasfield(LinearErrorControlVerbosity, key) | ||
setproperty!(error_control_verbosity, key, value) | ||
elseif hasfield(LinearPerformanceVerbosity, key) | ||
setproperty!(performance_verbosity, key, value) | ||
elseif hasfield(LinearNumericalVerbosity, key) | ||
setproperty!(numerical_verbosity, key, value) | ||
else | ||
error("$key is not a recognized verbosity toggle.") | ||
end | ||
end | ||
end | ||
|
||
LinearVerbosity{true}(error_control_verbosity, | ||
performance_verbosity, numerical_verbosity) | ||
end | ||
Comment on lines
+55
to
+192
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All of this should go to linearsolve since it will be edited alongside the code |
||
|
||
# Utilities | ||
|
||
function message_level(verbose::AbstractVerbositySpecifier{true}, option, group) | ||
group = getproperty(verbose, group) | ||
opt_level = getproperty(group, option) | ||
Comment on lines
+197
to
+198
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this mean every There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes but this can definitely be changed to be more flexible. |
||
|
||
@match opt_level begin | ||
Verbosity.None() => nothing | ||
Verbosity.Info() => Logging.Info | ||
Verbosity.Warn() => Logging.Warn | ||
Verbosity.Error() => Logging.Error | ||
Verbosity.Level(i) => Logging.LogLevel(i) | ||
end | ||
end | ||
|
||
function emit_message( | ||
f::Function, verbose::V, option, group, file, line, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if we don't type this as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah that makes sense. |
||
_module) where {V <: AbstractVerbositySpecifier{true}} | ||
level = message_level( | ||
verbose, option, group) | ||
if !isnothing(level) | ||
message = f() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we pass the option and group here? This allows for the function to have side effects at specific locations. My main use case is hooking into Infiltrator.jl. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think what we could do is have a 'Code' verbosity type that holds a quote, and we can modify the '@SciMLMessage' macro to evaluate the quote when called. That way you can just set one of the toggles of the verbosity object to 'Code(:Main.@infiltrate)' and it will infiltrate whenever it hits the part of the code associated with the toggle. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm pretty sure runtime There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I probably used the wrong words, but I think if I write the macro correctly it should have the same access as surrounding code. Anyways the details can be worked out when I get back on Monday 🙂 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, sounds good. |
||
Base.@logmsg level message _file=file _line=line _module=_module | ||
end | ||
end | ||
|
||
function emit_message(message::String, verbose::V, | ||
option, group, file, line, _module) where {V <: AbstractVerbositySpecifier{true}} | ||
level = message_level(verbose, option, group) | ||
|
||
if !isnothing(level) | ||
Base.@logmsg level message _file=file _line=line _module=_module _group = group | ||
end | ||
end | ||
|
||
function emit_message( | ||
f, verbose::AbstractVerbositySpecifier{false}, option, group, file, line, _module) | ||
end | ||
|
||
@doc doc""" | ||
A macro that emits a log message based on the log level specified in the `option` and `group` of the `AbstractVerbositySpecifier` supplied. | ||
|
||
`f_or_message` may be a message String, or a 0-argument function that returns a String. | ||
|
||
## Usage | ||
To emit a simple string, `@SciMLMessage("message", verbosity, :option, :group)` will emit a log message with the LogLevel specified in `verbosity`, at the appropriate `option` and `group`. | ||
|
||
`@SciMLMessage` can also be used to emit a log message coming from the evaluation of a 0-argument function. This function is resolved in the environment of the macro call. | ||
Therefore it can use variables from the surrounding environment. This may be useful if the log message writer wishes to carry out some calculations using existing variables | ||
and use them in the log message. | ||
|
||
```julia | ||
x = 10 | ||
y = 20 | ||
|
||
@SciMLMessage(verbosity, :option, :group) do | ||
z = x + y | ||
"Message is: x + y = \$z" | ||
end | ||
``` | ||
""" | ||
macro SciMLMessage(f_or_message, verb, option, group) | ||
line = __source__.line | ||
file = string(__source__.file) | ||
_module = __module__ | ||
return :(emit_message( | ||
$(esc(f_or_message)), $(esc(verb)), $option, $group, $file, $line, $_module)) | ||
end | ||
|
||
function SciMLLogger(; info_repl = true, warn_repl = true, error_repl = true, | ||
info_file = nothing, warn_file = nothing, error_file = nothing) | ||
info_sink = isnothing(info_file) ? NullLogger() : FileLogger(info_file) | ||
warn_sink = isnothing(warn_file) ? NullLogger() : FileLogger(warn_file) | ||
error_sink = isnothing(error_file) ? NullLogger() : FileLogger(error_file) | ||
|
||
repl_filter = EarlyFilteredLogger(current_logger()) do log | ||
if log.level == Logging.Info && info_repl | ||
return true | ||
end | ||
|
||
if log.level == Logging.Warn && warn_repl | ||
return true | ||
end | ||
|
||
if log.level == Logging.Error && error_repl | ||
return true | ||
end | ||
|
||
return false | ||
end | ||
|
||
info_filter = EarlyFilteredLogger(info_sink) do log | ||
log.level == Logging.Info | ||
end | ||
|
||
warn_filter = EarlyFilteredLogger(warn_sink) do log | ||
log.level == Logging.Warn | ||
end | ||
|
||
error_filter = EarlyFilteredLogger(error_sink) do log | ||
log.level == Logging.Error | ||
end | ||
|
||
TeeLogger(repl_filter, info_filter, warn_filter, error_filter) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is this macro? It's not standard, namespace it.