Skip to content

Commit

Permalink
Merge pull request #98 from dpsanders/convert_to_convert
Browse files Browse the repository at this point in the history
Change make_interval to convert
  • Loading branch information
dpsanders committed Apr 2, 2016
2 parents 90d5e6f + 94424d6 commit 6b531a7
Show file tree
Hide file tree
Showing 13 changed files with 233 additions and 175 deletions.
3 changes: 1 addition & 2 deletions src/ValidatedNumerics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,12 @@ import Base:
sinh, cosh, tanh, asinh, acosh, atanh,
union, intersect, isempty,
convert, promote_rule, eltype,
BigFloat, float, widen,
BigFloat, float, widen, big,
, eps,
floor, ceil, trunc, sign, round,
expm1, log1p,
isfinite, isnan


export
Interval,
@interval, @biginterval, @floatinterval, @make_interval,
Expand Down
80 changes: 71 additions & 9 deletions src/intervals/conversion_promotion.jl
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
# This file is part of the ValidatedNumerics.jl package; MIT licensed

## Conversion and promotion

## Default conversion to Interval, corresponds to Interval{Float64}
# do we really need this?
convert{T<:Real}(::Type{Interval}, x::T) = make_interval(Float64, x)

## Conversion to specific type intervals
convert{T<:Real}(::Type{Interval{T}}, x::Real) = make_interval(T, x)

## Promotion

## Promotion rules
promote_rule{T<:Real, S<:Real}(::Type{Interval{T}}, ::Type{Interval{S}}) =
Expand All @@ -19,3 +11,73 @@ promote_rule{T<:Real, S<:Real}(::Type{Interval{T}}, ::Type{S}) =

promote_rule{T<:Real}(::Type{BigFloat}, ::Type{Interval{T}}) =
Interval{promote_type(T, BigFloat)}


## Conversion rules

# convert{T<:Real}(::Type{Interval}, x::T) = convert(Interval{Float64}, x)

doc"""`split_interval_string` deals with strings of the form
\"[3.5, 7.2]\""""

function split_interval_string(T, x::AbstractString)
if !(contains(x, "[")) # string like "3.1"
return @thin_round(T, parse(T, x))
end

m = match(r"\[(.*),(.*)\]", x) # string like "[1, 2]"

if m == nothing
throw(ArgumentError("Unable to process string $x as interval"))
end

@round(T, parse(T, m.captures[1]), parse(T, m.captures[2]))
end


# Floating point intervals:

convert{T<:AbstractFloat}(::Type{Interval{T}}, x::AbstractString) =
split_interval_string(T, x)

function convert{T<:AbstractFloat, S<:Real}(::Type{Interval{T}}, x::S)
Interval{T}( T(x, RoundDown), T(x, RoundUp) )
# the rounding up could be down as nextfloat of the rounded down one
end

function convert{T<:AbstractFloat}(::Type{Interval{T}}, x::Float64)
convert(Interval{T}, rationalize(x))
end

function convert{T<:AbstractFloat}(::Type{Interval{T}}, x::Interval)
Interval{T}( T(x.lo, RoundDown), T(x.hi, RoundUp) )
end


# Complex numbers:
convert{T<:AbstractFloat}(::Type{Interval{T}}, x::Complex{Bool}) = (x == im) ?
one(T)*im : throw(ArgumentError("Complex{Bool} not equal to im"))


# Rational intervals
function convert(::Type{Interval{Rational{Int}}}, x::Irrational)
a = float(convert(Interval{BigFloat}, x))
convert(Interval{Rational{Int}}, a)
end

function convert(::Type{Interval{Rational{BigInt}}}, x::Irrational)
a = convert(Interval{BigFloat}, x)
convert(Interval{Rational{BigInt}}, a)
end

convert{T<:Integer, S<:Integer}(::Type{Interval{Rational{T}}}, x::S) =
Interval(x*one(Rational{T}))

convert{T<:Integer, S<:Integer}(::Type{Interval{Rational{T}}}, x::Rational{S}) =
Interval(x*one(Rational{T}))

convert{T<:Integer, S<:Float64}(::Type{Interval{Rational{T}}}, x::S) =
Interval(rationalize(T, x))

convert{T<:Integer, S<:BigFloat}(::Type{Interval{Rational{T}}}, x::S) =
Interval(rationalize(T, x))
13 changes: 8 additions & 5 deletions src/intervals/functions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
# CRlibm does not contain a correctly-rounded ^ function for Float64
# Use the BigFloat version from MPFR instead, which is correctly-rounded:

# Write explicitly like this to avoid ambiguity warnings:
for T in (:Integer, :Rational, :Float64, :BigFloat, :Interval)
@eval ^(a::Interval{Float64}, x::$T) = float(big53(a)^x)

@eval ^(a::Interval{Float64}, x::$T) = convert(Interval{Float64}, big53(a)^x)
end


# Integer power:

function ^(a::Interval{BigFloat}, n::Integer)
Expand Down Expand Up @@ -106,7 +109,7 @@ end
function ^{T<:Integer,}(a::Interval{Rational{T}}, x::AbstractFloat)
a = Interval(a.lo.num/a.lo.den, a.hi.num/a.hi.den)
a = a^x
make_interval(Rational{T}, a)
convert(Interval{Rational{T}}, a)
end

# Rational power
Expand All @@ -120,16 +123,16 @@ function ^{S<:Integer}(a::Interval{BigFloat}, r::Rational{S})
return emptyinterval(a)
end

isinteger(r) && return make_interval(T, a^round(S,r))
r == one(S)//2 && return make_interval(T, sqrt(a))
isinteger(r) && return convert(Interval{T}, a^round(S,r))
r == one(S)//2 && return sqrt(a)

a = a domain
(isempty(r) || isempty(a)) && return emptyinterval(a)

r = r.num / r.den
a = a^r

make_interval(T, a)
convert(Interval{T}, a)
end

# Interval power of an interval:
Expand Down
2 changes: 1 addition & 1 deletion src/intervals/hyperbolic_functions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,6 @@ for f in (:tanh, :asinh, :acosh, :atanh)
@eval function ($f)(a::Interval{Float64})
isempty(a) && return a

float(($f)(big53(a)))
float( ($f)(big53(a)) )
end
end
4 changes: 2 additions & 2 deletions src/intervals/intervals.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ immutable Interval{T<:Real} <: Real
hi :: T

function Interval(a::Real, b::Real)
# The following exception is needed to define emptyintervals as [∞,-∞]
(isinf(a) && isinf(b)) && return new(a, b)

if a > b
(isinf(a) && isinf(b)) && return new(a, b) # empty interval = [∞,-∞]

throw(ArgumentError("Must have a ≤ b to construct Interval(a, b)."))
end

Expand Down
21 changes: 12 additions & 9 deletions src/intervals/precision.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,29 @@
doc"`big53` creates an equivalent `BigFloat` interval to a given `Float64` interval."
function big53(a::Interval{Float64})
x = with_interval_precision(53) do # precision of Float64
Interval{BigFloat}(a)
convert(Interval{BigFloat}, a)
end
end


set_interval_precision(::Type{Float64}, prec=-1) = parameters.precision_type = Float64
set_interval_precision(::Type{Float64}) = parameters.precision_type = Float64
# does not change the BigFloat precision


function set_interval_precision(::Type{BigFloat}, precision::Integer=256)
setprecision(precision)
function set_interval_precision{T}(::Type{T}, precision::Integer=256)
#println("SETTING BIGFLOAT PRECISION TO $precision")
setprecision(BigFloat, precision)

parameters.precision_type = BigFloat
parameters.precision_type = T
parameters.precision = precision
parameters.pi = make_interval(BigFloat, pi)
parameters.pi = convert(Interval{BigFloat}, pi)

precision
end

function with_interval_precision(f::Function, precision::Integer=256)
old_interval_precision = get_interval_precision()
#@show old_interval_precision
set_interval_precision(precision)
try
return f()
Expand All @@ -36,11 +39,11 @@ end
set_interval_precision(precision) = set_interval_precision(BigFloat, precision)
set_interval_precision(t::Tuple) = set_interval_precision(t...)

get_interval_precision() =
parameters.precision_type == Float64 ? (Float64, -1) : (BigFloat, parameters.precision)
get_interval_precision() = (parameters.precision_type, parameters.precision)
#parameters.precision_type == Float64 ? (Float64, -1) : (BigFloat, parameters.precision)


const float_interval_pi = make_interval(Float64, pi) # does not change
const float_interval_pi = convert(Interval{Float64}, pi) # does not change

pi_interval(::Type{BigFloat}) = parameters.pi
pi_interval(::Type{Float64}) = float_interval_pi
112 changes: 14 additions & 98 deletions src/intervals/rounding.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@

Base.float{T}(::Type{Rational{T}}) = typeof(float(one(Rational{T})))

# better to just do the following ?
# Base.float(::Type{Rational{Int64}}) = Float64
# Base.float(::Type{Rational{BigInt}}) = BigFloat

# Use that type for rounding with rationals, e.g. for sqrt:

if VERSION < v"0.5.0-dev+1182"
Expand Down Expand Up @@ -61,101 +65,7 @@ macro thin_round(T, expr)
end
end

doc"""`split_interval_string` deals with strings of the form
\"[3.5, 7.2]\""""

function split_interval_string(T, x::AbstractString)
if !(contains(x, "["))
return @thin_round(T, parse(T,x))
end

m = match(r"\[(.*),(.*)\]", x)

if m == nothing
throw(ArgumentError("Unable to process string $x as interval"))
end

@round(T, parse(T, m.captures[1]), parse(T, m.captures[2]))
end


doc"""`make_interval` is used by `@interval` to create intervals from
individual elements of different types"""
# make_interval for BigFloat intervals
make_interval(::Type{BigFloat}, x::AbstractString) =
split_interval_string(BigFloat, x)

make_interval(::Type{BigFloat}, x::Irrational) = @thin_round(BigFloat, big(x))
make_interval(::Type{BigFloat}, x::Rational) = @thin_round(BigFloat, BigFloat(x))

function make_interval(::Type{BigFloat}, x::Float64)
isinf(x) && return Interval(convert(BigFloat,x))
split_interval_string(BigFloat, string(x))
end

make_interval(::Type{BigFloat}, x::Integer) =
@thin_round(BigFloat, convert(BigFloat, x))
make_interval(::Type{BigFloat}, x::BigFloat) = @thin_round(BigFloat, x) # convert to possibly different BigFloat precision

function make_interval(::Type{BigFloat}, x::Interval)
# a = make_interval(BigFloat, x.lo)
# b = make_interval(BigFloat, x.hi)
# Interval(a.lo, b.hi)
@round(BigFloat, big(x.lo), big(x.hi))
end


# make_interval for Float64 intervals
make_interval(::Type{Float64}, x::AbstractString) = split_interval_string(Float64, x)

make_interval(::Type{Float64}, x::Irrational) = float(make_interval(BigFloat, x))
make_interval(::Type{Float64}, x::Rational) = @thin_round(Float64, Float64(x))

function make_interval(::Type{Float64}, x::Float64)
isinf(x) && return Interval(x)
split_interval_string(Float64, string(x))
end

function make_interval(::Type{Float64}, x::Integer)
a = setprecision(53) do
make_interval(BigFloat, x)
end
float(a)
end

make_interval(::Type{Float64}, x::BigFloat) = @thin_round(Float64, convert(Float64, x))

function make_interval(::Type{Float64}, x::Interval)
# a = make_interval(Float64, x.lo)
# b = make_interval(Float64, x.hi)

Interval( Float64(x.lo, RoundDown), Float64(x.hi, RoundUp) )

end


# make_interval for Rational intervals
function make_interval(::Type{Rational{Int}}, x::Irrational)
a = float(make_interval(BigFloat, x))
make_interval(Rational{Int}, a)
end
function make_interval(::Type{Rational{BigInt}}, x::Irrational)
a = make_interval(BigFloat, x)
make_interval(Rational{BigInt}, a)
end
make_interval{T<:Integer, S<:Integer}(::Type{Rational{T}}, x::S) =
Interval(x*one(Rational{T}))
make_interval{T<:Integer, S<:Integer}(::Type{Rational{T}}, x::Rational{S}) =
Interval(x*one(Rational{T}))
make_interval{T<:Integer, S<:Float64}(::Type{Rational{T}}, x::S) =
Interval(rationalize(T, x))
make_interval{T<:Integer, S<:BigFloat}(::Type{Rational{T}}, x::S) =
Interval(rationalize(T, x))
function make_interval{T<:Integer}(::Type{Rational{T}}, x::Interval)
a = make_interval(Rational{T}, x.lo)
b = make_interval(Rational{T}, x.hi)
Interval(a.lo, b.hi)
end


doc"""`transform` transforms a string by applying the function `f` and type
Expand Down Expand Up @@ -183,6 +93,10 @@ function transform(expr::Expr, f::Symbol, T)
end
end

if expr.head == :macrocall # handles BigInts etc.
return :($f($(esc(T)), $(esc(expr)))) # hack: pass straight through
end

for (i, arg) in enumerate(expr.args)
i < first && continue
#@show i,arg
Expand All @@ -200,21 +114,21 @@ and making each literal (0.1, 1, etc.) into a corresponding interval constructio
by calling `transform`."""

function make_interval(T, expr1, expr2)
expr1 = transform(expr1, :make_interval, T)
expr1 = transform(expr1, :convert, :(Interval{$T}))

if isempty(expr2) # only one argument
return expr1
end

expr2 = transform(expr2[1], :make_interval, T)
expr2 = transform(expr2[1], :convert, :(Interval{$T}))

:(hull($expr1, $expr2))
end


# float(x::Interval) = Interval(convert(Float64,x.lo),convert(Float64,x.hi))
float(x::Interval) =
@round(BigFloat, convert(Float64, x.lo), convert(Float64, x.hi))
# @round(BigFloat, convert(Float64, x.lo), convert(Float64, x.hi))
convert(Interval{Float64}, x)

## Change type of interval rounding:

Expand All @@ -238,3 +152,5 @@ function set_interval_rounding(mode)

parameters.rounding = mode # a symbol
end

big{T}(x::Interval{T}) = convert(Interval{BigFloat}, x)
Loading

0 comments on commit 6b531a7

Please sign in to comment.