Skip to content

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

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

jClugstor
Copy link
Member

Checklist

  • Appropriate tests were added
  • Any code changes were done in a way that does not break public API
  • All documentation related to code changes were updated
  • The new code follows the
    contributor guidelines, in particular the SciML Style Guide and
    COLPRAC.
  • Any new documentation only uses public API

Additional context

#962

This contains just the things necessary to use the verbosity system with LinearSolve, so we can test things out and make sure that this meets the requirements for the verbosity system.

A Moshi.jl ADT is used for the base verbosity specifiers. LinearVerbosity holds a LinearErrorControlVerbosity, a LinearPerformanceVerbosity, and a LinearNumericalVerbosity. The @SciMLMessage macro takes a String or a function that returns a String, a verbosity object, a symbol representing the base toggle, and a symbol representing what group the message belongs to.

using SciMLBase
using SciMLBase: Verbosity


verbose = SciMLBase.LinearVerbosity(default_lu_fallback=Verbosity.Warn())

SciMLBase.@SciMLMessage("LU factorization failed, switching to QR factorization", verbose, :default_lu_fallback, :error_control)


verbose = SciMLBase.LinearVerbosity(numerical = Verbosity.Info())

SciMLBase.@SciMLMessage("Using IterativeSolvers", verbose, :using_IterativeSolvers, :numerical)


error_verb = SciMLBase.LinearErrorControlVerbosity(Verbosity.Info())

verbose = SciMLBase.LinearVerbosity(error_control = error_verb)

SciMLBase.@SciMLMessage("LU factorization failed, switching to QR factorization", verbose, :default_lu_fallback, :error_control)


x = 10
y = 20

SciMLBase.@SciMLMessage(verbose, :default_lu_fallback, :error_control) do 
    z = x + y
    "z is $z."
end

@jClugstor
Copy link
Member Author

jClugstor commented Jun 11, 2025

@ChrisRackauckas this is the PR with the types and constructors for LinearVerbosity, along with the @SciMLMessage macro. This stuff is used in SciML/LinearSolve.jl#622

@@ -0,0 +1,298 @@
@data Verbosity begin
Copy link
Member

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.

Comment on lines +14 to +22
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()
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should live in LinearSolve

Comment on lines +55 to +192
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
Copy link
Member

Choose a reason for hiding this comment

The 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

Copy link
Member

@AayushSabharwal AayushSabharwal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice if this can be moved to something like SciMLVerbosity.jl so

  • packages not depending on SciMLBase can use this
  • we can version it independently and break the API if we need to

I'd really like to use this in MTK and possibly Symbolics.

Comment on lines +197 to +198
group = getproperty(verbose, group)
opt_level = getproperty(group, option)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean every AbstractVerbositySpecifier has to have exactly 2 levels of nesting?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes but this can definitely be changed to be more flexible.

end

function emit_message(
f::Function, verbose::V, option, group, file, line,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if we don't type this as ::Function, so that any callable can go here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that makes sense.

level = message_level(
verbose, option, group)
if !isnothing(level)
message = f()
Copy link
Member

Choose a reason for hiding this comment

The 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.

Copy link
Member Author

Choose a reason for hiding this comment

The 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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure runtime eval doesn't have access to local function scope, which means @infiltrate won't really do anything useful.

Copy link
Member Author

Choose a reason for hiding this comment

The 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 🙂

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, sounds good.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants