Skip to content

Fixes return-types for multiple rules #55

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

Merged
merged 50 commits into from
Jan 28, 2022

Conversation

torfjelde
Copy link
Contributor

Motivation

Currently, almost all of the rules using π currently has a return-type that differs from the input, e.g. SpecialFunctions.erfcx uses √π and so SpecialFunctions.erfcx(1f0) results in Float64 rather than Float32.

Aim

This PR fixes these issue by replacing all usages of π with oftype($x, π). This is a rather naive and quick-fix.

Up-for-discussion

  • It might be preferable to instead exploit Julia's constant propagation, e.g. do √π is replaced by oftype($x, √π) instead of √oftype($x, π), but I don't know enough about LLVM to know if this makes a difference such simple computations, hence I just went with naive conversion of π directly.
  • It would be ideal to have tests for this, but it's unclear to me if all unary rules are expected to have the same return-type as the input-type or not. It seems to me like this ought to be the case, but didn't add any yet as I'm not 100% certain.

@torfjelde torfjelde changed the title Fix issue for return-types for multiple rules Fixes return-types for multiple rules Jan 17, 2021
@andreasnoack
Copy link
Member

Thanks for fixing these. As a matter of style, I prefer to avoid explicit uses of typeof when possible. Sometimes it's the only way to get the type right but often you can simply adjust the order of evaluation in a way such that the promotion is correct. E.g. (π / 180) * cosd($x) could be changed to cosd($x) / 180 * π.

src/rules.jl Outdated
@define_diffrule SpecialFunctions.erfcx(x) =
:( (2 * $x * SpecialFunctions.erfcx($x)) - (2 / sqrt(π)) )
:( (2 * $x * SpecialFunctions.erfcx($x)) - (2 / sqrt(oftype($x, π))) )
Copy link
Member

@andreasnoack andreasnoack Jan 18, 2021

Choose a reason for hiding this comment

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

I think this could fail for integer inputs (unless there is some promotion before the rules are hit). Generally, it's safer to use "output" variables for the promotion so it would be something like

y = 2 * $x * SpecialFunctions.erfcx($x))
return y - oftype(y, 2)/π

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry for the delay here; been a busy couple of months and so this was pushed quite far down my priority list.

But yeah, this is a good point. Is the best solution then to go through all of the rules and manually make these changes, or is there a better way?

src/rules.jl Outdated
@define_diffrule Base.cotd(x) = :( -(π / 180) * (1 + cotd($x)^2) )
@define_diffrule Base.sinpi(x) = :( π * cospi($x) )
@define_diffrule Base.cospi(x) = :( -π * sinpi($x) )
@define_diffrule Base.sind(x) = :( (oftype($x, π) / 180) * cosd($x) )
Copy link
Member

Choose a reason for hiding this comment

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

Should we be concerned what happens if x is a substantial expression? This might evaluate it twice, which could be avoided by something like

@gensym z
@define_diffrule Base.sind(x)  = :(  $z=$x;  (oftype($z, π) / 180) * cosd($z)   )

I see that a few existing rules do already violate this, e.g. with exp(-$x * $x).

It's possible that @define_diffrule could be extended to take care of this. It's also possible that every downstream use calls CSE which will clean this up anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wasn't aware that x could potentially be an non-evaluated expression; if so, that's for sure an issue, but maybe a separate one from this PR?

@torfjelde
Copy link
Contributor Author

Made some updates. There are a couple of things that:

  1. Currently not testing binary methods properly because it's a bit unclear to how the types of the input arguments is decide in these tests. Seems like you want to sometimes have the first argument be a integer?
  2. Added a @testset wrapper because I was a bit confused as to what threw which error. IMO the output is more understandable if something fails, but this can be removed if you want.

@torfjelde
Copy link
Contributor Author

Actually, would it be better if I just added the constants needed, e.g. copy-paste those we need from https://github.com/JuliaStats/StatsFuns.jl/blob/master/src/constants.jl ?

That would solve almost all the promotion-bugs:)

@torfjelde
Copy link
Contributor Author

An update here: am currently trying to convince people that it's a good idea to make a MathConstants.jl package so that we can avoid explicit conversions here, in SpecialFunctions.jl, etc.

@torfjelde
Copy link
Contributor Author

torfjelde commented Jun 20, 2021

I added the necessary irrationals to simplify the type-promotion. If you're happy to depend on IrrationalConstants.jl, we can make use of irrationals defined there instead:)

@torfjelde
Copy link
Contributor Author

Now that packages have started using IrrationalConstants.jl, maybe this is ready for a proper consideration? @andreasnoack @mcabbott

@codecov-commenter
Copy link

codecov-commenter commented Aug 11, 2021

Codecov Report

Merging #55 (5d7b635) into master (7b1c31e) will not change coverage.
The diff coverage is 100.00%.

Impacted file tree graph

@@           Coverage Diff           @@
##           master      #55   +/-   ##
=======================================
  Coverage   96.96%   96.96%           
=======================================
  Files           3        3           
  Lines         165      165           
=======================================
  Hits          160      160           
  Misses          5        5           
Impacted Files Coverage Δ
src/DiffRules.jl 100.00% <ø> (ø)
src/rules.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 7b1c31e...5d7b635. Read the comment docs.

Copy link
Member

@devmotion devmotion left a comment

Choose a reason for hiding this comment

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

Looks good, only some minor questions/suggestions left 🙂

torfjelde and others added 4 commits January 28, 2022 16:02
Co-authored-by: David Widmann <devmotion@users.noreply.github.com>
Co-authored-by: David Widmann <devmotion@users.noreply.github.com>
@torfjelde
Copy link
Contributor Author

Btw, seems like the integration tests for Symbolics.jl are broken.

@devmotion
Copy link
Member

Btw, seems like the integration tests for Symbolics.jl are broken.

Yeah, seems it's a Symbolics issue, the same errors show up on their master branch: https://github.com/JuliaSymbolics/Symbolics.jl/runs/4975659103?check_suite_focus=true

Copy link
Member

@devmotion devmotion left a comment

Choose a reason for hiding this comment

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

LGTM, thanks @torfjelde!

@devmotion devmotion merged commit 773be5a into JuliaDiff:master Jan 28, 2022
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.

5 participants