Skip to content

Stagnation checks ignore custom norms (problematic for MPI) #1310

Description

@Iddingsite

Describe the bug 🐞

AbsNormSafeTerminationMode and AbsNormSafeBestTerminationMode accept an internalnorm, but their
stagnation check bypasses it:

du_norm = L2_NORM(cache.u_diff_cache)

This means that a user-provided norm controls the residual-based convergence checks but not the step-
stagnation checks.

This is problematic for distributed-memory solvers. An MPI-aware norm needs a global reduction. The
hard-coded L2_NORM instead evaluates a rank-local norm unless the distributed vector itself overloads
norm. Different ranks may then make different termination decisions and deadlock on the next collective
residual or linear-solver operation.

Expected behavior

A termination mode configured with internalnorm should use that norm consistently for all of its norm
evaluations, including the step-stagnation test.

In particular, I expect this line to use the mode’s configured norm:

  du_norm = Utils.apply_norm(mode.internalnorm, cache.u_diff_cache)

rather than the hard-coded L2_NORM.

Minimal Reproducible Example 👇

using Test
  using NonlinearSolveBase
  using SciMLBase: NonlinearProblem, init

  calls = Ref(0)

  function counting_norm(x)
      calls[] += 1
      return 1.0
  end

  prob = NonlinearProblem((u, p) -> u, [1.0])

  mode = AbsNormSafeBestTerminationMode(
      counting_norm;
      max_stalled_steps = 1,
  )

  cache = init(
      prob,
      mode,
      [1.0],
      [1.0];
      abstol = 0.0,
      reltol = 0.0,
  )

  # Two non-converged termination checks.
  cache([1.0], [2.0], [1.0])
  cache([1.0], [3.0], [2.0])

  # Expected calls:
  #   1 initial residual norm
  #   2 residual norms in the two checks
  #   2 step norms in the two stagnation checks
  @test calls[] == 5

Error & Stacktrace ⚠️

Test Failed at ...:...

    Expression: calls[] == 5
     Evaluated: 3 == 5

Environment (please complete the following information):

  • Output of using Pkg; Pkg.status()
  [8913a72c] NonlinearSolve v4.32.0
  [be0214bd] NonlinearSolveBase v2.52.2
  [0bca4576] SciMLBase v3.55.0
  • Output of using Pkg; Pkg.status(; mode = PKGMODE_MANIFEST)
  [47edcb42] ADTypes v1.24.0
  [14f7f29c] AMD v0.5.4
  [7d9f7c33] Accessors v0.1.45
  [79e6a3ab] Adapt v4.7.1
  [4fba245c] ArrayInterface v7.30.2
  [70df07ce] BracketingNonlinearSolve v1.12.7
  [38540f10] CommonSolve v0.2.14
  [bbf7d656] CommonSubexpressions v0.3.1
  [34da2185] Compat v4.18.1
  [a33af91c] CompositionsBase v0.1.2
  [2569d6c7] ConcreteStructs v0.2.8
  [187b0558] ConstructionBase v1.6.0
  [a8cc5b0e] Crayons v4.2.0
  [9a962f9c] DataAPI v1.16.0
  [e2d170a0] DataValueInterfaces v1.0.0
  [8bb1440f] DelimitedFiles v1.9.1
  [163ba53b] DiffResults v1.1.0
  [b552c78f] DiffRules v1.16.0
  [a0c0ee7d] DifferentiationInterface v0.7.21
  [ffbed154] DocStringExtensions v0.9.5
  [4e289a0a] EnumX v1.0.7
  [f151be2c] EnzymeCore v0.8.21
  [e2ba6199] ExprTools v0.1.11
  [9aa1b823] FastClosures v0.3.2
  [64ca27bc] FindFirstFunctions v3.3.0
  [6a86dc24] FiniteDiff v2.33.0
  [f6369f11] ForwardDiff v1.4.6
  [069b7b12] FunctionWrappers v1.1.3
  [77dc65aa] FunctionWrappersWrappers v1.13.0
  [46192b85] GPUArraysCore v0.2.0
  [3587e190] InverseFunctions v0.1.17
  [92d709cd] IrrationalConstants v0.2.6
  [82899510] IteratorInterfaceExtensions v1.0.0
  [692b3bcd] JLLWrappers v1.8.0
  [ba0b0d4f] Krylov v0.10.10
  [2faa5264] LHLFactorization v2.2.2
  [b964fa9f] LaTeXStrings v1.4.1
  [87fe0de2] LineSearch v0.1.18
  [7ed4a6bd] LinearSolve v5.17.7
  [2ab3a3ac] LogExpFunctions v1.0.1
  [e6f89c97] LoggingExtras v1.2.0
  [1914dd2f] MacroTools v0.5.16
  [bb5d69b7] MaybeInplace v0.1.8
  [77ba4419] NaNMath v1.1.4
  [8913a72c] NonlinearSolve v4.32.0
  [be0214bd] NonlinearSolveBase v2.52.2
  [5959db7a] NonlinearSolveFirstOrder v2.9.1
  [9a2c21bd] NonlinearSolveQuasiNewton v1.15.3
  [26075421] NonlinearSolveSpectralMethods v1.8.3
  [bac558e1] OrderedCollections v2.0.1
  [d236fae5] PreallocationTools v1.7.1
  [aea7be01] PrecompileTools v1.3.4
  [21216c6a] Preferences v1.6.0
  [08abe8d2] PrettyTables v3.4.8
  [0c0d3e7f] PureKLU v1.6.0
  [3cdcf5f2] RecipesBase v1.3.4
  [731186ca] RecursiveArrayTools v4.5.1
  [189a3867] Reexport v1.2.2
  [9fe22ead] RespecializeParams v1.3.0
  [7e49a35a] RuntimeGeneratedFunctions v0.5.26
  [0bca4576] SciMLBase v3.55.0
  [19f34311] SciMLJacobianOperators v0.1.19
  [a6db7da4] SciMLLogging v2.1.0
  [c0aeaf25] SciMLOperators v1.30.1
  [431bcebd] SciMLPublic v1.3.0
  [53ae85a6] SciMLStructures v1.10.5
  [efcf1570] Setfield v1.1.2
  [727e6d20] SimpleNonlinearSolve v2.14.5
  [ed01d8cd] Sobol v1.5.0
  [a57abbd0] SparseColumnPivotedQR v2.1.8
  [276daf66] SpecialFunctions v2.9.0
  [1e83bf80] StaticArraysCore v1.4.4
  [10745b16] Statistics v1.11.5
⌅ [892a3eda] StringManipulation v0.5.0
  [2efcf032] SymbolicIndexingInterface v0.3.55
  [3783bdb8] TableTraits v1.0.1
  [bd369af6] Tables v1.14.0
  [a759f4b9] TimerOutputs v1.2.2
  [1d5cc7b8] IntelOpenMP_jll v2025.2.0+0
  [856f044c] MKL_jll v2025.2.0+0
  [efe28fd5] OpenSpecFun_jll v0.5.6+0
  [1317d2d5] oneTBB_jll v2022.3.0+0
  [0dad84c5] ArgTools v1.1.2
  [56f22d72] Artifacts v1.11.0
  [2a0f44e3] Base64 v1.11.0
  [ade2ca70] Dates v1.11.0
  [8ba89e20] Distributed v1.11.0
  [f43a241f] Downloads v1.7.0
  [7b1f6079] FileWatching v1.11.0
  [9fa8497b] Future v1.11.0
  [b77e0a4c] InteractiveUtils v1.11.0
  [ac6e5ff7] JuliaSyntaxHighlighting v1.12.0
  [4af54fe1] LazyArtifacts v1.11.0
  [b27032c2] LibCURL v1.0.0
  [76f85450] LibGit2 v1.11.0
  [8f399da3] Libdl v1.11.0
  [37e2e46d] LinearAlgebra v1.13.0
  [56ddb016] Logging v1.11.0
  [d6f4376e] Markdown v1.11.0
  [a63ad114] Mmap v1.11.0
  [ca575930] NetworkOptions v1.3.0
  [44cfe95a] Pkg v1.13.0
  [de0858da] Printf v1.11.0
  [3fa0cd96] REPL v1.11.0
  [9a3f8284] Random v1.11.0
  [ea8e919c] SHA v1.0.0
  [9e88b42a] Serialization v1.11.0
  [6462fe0b] Sockets v1.11.0
  [2f01184e] SparseArrays v1.13.0
  [f489334b] StyledStrings v1.11.0
  [fa267f1f] TOML v1.0.3
  [a4e569a6] Tar v1.10.0
  [cf7118a7] UUIDs v1.11.0
  [4ec0a83e] Unicode v1.11.0
  [e66e0078] CompilerSupportLibraries_jll v1.5.5+2
  [deac9b47] LibCURL_jll v8.18.0+1
  [e37daf67] LibGit2_jll v1.9.1+0
  [29816b5a] LibSSH2_jll v1.11.103+0
  [14a3606d] MozillaCACerts_jll v2026.8.13
  [4536629a] OpenBLAS_jll v0.3.30+0
  [05823500] OpenLibm_jll v0.8.7+0
  [458c3c95] OpenSSL_jll v3.5.6+0
  [efcefdf7] PCRE2_jll v10.46.0+0
  [bea87d4a] SuiteSparse_jll v7.10.1+0
  [83775a58] Zlib_jll v1.3.1+2
  [3161d3a3] Zstd_jll v1.5.7+1
  [8e850b90] libblastrampoline_jll v5.15.0+0
  [8e850ede] nghttp2_jll v1.67.1+0
  [3f19e933] p7zip_jll v17.8.2+0
Info Packages marked with ⌅ have new versions available but compatibility constraints restrict them from upgrading. To see why use `status --outdated -m`
  • Output of versioninfo()
Julia Version 1.13.0
Commit d1c37793dd2 (2026-09-09 19:00 UTC)
Build Info:
  Official https://julialang.org release
Platform Info:
  OS: macOS (arm64-apple-darwin25.6.0)
  CPU: 16 × Apple M4 Max
  WORD_SIZE: 64
  LLVM: libLLVM-20.1.8 (ORCJIT, apple-m4)
  GC: Built with stock GC
Threads: 1 default, 1 interactive, 1 GC (on 16 virtual cores)

Additional context

There is a related API question: solve(...; internalnorm=...) does not propagate that norm into the
automatically constructed default termination condition. The default root-finding termination mode uses
maximum(abs, residual), i.e. a local L∞ norm.

I am less certain whether that part is intended design or a bug. The issue above is independent: even
when a user explicitly constructs a termination mode with a custom norm, its stagnation path
currently ignores that norm.

Activity

  1. Iddingsite commented on Sep 22, 2026

    @Iddingsite
    Author

    Current workaround is something like that:

      termination = AbsNormSafeBestTerminationMode(
          mpi_l2_norm;
          max_stalled_steps = nothing,
      )
    
      sol = solve(
          prob,
          alg;
          internalnorm = mpi_l2_norm,
          termination_condition = termination,
      )
    

    Disabling max_stalled_steps is necessary because its implementation bypasses
    the configured norm.

  2. ChrisRackauckas-Claude commented on Oct 9, 2026

    @ChrisRackauckas-Claude
    Member

    Thanks for the workaround. #1333 makes the safe termination modes' stagnation check use mode.internalnorm, so a custom norm (e.g. an MPI-reduced one) is respected without the wrapper. It is a draft awaiting maintainer review. Until it is merged, your workaround remains the way to go.

    🤖 Status update by an AI agent (Claude Code, model claude-opus-5-5[1m]) on behalf of Chris Rackauckas — https://claude.ai/code/session_01AXwyJDRaMKgnYMG9M1Ut49

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions