Skip to content
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

Limit Dual to Real primals, replace all Number with Real #95

Merged
merged 7 commits into from
May 23, 2024
Merged

Conversation

gdalle
Copy link
Collaborator

@gdalle gdalle commented May 23, 2024

Fix #91 for good

@codecov-commenter
Copy link

codecov-commenter commented May 23, 2024

Codecov Report

Attention: Patch coverage is 74.19355% with 8 lines in your changes are missing coverage. Please review.

Project coverage is 79.62%. Comparing base (f1bf915) to head (46c9879).
Report is 1 commits behind head on main.

Files Patch % Lines
src/overload_dual.jl 0.00% 4 Missing ⚠️
src/conversion.jl 87.50% 1 Missing ⚠️
src/overload_connectivity.jl 50.00% 1 Missing ⚠️
src/pattern.jl 66.66% 1 Missing ⚠️
src/tracers.jl 85.71% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main      #95   +/-   ##
=======================================
  Coverage   79.62%   79.62%           
=======================================
  Files          17       17           
  Lines         702      702           
=======================================
  Hits          559      559           
  Misses        143      143           

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

@gdalle gdalle requested a review from adrhill May 23, 2024 07:12
@gdalle gdalle changed the title Limit Dual to Real primals Limit Dual to Real primals, replace all Number with Real May 23, 2024
Copy link
Owner

@adrhill adrhill left a comment

Choose a reason for hiding this comment

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

I'm not sure we actually need the other changes besides the one I commented on. Would be interested in discussing this.

struct Dual{P<:Number,T<:Union{ConnectivityTracer,GradientTracer,HessianTracer}} <:
struct Dual{P<:Real,T<:Union{ConnectivityTracer,GradientTracer,HessianTracer}} <:
Copy link
Owner

Choose a reason for hiding this comment

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

This is all we need.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I thought so too. But then I ran the tests and found that leaving the ::Number dispatches led to some method ambiguities, typically between

<=(::Real, ::Real)
<=(::SCT.Dual, ::Number)

Copy link
Owner

Choose a reason for hiding this comment

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

I would even argue that it would be more informative to have MethodErrors due to type restrictions occur here rather than in Base.promote_rule.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

See the discussion below, we shouldn't promise complex number support anyway. Tracers are real numbers, and if people want to go beyond they can do Complex{Tracer}

@adrhill
Copy link
Owner

adrhill commented May 23, 2024

The original motivation for this PR is that #92 introduced a weird edge case:

Some weirdness resulting from [#92]: our local tracer type Dual can contain a Complex primal value but is Real.
Maybe the most sane fix is to constrain the primal to Real as well.

This PR however removes a lot of functionality from ConnectivityTracer and other global tracers without any obvious gain. For example, the following doesn't work anymore:

connectivity_pattern(x -> x^(2im), 1)

@gdalle
Copy link
Collaborator Author

gdalle commented May 23, 2024

This PR however removes a lot of functionality from ConnectivityTracer and other global tracers without any obvious gain.

Point taken for ConnectivityTracer. As for the other tracers, I must disagree.
We don't know how to autodiff with complex numbers, or it's very complicated (see this Discourse thread). So claiming that we can properly trace first and second derivatives for complex numbers is a bit of an oversell, which this PR remedies.

If we wanted to promise that a complex derivative is zero, derivative would that be anyway? The holomorphic derivative $\mathbb{C} \to \mathbb{C}$, or the Jacobian of the function taken as a mapping $\mathbb{R}^2 \to \mathbb{R}^2$?

@gdalle
Copy link
Collaborator Author

gdalle commented May 23, 2024

However, I note that ForwardDiff doesn't constrain the primal and partial values inside their Dual struct, only the Dual itself must subtype Real:

https://github.com/JuliaDiff/ForwardDiff.jl/blob/1907196ba50e3eb337860301ece3ec37d0582257/src/dual.jl#L14-L21

Still, based on what I understand from previous discussion, the right way to proceed is to make a Complex{Dual}, not a Dual{Complex}.

Related:

@gdalle
Copy link
Collaborator Author

gdalle commented May 23, 2024

I think however that we should be able to support functions that do $\mathbb{R} \to \mathbb{C} \to \mathbb{R}$ out of the box, so the last commit reintroduces the former complex-valued tests by projecting them to the real axis

@adrhill
Copy link
Owner

adrhill commented May 23, 2024

the right way to proceed is to make a Complex{Dual}, not a Dual{Complex}.

This I like. I guess this requires adding some new Base.promote_rule?

@gdalle
Copy link
Collaborator Author

gdalle commented May 23, 2024

This I like. I guess this requires adding some new Base.promote_rule?

The test I added back failed and that's probably the reason, will investigate

src/tracers.jl Show resolved Hide resolved
src/tracers.jl Show resolved Hide resolved
src/pattern.jl Show resolved Hide resolved
src/pattern.jl Show resolved Hide resolved
src/pattern.jl Show resolved Hide resolved
src/overload_hessian.jl Show resolved Hide resolved
src/overload_hessian.jl Show resolved Hide resolved
src/overload_gradient.jl Show resolved Hide resolved
src/conversion.jl Show resolved Hide resolved
@gdalle gdalle mentioned this pull request May 23, 2024
@gdalle
Copy link
Collaborator Author

gdalle commented May 23, 2024

Here's an argument for only supporting real inputs (nevermind the output type):

@adrhill
Copy link
Owner

adrhill commented May 23, 2024

Here's an argument for only supporting real inputs (nevermind the output type):

I agree with all of this. My suggestion was that a Complex{Float64} input could be converted to

  • Complex{T} for non-dual tracers, T<:Union{ConnectivityTracer, GradientTracer, HessianTracer}.
  • Complex{Dual{Float64, T}} for dual tracers.

@adrhill
Copy link
Owner

adrhill commented May 23, 2024

This would require additional trace_input and create_tracer methods for Complex numbers.

@adrhill
Copy link
Owner

adrhill commented May 23, 2024

But all of this is not something that has to be done in this PR (or has to be done in general until the need for it arises).

@adrhill adrhill merged commit c9040ba into main May 23, 2024
4 checks passed
@adrhill adrhill deleted the gd/dual_real branch May 23, 2024 15: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.

Make AbstractTracer a subtype of Real, not Number
3 participants