Skip to content

Conversation

@SouthEndMusic
Copy link
Collaborator

@SouthEndMusic SouthEndMusic commented Oct 1, 2025

Fixes #2176 (?)

I haven't really looked into scaling yet, but I found an avenue for performance improvement: evaluating the Jacobian with mixed mode AD where the reverse mode is AutoMooncake(). Some notes:

@SouthEndMusic SouthEndMusic marked this pull request as draft October 1, 2025 10:53
@SouthEndMusic SouthEndMusic requested a review from visr October 6, 2025 06:38
@gdalle
Copy link

gdalle commented Oct 6, 2025

Just popping by to say that the bug you found is indeed easy to fix, but that bidirectional coloring is still fairly new and not widely used. In particular, the number of colors it returns for each side is fairly unpredictable, as you have noticed. I'll be tinkering with ways to weight forward and reverse mode differently, in order to favor forward colors when possible

@SouthEndMusic
Copy link
Collaborator Author

SouthEndMusic commented Oct 6, 2025

@gdalle In this PR I loop over the orderings for the mixed mode coloring and check the Jacobian evaluation time:

Ribasim/core/src/model.jl

Lines 80 to 104 in 33f082c

if solver.sparse && experimental.optimize_jacobian
backend_jac = nothing
jac_prep = nothing
t_min_jac_eval = Inf
for order in
(NaturalOrder, LargestFirst, SmallestLast, IncidenceDegree, DynamicLargestFirst)
backend = get_jac_ad_backend(solver; order, mixed_mode = true)
jac_prep_option = jac_prep_from_backend(backend)
J = Float64.(sparsity_pattern(jac_prep_option))
args = (J, u_raw, p, t, jac_prep_option, backend)
# First evaluate only for precompilation purposes
jac_from_jac_prep(args...)
t_jac_eval = @elapsed for _ in 1:10
jac_from_jac_prep(args...)
end
if t_jac_eval < t_min_jac_eval
t_min_jac_eval = t_jac_eval
backend_jac = backend
jac_prep = jac_prep_option
end
end
else
backend_jac = get_jac_ad_backend(solver)
jac_prep = jac_prep_from_backend(backend_jac)
end

As an (opt-in) feature you could do something similar internally where you time a forward and reverse pass to use as weights in the coloring algorithm

@gdalle
Copy link

gdalle commented Nov 4, 2025

Hi @SouthEndMusic,
@amontoison and I have been working on ways to target specifically the number of row colors or the number of column colors to be minimized during postprocessing. This should allow a bit more control over how many reverse vs. forward autodiff passes are necessary for your mixed-mode Jacobian, but we're still unsure whether our heuristics work. Would you mind trying out the code from the following PR on your matrices, to see if it makes a difference?

using StableRNGs, SparseArrays
using SparseMatrixColorings  # branch https://github.com/gdalle/SparseMatrixColorings.jl/tree/am/postprocessing_v2

J = sprand(StableRNG(0), Bool, 1000, 2000, 0.001)  # replace with your Jacobian sparsity pattern

problem = ColoringProblem(; structure = :nonsymmetric, partition = :bidirectional)

order = RandomOrder(StableRNG(0), 0)

algo_r = GreedyColoringAlgorithm(
    order; postprocessing = true, postprocessing_minimizes = :row_colors
)
algo_c = GreedyColoringAlgorithm(
    order; postprocessing = true, postprocessing_minimizes = :column_colors
)
algo_a = GreedyColoringAlgorithm(
    order; postprocessing = true, postprocessing_minimizes = :all_colors
)

result_r = coloring(J, problem, algo_r);
result_c = coloring(J, problem, algo_c);
result_a = coloring(J, problem, algo_a);

count_colors(result) = length(row_groups(result)), length(column_groups(result))

count_colors(result_r)
count_colors(result_a)
count_colors(result_c)

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.

Investigate performance for combined models

3 participants