Skip to content

Make min/max on Dual consistent with isless - #869

Merged
devmotion merged 5 commits into
masterfrom
dmw/minmax-isless
Nov 13, 2026
Merged

devmotion merged 5 commits into
masterfrom
dmw/minmax-isless

Conversation

@devmotion

Copy link
Copy Markdown
Member

Fixes #868.

min/max on Dual were generated from DiffRules. Those rules choose the partials by value only, so on a tie in value the result depended on the argument order rather than on isless, which breaks ties on the partials since #695:

julia> x = ForwardDiff.Dual(48.0, 1.0);

julia> min(x, 48.0), min(48.0, x)  # before
(Dual{Nothing}(48.0,1.0), Dual{Nothing}(48.0,0.0))

julia> min(x, 48.0), min(48.0, x)  # after
(Dual{Nothing}(48.0,0.0), Dual{Nothing}(48.0,0.0))

No DiffRules rule can express this tie-breaking, since a rule only sees the values. So this PR stops generating min/max from DiffRules and defines them in terms of isless. Like min/max on floats, they propagate NaN values. Mixed Dual/Real arguments are promoted by Base's min(::Real, ::Real).

🤖 Generated with Claude Code

@codecov

codecov Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.06%. Comparing base (7c157f9) to head (1aeb58b).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #869      +/-   ##
==========================================
- Coverage   91.14%   91.06%   -0.08%     
==========================================
  Files          11       11              
  Lines        1084     1086       +2     
==========================================
+ Hits          988      989       +1     
- Misses         96       97       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@pkofod

pkofod commented Nov 2, 2026

Copy link
Copy Markdown

Looks right to me and behaves like #695. A case worth a test: under nesting, master's min can return
neither argument. With D(f) = t -> ForwardDiff.derivative(f, t) and
f(t) = min(3t - 2, t^2) at t = 1, master gives D(f)(1.0) == 3 (left branch)
and D(D(f))(1.0) == 2 (right branch). With this PR both come from t^2 (2 and 2),
in either argument order.

It may also deserve a line in the release notes, since derivatives at exact ties
change (d/dt max(t, 0) at 0 goes from 0 to 1), and tie gradients now depend on the
chunk size, as comparison-based code already does since #695.

Comment thread src/dual.jl Resolved
@andreasnoack

andreasnoack commented Nov 13, 2026 •

Copy link
Copy Markdown
Member

Not clear to me why GitHub isn't showing the "Update PR" button now that #875 has been merged.

@pkofod

pkofod commented Nov 13, 2026 •

Copy link
Copy Markdown

Not clear to me why GitHub isn't showing the "Update PR" button now that #875 has been merged.

you mean "Update branch"? Is "Always suggest updating pull request branches" on for this repo? Settings, General, Pull Requests.

@andreasnoack

Copy link
Copy Markdown
Member

Yes. The setting is enabled.

Screenshot 2026-11-13 at 09 04 43

@devmotion

Copy link
Copy Markdown
Member Author
image

@andreasnoack

Copy link
Copy Markdown
Member

I was trying out the new "Overview page" view, and apparently they haven't implemented the "Update PR" button yet.

devmotion and others added 5 commits November 13, 2026 09:52
`min`/`max` were generated from DiffRules, which choose the partials by
value only. On ties in value the result depended on the argument order
rather than `isless`, which breaks ties on the partials since #695.

Fixes #868.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This reverts commit 6c93fd3.

Without these methods, `min`/`max` on `Dual` fall back to Base's
`min(x::T, y::T) where {T<:Real}`, which uses `<` instead of `isless`
and returns the first argument whenever a NaN is involved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@devmotion

Copy link
Copy Markdown
Member Author

FYI the segfault on Julia 1.14 on windows is tracked upstream: JuliaLang/julia#64013

@devmotion
devmotion merged commit 94425f7 into master Nov 13, 2026
19 of 21 checks passed
@devmotion
devmotion deleted the dmw/minmax-isless branch November 13, 2026 11:47
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.

min does not return the minimum with respect to isless when values tie

3 participants