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

RFC: More methods for Irrational #32117

Closed
wants to merge 3 commits into from

Conversation

perrutquist
Copy link
Contributor

@perrutquist perrutquist commented May 23, 2019

The purpose of this PR is to make Irrational work with many more functions, including those mentioned in #31949 #26701 and #22878.

The main way that this is accomplished is by defining

zero(::AbstractIrrational) = false
zero(::Type{<:AbstractIrrational}) = false
one(::Type{<:AbstractIrrational}) = true

The documentation says that these functions should return the additive/multiplicative identities for the type T - not of the type T.

In order to make false and true into additive/multiplicative identities, this PR also re-defines Irrational-Bool addition, subtraction and multiplication.
This is technically breaking if someone is currently using true * pi or pi + false as a way of forcing conversion to Float64. (The alternatives 1 * pi and 0 + pi continue to work as before.)
The new +, - and * methods are not type-stable, which the old ones were. On the other hand, Julia is now much better at type inference than it was when the original methods were written.

With this, a whole range of functions start to work, as the only reason they were throwing before was a call to one or zero. To give one example, sec(x::Number) is defined as one(x)/cos(x) which now works.

This PR also extends the list of two-argument functions that convert to Float64 when given two identical Irrational arguments. The intent is to make all "math-like" functions in Base work with Irrational. (This should mitigate the effect of true*pi now returning pi rather than a Float64.)

Finally, some minor fixes: widemul of two Irrationals now returns a BigFloat same as widemul of Float64s, and sin(pi) and tan(pi) now return exactly 0.0.

@perrutquist
Copy link
Contributor Author

perrutquist commented May 23, 2019

Another effect that should be mentioned is that complex(pi) (previously an error) will now return a Complex{Float64} because of how (pi, zero(pi)) is promoted.

@perrutquist
Copy link
Contributor Author

...and for that reason it seemed to make sense to also make Complex(pi, pi) a Complex{Float64} rather than Complex{Irrational{:π}}. It would be a very odd special case.

@ararslan
Copy link
Member

The documentation says that these functions should return the additive/multiplicative identities for the type T - not of the type T.

I think that's taking the documentation a bit overly literal in this case. I don't know of any other cases where zero/one does not return value of the input type. Indeed, it's assumed in a number of places, especially when determining the element type for a container.

@stevengj
Copy link
Member

I don't know of any other cases where zero/one does not return value of the input type.

oneunit(T) usually returns something of type T, but one(T) does not for dimensionful types.

@mohamed82008
Copy link
Contributor

I am running into issues with DiffRules and Distributions that this PR can fix. Are people here for or against the proposed changes? If only a rebase is needed, I can go ahead and do that.

@mohamed82008
Copy link
Contributor

I only care about one and zero for irrationals.

Rational(x::AbstractIrrational) = rationalize(x)
rationalize(x::Irrational{T}) where T = throw(ArgumentError("Type must be specified. Use rationalize(Int, $T) to obtain a Rational{Int} approximation to the irrational constant $T."))
Math.mod2pi(x::AbstractIrrational) = Float64(mod2pi(big(x)))
Math.rem2pi(x::AbstractIrrational, m) = Float64(rem2pi(big(x), m))
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't use BigFloat arithmetic at runtime.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. I will turn these into generated functions.

@stevengj
Copy link
Member

I'm okay with one and zero. The other functions are mostly fine but might require some more thought. My suggestion would be to make a separate PR with just one and zero if that's all you need for now, since that can be merged quickly.

@mohamed82008
Copy link
Contributor

Sounds good, will do.

@mohamed82008
Copy link
Contributor

Done.

Define `zero(Irrational)` and `one(Irrational)` to return `false`
and `true` respectively. (This is breaking if someone is currently
using `true * pi` or `pi + false` as a way of forcing conversion to
`Float64`.)

Make `false` act as the additive identity for `Irrational` by defining
a method for `+`. (Not type stable)

Make `true` act as the multiplicative identity for `Irrational` by
defining a method for `*` (Not type stable)

Make `widemul` of two `Irrational`s return a `BigFloat`

Make `sin(pi)` and `tan(pi)` return exactly 0.0

Define a number of methods on `Irrational` to default to conversion to
Float64.
@perrutquist
Copy link
Contributor Author

perrutquist commented Feb 16, 2020

I'm very sorry. I tried to rebase this on the latest master, and I obviously messed up. Now it looks like I've modified 1658 files. Git and I are not always friends. Will try to fix this. Hopefully fixed now.

@StefanKarpinski StefanKarpinski added the triage This should be discussed on a triage call label Feb 17, 2020
Comment on lines +178 to +179
@generated Math.mod2pi(x::AbstractIrrational) = Float64(mod2pi(big(x())))
@generated Math.rem2pi(x::AbstractIrrational, r::RoundingMode) = Float64(rem2pi(big(x()), r()))
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

Suggested change
@generated Math.mod2pi(x::AbstractIrrational) = Float64(mod2pi(big(x())))
@generated Math.rem2pi(x::AbstractIrrational, r::RoundingMode) = Float64(rem2pi(big(x()), r()))
Math.mod2pi(x::AbstractIrrational) = Float64(mod2pi(big(x)))
Math.rem2pi(x::AbstractIrrational, r::RoundingMode) = Float64(rem2pi(big(x), r))

@eval $op(x::AbstractIrrational, y::AbstractIrrational) = $op(Float64(x),Float64(y))
end
*(x::Bool, y::AbstractIrrational) = ifelse(x, Float64(y), 0.0)
*(x::Bool, y::AbstractIrrational) = x ? y : zero(y)
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

Type unstable --- I'm not sure this is worth it.

@@ -45,6 +51,7 @@ promote_rule(::Type{S}, ::Type{T}) where {S<:AbstractIrrational,T<:Number} = pro
AbstractFloat(x::AbstractIrrational) = Float64(x)::Float64
Float16(x::AbstractIrrational) = Float16(Float32(x)::Float32)
Complex{T}(x::AbstractIrrational) where {T<:Real} = Complex{T}(T(x))
Complex(x::AbstractIrrational, y::AbstractIrrational) = Complex(Float64(x), Float64(y))
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

Seems unnecessary?

julia> Complex(π, ℯ)
3.141592653589793 + 2.718281828459045im
Suggested change
Complex(x::AbstractIrrational, y::AbstractIrrational) = Complex(Float64(x), Float64(y))


widemul(x::AbstractIrrational, y::AbstractIrrational) = big(x)*big(y)

(::Colon)(x::AbstractIrrational, y::AbstractIrrational) = Float64(x):Float64(y)
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

Seems to already work without this?

Suggested change
(::Colon)(x::AbstractIrrational, y::AbstractIrrational) = Float64(x):Float64(y)
julia> ℯ:π
2.718281828459045:1.0:2.718281828459045

@vtjnash
Copy link
Sponsor Member

vtjnash commented Apr 8, 2021

We discussed this on triage, and felt that it is better to be explicit about the transforms for irrational numbers into the appropriate computational types. Thanks for the PR however. I know it is sad to see one not get merged, but it happens to all of us.

@vtjnash vtjnash closed this Apr 8, 2021
@oscardssmith oscardssmith removed the triage This should be discussed on a triage call label May 27, 2021
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.

8 participants