Skip to content

Commit

Permalink
Merge pull request #179 from kalmarek/float-interface-improvements
Browse files Browse the repository at this point in the history
Float interface improvements - and some other things
  • Loading branch information
Joel-Dahne authored Feb 7, 2024
2 parents 492efe4 + 8e5abcd commit 83bbb16
Show file tree
Hide file tree
Showing 19 changed files with 492 additions and 58 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "Arblib"
uuid = "fb37089c-8514-4489-9461-98f9c8763369"
authors = ["Marek Kaluba <[email protected]>", "Sascha Timme <Sascha Timme <[email protected]>", "Joel Dahne <[email protected]>"]
version = "1.0"
version = "1.1"

[deps]
FLINT_jll = "e134572f-a0d5-539d-bddf-3cad8db41a82"
Expand Down
1 change: 1 addition & 0 deletions src/Arblib.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ include("precision.jl")

include("setters.jl")
include("constructors.jl")
include("conversion.jl")
include("predicates.jl")
include("show.jl")
include("promotion.jl")
Expand Down
16 changes: 0 additions & 16 deletions src/constructors.jl
Original file line number Diff line number Diff line change
Expand Up @@ -66,22 +66,6 @@ function Acb(re::AbstractString, im::AbstractString; prec::Integer = DEFAULT_PRE
return res
end

Base.Int(x::ArfOrRef; rnd::Union{arb_rnd,RoundingMode} = RoundNearest) =
is_int(x) ? get_si(x, rnd) : throw(InexactError(:Int64, Int64, x))

Base.Float64(x::MagOrRef) = get(x)
Base.Float64(x::ArfOrRef; rnd::Union{arb_rnd,RoundingMode} = RoundNearest) = get_d(x, rnd)
Base.Float64(x::ArbOrRef) = Float64(midref(x))

Base.ComplexF64(z::AcbOrRef) = Complex(Float64(realref(z)), Float64(imagref(z)))

function Base.BigFloat(x::ArfOrRef)
y = BigFloat(; precision = precision(x))
get!(y, x)
return y
end
Base.BigFloat(x::ArbOrRef) = BigFloat(midref(x))

Base.zero(::Union{Mag,Type{Mag}}) = Mag(UInt64(0))
Base.one(::Union{Mag,Type{Mag}}) = Mag(UInt64(1))
Base.zero(x::T) where {T<:Union{Arf,Arb,Acb}} = T(0, prec = precision(x))
Expand Down
102 changes: 102 additions & 0 deletions src/conversion.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Conversion to float types in Base

## Float64
Base.Float64(x::MagOrRef, r::RoundingMode = RoundUp) = Float64(x, convert(arb_rnd, r))
Base.Float64(x::ArfOrRef, r::RoundingMode) = Float64(x, convert(arb_rnd, r))
Base.Float64(x::ArbOrRef, r::RoundingMode = RoundNearest) = Float64(x, convert(arb_rnd, r))

function Base.Float64(x::MagOrRef, r::arb_rnd)
r == ArbRoundUp ||
throw(ArgumentError("only supports rounding up when converting Mag to Float64"))
return get(x)
end
Base.Float64(x::ArfOrRef, r::arb_rnd) = get_d(x, r)
Base.Float64(x::ArbOrRef, r::arb_rnd) = Float64(midref(x), r)

# Deprecated
Base.Float64(x::ArfOrRef; rnd::arb_rnd = ArbRoundNearest) = Float64(x, rnd)

## Float16 and Float32
Base.Float16(x::MagOrRef, r::RoundingMode = RoundUp) = Float16(Float64(x, r), r)
Base.Float16(x::MagOrRef, r::arb_rnd) = Float16(x, convert(RoundingMode, r))
Base.Float32(x::MagOrRef, r::RoundingMode = RoundUp) = Float32(Float64(x, r), r)
Base.Float32(x::MagOrRef, r::arb_rnd) = Float32(x, convert(RoundingMode, r))
Base.Float16(x::Union{ArfOrRef,ArbOrRef}, r::RoundingMode = RoundNearest) =
Float16(Float64(x, r), r)
Base.Float16(x::Union{ArfOrRef,ArbOrRef}, r::arb_rnd) = Float16(x, convert(RoundingMode, r))
Base.Float32(x::Union{ArfOrRef,ArbOrRef}, r::RoundingMode = RoundNearest) =
Float32(Float64(x, r), r)
Base.Float32(x::Union{ArfOrRef,ArbOrRef}, r::arb_rnd) = Float32(x, convert(RoundingMode, r))

## BigFloat

# Note that this uses different default values than the constructors
# in Base. It always defaults to RoundNearest and uses the precision
# given by the input argument. This means that it doesn't depend on
# the global rounding mode and precision.

Base.BigFloat(x::Union{ArfOrRef,ArbOrRef}, r::RoundingMode; precision = Base.precision(x)) =
BigFloat(x, convert(Base.MPFR.MPFRRoundingMode, r); precision)

Base.BigFloat(x::Union{ArfOrRef,ArbOrRef}, r::arb_rnd; precision = Base.precision(x)) =
BigFloat(x, convert(RoundingMode, r); precision)

function Base.BigFloat(
x::ArfOrRef,
r::Base.MPFR.MPFRRoundingMode = Base.MPFR.RoundNearest;
precision = Base.precision(x),
)
y = BigFloat(; precision)
get!(y, x, r)
return y
end
Base.BigFloat(
x::ArbOrRef,
r::Base.MPFR.MPFRRoundingMode = Base.MPFR.RoundNearest;
precision = Base.precision(x),
) = BigFloat(midref(x), r; precision)

## Conversion to integers

# IMPROVE: We currently don't support any rounding to integers. This
# would maybe be nice to do.
# IMPROVE: Is there any point in supporting conversion to other
# integers?

function Base.Int(x::ArfOrRef)
is_int(x) || throw(InexactError(:Int, Int, x))
typemin(Int) <= x <= typemax(Int) || throw(InexactError(:Int, Int, x))
return get_si(x, ArbRoundNearest)
end

Base.Int(x::ArbOrRef) = is_int(x) ? Int(midref(x)) : throw(InexactError(:Int, Int, x))

Base.Int(x::AcbOrRef) =
is_int(x) ? Int(midref(realref(x))) : throw(InexactError(:Int, Int, x))

function Base.BigInt(x::ArfOrRef)
is_int(x) || throw(InexactError(:BigInt, BigInt, x))
n = fmpz_struct()
ccall(
@libflint(arf_get_fmpz),
Cint,
(Ref{fmpz_struct}, Ref{arf_struct}, Ref{arb_rnd}),
n,
x,
ArbRoundNearest,
)
return BigInt(n)
end

Base.BigInt(x::ArbOrRef) =
is_int(x) ? BigInt(midref(x)) : throw(InexactError(:BigInt, BigInt, x))

Base.BigInt(x::AcbOrRef) =
is_int(x) ? BigInt(midref(realref(x))) : throw(InexactError(:BigInt, BigInt, x))

## Conversion to Complex

# TODO: This currently allows construction of Complex{ArbRef}, which
# we probably don't want.
Base.Complex{T}(z::AcbOrRef) where {T} = Complex{T}(realref(z), imagref(z))
Base.Complex(z::AcbOrRef) = Complex{Arb}(z)
11 changes: 11 additions & 0 deletions src/elementary.jl
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,17 @@ sqrtpos(x::ArbOrRef) = sqrtpos!(zero(x), x)
sqrt1pm1(x::ArbOrRef) = sqrt1pm1!(zero(x), x)
rsqrt(x::Union{ArbOrRef,AcbOrRef}) = rsqrt!(zero(x), x)

function Base.log2(x::Union{ArbOrRef,AcbOrRef})
res = log(x)
log_2 = const_log2!(Arb(prec = precision(x)))
return div!(res, res, log_2)
end
function Base.log10(x::Union{ArbOrRef,AcbOrRef})
res = log(x)
log_10 = const_log10!(Arb(prec = precision(x)))
return div!(res, res, log_10)
end

Base.sinpi(x::Union{ArbOrRef,AcbOrRef}) = sin_pi!(zero(x), x)
Base.cospi(x::Union{ArbOrRef,AcbOrRef}) = cos_pi!(zero(x), x)
tanpi(x::Union{ArbOrRef,AcbOrRef}) = tan_pi!(zero(x), x)
Expand Down
31 changes: 31 additions & 0 deletions src/float.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,34 @@ function Base.eps(T::Type{<:Union{ArfOrRef,ArbOrRef}})
return eps!(res, res)
end
Base.eps(x::Union{ArfOrRef,ArbOrRef}) = eps!(zero(x), x)

Base.typemin(::Type{<:MagOrRef}) = zero!(Mag())
Base.typemin(x::Union{ArfOrRef,ArbOrRef}) = neg_inf!(zero(x))
Base.typemin(T::Type{<:Union{ArfOrRef,ArbOrRef}}) = neg_inf!(zero(T))

Base.typemax(::Type{<:MagOrRef}) = inf!(Mag())
Base.typemax(x::Union{ArfOrRef,ArbOrRef}) = pos_inf!(zero(x))
Base.typemax(T::Type{<:Union{ArfOrRef,ArbOrRef}}) = pos_inf!(zero(T))

function Base.frexp(x::ArfOrRef)
m = zero(x)
e = fmpz_struct()
ccall(
@libflint(arf_frexp),
Nothing,
(Ref{arf_struct}, Ref{fmpz_struct}, Ref{arf_struct}),
m,
e,
x,
)

return m, BigInt(e)
end

function Base.frexp(x::ArbOrRef)
# Compute for midpoint and just scale radius
_, e = frexp(midref(x))
return ldexp(x, -e), e
end

Base.ldexp(x::Union{ArfOrRef,ArbOrRef}, n::Integer) = mul_2exp!(zero(x), x, n)
6 changes: 4 additions & 2 deletions src/interval.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ export radius,
radius([T, ] x::ArbOrRef)
Returns the radius of `x` as a `Mag`. If `T` is given convert to this
type, supports `Mag`, `Arf` and `Arb`.
type, supports `Mag`, `Arf`, `Arb` and `Float64`.
"""
radius(::Type{Mag}, x::ArbOrRef) = Mag(radref(x))
radius(::Type{Arf}, x::ArbOrRef) = Arf(radref(x), prec = precision(x))
radius(::Type{Arb}, x::ArbOrRef) = Arb(radref(x), prec = precision(x))
radius(::Type{Float64}, x::ArbOrRef) = Float64(radref(x))
radius(x::ArbOrRef) = radius(Mag, x)

"""
Expand Down Expand Up @@ -133,11 +134,12 @@ getinterval(x::ArbOrRef) = getinterval(Arf, x)
Returns a tuple `(m::Arf, r::Mag)` where `m` is the midpoint of the
ball and `r` is the radius. If `T` is given convert both `m` and `r`
to this type, supports `Arb`.
to this type, supports `Arf` and `Arb`.
See also [`setball`](@ref) and [`getinterval`](@ref).
"""
getball(x::ArbOrRef) = (Arf(midref(x)), Mag(radref(x)))
getball(::Type{Arf}, x::ArbOrRef) = (Arf(midref(x)), Arf(radref(x), prec = precision(x)))
getball(::Type{Arb}, x::ArbOrRef) = (Arb(midref(x)), Arb(radref(x), prec = precision(x)))

"""
Expand Down
36 changes: 26 additions & 10 deletions src/precision.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,32 @@ else
# overloading that we automatically support giving base argument.

# Types
Base._precision(::Type{<:Union{ArbTypes,ArbStructTypes,Ptr{<:ArbStructTypes}}}) =
DEFAULT_PRECISION[]
# Types not storing their precision
Base._precision(::Union{ArbStructTypes,Ptr{<:ArbStructTypes}}) = DEFAULT_PRECISION[]
# Types storing their precision
Base._precision(x::ArbTypes) = x.prec
# Mag doesn't store a precision
Base._precision(::MagOrRef) = DEFAULT_PRECISION[]
# ArbSeries and AcbSeries don't store their precision directly
Base._precision(x::Union{ArbSeries,AcbSeries}) = Base._precision(x.poly)
if VERSION < v"1.11.0-DEV"
Base._precision(::Type{<:Union{ArbTypes,ArbStructTypes,Ptr{<:ArbStructTypes}}}) =
DEFAULT_PRECISION[]
# Types not storing their precision
Base._precision(::Union{ArbStructTypes,Ptr{<:ArbStructTypes}}) = DEFAULT_PRECISION[]
# Types storing their precision
Base._precision(x::ArbTypes) = x.prec
# Mag doesn't store a precision
Base._precision(::MagOrRef) = DEFAULT_PRECISION[]
# ArbSeries and AcbSeries don't store their precision directly
Base._precision(x::Union{ArbSeries,AcbSeries}) = Base._precision(x.poly)
else
Base._precision_with_base_2(
::Type{<:Union{ArbTypes,ArbStructTypes,Ptr{<:ArbStructTypes}}},
) = DEFAULT_PRECISION[]
# Types not storing their precision
Base._precision_with_base_2(::Union{ArbStructTypes,Ptr{<:ArbStructTypes}}) =
DEFAULT_PRECISION[]
# Types storing their precision
Base._precision_with_base_2(x::ArbTypes) = x.prec
# Mag doesn't store a precision
Base._precision_with_base_2(::MagOrRef) = DEFAULT_PRECISION[]
# ArbSeries and AcbSeries don't store their precision directly
Base._precision_with_base_2(x::Union{ArbSeries,AcbSeries}) =
Base._precision_with_base_2(x.poly)
end

# Base.precision only allows AbstractFloat, we want to be able to use
# all ArbLib types.
Expand Down
18 changes: 18 additions & 0 deletions src/rounding.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,21 @@ Base.convert(::Type{arb_rnd}, ::RoundingMode{:Down}) = ArbRoundDown
Base.convert(::Type{arb_rnd}, ::RoundingMode{:Up}) = ArbRoundUp
Base.convert(::Type{arb_rnd}, ::RoundingMode{:Nearest}) = ArbRoundNearest
Base.convert(::Type{arb_rnd}, ::RoundingMode{:Exact}) = ArbRoundExact

function Base.convert(::Type{RoundingMode}, r::arb_rnd)
if r == ArbRoundToZero
return RoundToZero
elseif r == ArbRoundFromZero
return RoundFromZero
elseif r == ArbRoundDown
return RoundDown
elseif r == ArbRoundUp
return RoundUp
elseif r == ArbRoundNearest
return RoundNearest
elseif r == ArbRoundExact
return RoundingMode{:Exact}()
else
throw(ArgumentError("invalid Arb rounding mode code: $r"))
end
end
6 changes: 3 additions & 3 deletions src/setters.jl
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ end

function set!(res::ArbLike, ::Irrational{:φ}; prec::Integer = precision(res))
set!(res, 5)
sqrt!(res, res, prec)
add!(res, res, 1, prec)
return div!(res, res, 2, prec)
sqrt!(res, res; prec)
add!(res, res, 1; prec)
return mul_2exp!(res, res, -1)
end

function set!(
Expand Down
2 changes: 2 additions & 0 deletions src/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ function load_string!(x::Union{MagLike,ArfLike,ArbLike}, str::AbstractString)
return x
end

load_string(T::Type{<:Union{Mag,Arf,Arb}}, str::AbstractString) = load_string!(zero(T), str)

function dump_string(x::Union{MagLike,ArfLike,ArbLike})
char_ptr = dump(x)
str = unsafe_string(char_ptr)
Expand Down
22 changes: 0 additions & 22 deletions test/constructors.jl
Original file line number Diff line number Diff line change
Expand Up @@ -128,28 +128,6 @@
@test isequal(Acb((1, 2), (3, 4)), Acb(Arb((1, 2)), Arb((3, 4))))
end

@testset "Conversion" begin
@test Int(Arf(2.0)) isa Int
@test Int(Arf(2.0)) == 2
@test_throws InexactError Int(Arf(2.5))

@test Float64(Mag(2)) isa Float64
@test Float64(Mag(2)) == 2.0
@test Float64(Arf(2)) isa Float64
@test Float64(Arf(2)) == 2.0
@test Float64(Arb(2)) isa Float64
@test Float64(Arb(2)) == 2.0

@test ComplexF64(Acb(2 + 3im)) isa ComplexF64
@test ComplexF64(Acb(2 + 3im)) == 2.0 + 3.0im

@test Arblib.get_si(Arf(0.5)) == 0
@test Arblib.get_si(Arf(0.5); rnd = Arblib.ArbRoundFromZero) == 1

@test BigFloat(Arf(2.5)) == 2.5
@test BigFloat(Arb(2.5)) == 2.5
end

@testset "zeros/ones" begin
for T in [Arf, Arb, Acb]
@test zeros(T, 2) == [zero(T), zero(T)]
Expand Down
Loading

2 comments on commit 83bbb16

@Joel-Dahne
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@JuliaRegistrator register

Release Notes:

This is a minor release with a few new features:

  • Add typemin and typemax for Mag, Arf and Arb.
  • Add frexp and ldexp for Arf and Arb.
  • Add support for T == Float64 in radius. This doesn't allocate and can be useful for example when heuristically choosing algorithm based on the radius. Since Float64 has a bounded exponent this will overflow (rounding up) for extremely small or large radius.
  • Add support for T == Arf in getball.
  • Add log2 and log10 for Arb and Acb.
  • Improve conversion to Int and BigInt. The conversion to Int would previously crash on overflow.
  • Add load_string, non-inplace version of load_string!.

It also improves the handling of rounding arguments when converting to other float types. In particular it handles rounding arguments for conversion to BigFloat, Float64, Float32 and Float16. For Arb the conversion is done for the midpoint and the radius does therefore not play a role in the rounding. In the future it is possible that rounding for Arb will consider the full ball.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

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

Registration pull request created: JuliaRegistries/General/100393

Tagging

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v1.1.0 -m "<description of version>" 83bbb162fa14d7170b323c547ee2ddb22a16fcec
git push origin v1.1.0

Please sign in to comment.