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

Add realdot and imagdot from ChainRules #474

Closed
wants to merge 15 commits into from
Closed

Add realdot and imagdot from ChainRules #474

wants to merge 15 commits into from

Conversation

devmotion
Copy link
Member

@devmotion devmotion commented Oct 1, 2021

This PR adds realconjtimesrealdot and imagconjtimesimagdot, currently defined in ChainRules as _realconjtimes and _imagconjtimes. These optimizations of real(conj(x) * y), or more generally real(dot(x, y)), and imag(conj(x) * y)/imag(dot(x, y)) are useful more generally and could be used e.g. in JuliaMath/SpecialFunctions.jl#350.

I extracted src/rulesets/Base/utils.jl (and its history) from ChainRules, renamed the functions, and added some tests and documentation.

sethaxen and others added 7 commits June 30, 2020 08:00
* Fix indentation

* Test \ on complex inputs

* Test ^ on complex inputs

* Test identity on complex inputs

* Test muladd on complex inputs

* Test binary functions on complex inputs

* Test functions on complex inputs

* Release type constraint on exp

* Add _realconjtimes

* Use _realconjtimes in abs/abs2 rules

* Add complex rule for hypot

* Add generic rule for adjoint

* Add generic rule for real

* Add generic rule for imag

* Add complex rule for hypot

* Add rules/tests for Complex

* Test frule for identity

* Add missing angle test

* Make inline just in case

* Unify abs rules

* Introduce _imagconjtimes utility function

* Unify angle rules

* Unify sign rules

* Multiply by correct variable

* Fix argument order

* Bump ChainRulesTestUtils version number

* Restrict to Complex

* Use muladd

* Update src/rulesets/Base/fastmath_able.jl

Co-authored-by: willtebbutt <[email protected]>

Co-authored-by: willtebbutt <[email protected]>
* rename DoesNotExist

* rename Composite

* bump version and compat

* rename Zero

* remove typos

* reexport deprecated types manually
Pages = ["rule_definition_tools.jl"]
Pages = [
"rule_definition_tools.jl",
"utils.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.

Maybe they should be moved to src/rule_definition_tools.jl?

Copy link
Member

Choose a reason for hiding this comment

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

That file is full of metaprogramming stuff.
It doesn't need this.

Files called utils.jl are always a mistake.
I think something like complex_math.jl as it currently stands,
or additional_mathematical_functions.jl if we think we will need more of them later.

@codecov-commenter
Copy link

codecov-commenter commented Oct 1, 2021

Codecov Report

Merging #474 (9c3cab2) into master (d58e420) will increase coverage by 0.11%.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #474      +/-   ##
==========================================
+ Coverage   92.89%   93.00%   +0.11%     
==========================================
  Files          15       16       +1     
  Lines         816      829      +13     
==========================================
+ Hits          758      771      +13     
  Misses         58       58              
Impacted Files Coverage Δ
src/ChainRulesCore.jl 100.00% <ø> (ø)
src/utils.jl 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update d58e420...9c3cab2. Read the comment docs.

@sethaxen
Copy link
Member

sethaxen commented Oct 1, 2021

@oxinabox
Copy link
Member

oxinabox commented Oct 1, 2021

I am not sure if this is worth adding to our API surface?
Is it that bad to copy-paste this around to places it is needed?
If we had 3 packages that wanted it, i can see it maybe.
But two, maybe not?

idk I could be convinced that this is a crucial tool needed to define rules for nonholomorphic functions.
but I am not yet convinced

@sethaxen
Copy link
Member

sethaxen commented Oct 1, 2021

Oh I didn't notice this was ChainRulesCore. Then yes, in that case, I do not know if this is worth adding here, in the spirit of keeping this package as-simple-as-possible.

@devmotion
Copy link
Member Author

I'm not fully convinced either, that's why I asked in the SpecialFunctions PR 😄

I assumed it could be useful since it came up in these simple cases in SpecialFunctions and hence I assumed that it would likely pop up in other derivatives of non-holomorphic functions as well. I came across ChainRules._realconjtimes only by accident, and I assume if it is not part of the API or at least documented more clearly people won't realize that it exists and could be copied.

I still think it would be a bit annoying to have to copy _realconjtimes to SpecialFunctions, to me it seems like an optimization that other packages should not have to think about. Hence I also just used real(conj(x) * y) in the SpecialFunctions PR and neglected these possible optimizations.

@devmotion devmotion changed the title Add realconjtimes and imagconjtimes from ChainRules Add realdot and imagdot from ChainRules Oct 1, 2021
Copy link
Member

@oxinabox oxinabox left a comment

Choose a reason for hiding this comment

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

I am not yet done thinking about this

@nickrobinson251
Copy link
Contributor

nickrobinson251 commented Oct 4, 2021

i am not sure how much my opinion on this is worth, as i'm unlikely to be a user of the code added, but fwiw...

I think adding these particular methods to CRC seems fine to me, in a complex_math.jl file, so long as we’re going to have a really high bar for adding “helper” functions like this... because we want CRC to be as minimal as reasonably possible, and also i think maintainence-wise we don’t really want CRC to be a place that keeps being added to over time

maybe rough ideas of what “a high-bar” means here:

  • at least 3 packages need it
  • it is an annoying amount of code (e.g. 7 methods, not 2) or complexity of code (e.g. it took us a while to work out what the correct behaviour was) to copy/maintain in those packages directly
  • it makes a significant difference to use these helpers rather than the “naive” defintions (e.g. `real(dot(x, y))) in getting important edge-cases correct, and/or optimal performance in common use-cases.

(But as i say, perhaps as someone not often actually writing rules, this is setting the bar too high for adding more helpers functions)

@oxinabox
Copy link
Member

oxinabox commented Oct 4, 2021

I agree with this

maybe rough ideas of what “a high-bar” means here:

  • at least 3 packages need it
  • it is an annoying amount of code (e.g. 7 methods, not 2) or complexity of code (e.g. it took us a while to work out what the correct behaviour was) to copy/maintain in those packages directly
  • it makes a significant difference to use these helpers rather than the “naive” defintions (e.g. `real(dot(x, y))) in getting important edge-cases correct, and/or optimal performance in common use-cases.

I think as of right now now it doesn't quite meet the bar.
In particular it is needed in two packages (ChainRules.jl and SpecialFunctions.jl).
I do think that sooner or later we are likely to need it in another.
But I think we can just sit on this PR til that happens.
(and copy the code over to the packages and leave a link back to this PR in a comment)

@oxinabox oxinabox added the pending-clear-need We are not certain we need this. So waiting for evidence to be presented label Oct 4, 2021
@devmotion
Copy link
Member Author

Just came across https://github.com/JuliaStats/StatsBase.jl/blob/0a179531f14d0140356fcf1f1d55bd0240030cf2/src/scalarstats.jl#L259-L261, so I found another use case for realdot but it is not related to ChainRules 😂

Maybe the example indicates though that it could be useful to add them to base?

src/utils.jl Outdated
Comment on lines 13 to 15
@inline realdot(x::Real, y::Complex) = x * real(y)
@inline realdot(x::Complex, y::Real) = real(x) * y
@inline realdot(x::Real, y::Real) = x * y
Copy link
Member

Choose a reason for hiding this comment

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

Should be safe to generalize the cases, and then the last one is redundant since real(::Real) is a no-op

Suggested change
@inline realdot(x::Real, y::Complex) = x * real(y)
@inline realdot(x::Complex, y::Real) = real(x) * y
@inline realdot(x::Real, y::Real) = x * y
@inline realdot(x::Real, y::Number) = x * real(y)
@inline realdot(x::Number, y::Real) = real(x) * y

Copy link
Member

Choose a reason for hiding this comment

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

Won't this introduce an ambiguity, when both are real?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, that's right.

src/utils.jl Outdated Show resolved Hide resolved
@sethaxen
Copy link
Member

I don't think these have been shown to be useful enough to be in base Julia, but it might be worth putting them in their own ultra-lightweight package. Then we can point to that package in our documentation, and package developers can depend on it if they find it useful. Simultaneously resolves the concerns with putting it here while still making the functionality available.

Co-authored-by: Seth Axen <[email protected]>
@devmotion
Copy link
Member Author

The only problem with moving them to a separate package seems to be that imagdot returns a ZeroTangent when the output of dot is guaranteed to be a real number. This would only be possible if the package depends on ChainRulesCore. Of course, this is possible but limits its use to AD computations for CRC. Maybe this optimization/specialization should be dropped and it should just return false?

@sethaxen
Copy link
Member

The only problem with moving them to a separate package seems to be that imagdot returns a ZeroTangent when the output of dot is guaranteed to be a real number. This would only be possible if the package depends on ChainRulesCore.

Yeah, that's right. I'm thinking both imagdot and realdot could go in such a package, but ChainRules would only use realdot. imagdot is less useful anyways. I don't recall if your PR had a use for it, but in ChainRules that construction only shows up when projecting to be tangent to the complex circle (e.g. angle, sign, logabsdet, and eigen) at 1 + 0im, i.e. dot(x, y) - real(dot(x, y)), and I haven't it seen it come up anywhere else, whereas realdot shows up everywhere.

Of course, this is possible but limits its use to AD computations for CRC. Maybe this optimization/specialization should be dropped and it should just return false?

While false is a strong zero, it's not as strong as Zero(), so the complex branches that use imagdot won't be optimized out when the inputs are real.

@devmotion
Copy link
Member Author

I put together a package and added only realdot for now since it seems more generally useful and its implementation does not use ChainRules types (and I'm not sure anymore if a strong zero is really a good idea here, in non-AD scenarios probably one still wants that isnan(imagdot(1im, 1im) * NaN)): JuliaMath/RealDot.jl#2

@oxinabox
Copy link
Member

Cool, we can add that as a dep for ChainRules.jl and SpecialFunctions.jl?

@devmotion
Copy link
Member Author

It's not registered yet (and I'd like @sethaxen to approve the linked PR) but that's the plan 🙂

@devmotion
Copy link
Member Author

I initiated the registration process: JuliaRegistries/General#47024

@devmotion
Copy link
Member Author

Closed by JuliaDiff/ChainRules.jl#542.

@devmotion devmotion closed this Oct 24, 2021
@devmotion devmotion deleted the dw/conjtimes branch October 24, 2021 10:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pending-clear-need We are not certain we need this. So waiting for evidence to be presented
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants