This repository has been archived by the owner on May 4, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #119 from JuliaStats/nl/ops
Improve operators
- Loading branch information
Showing
2 changed files
with
289 additions
and
147 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,121 +1,132 @@ | ||
import Compat: @functorize | ||
## Lifted operators | ||
|
||
@noinline throw_error() = error() | ||
importall Base.Operators | ||
import Base: promote_op, abs, abs2, sqrt, cbrt, scalarmin, scalarmax | ||
using Compat: @functorize | ||
|
||
for f in ( | ||
:(@compat Base.:+), | ||
:(@compat Base.:-), | ||
:(@compat Base.:!), | ||
:(@compat Base.:~), | ||
) | ||
@eval begin | ||
@inline function $(f){S}(x::Nullable{S}) | ||
if isbits(S) | ||
Nullable($(f)(x.value), x.isnull) | ||
else | ||
throw_error() | ||
end | ||
end | ||
end | ||
# Methods adapted from Base Julia 0.5 | ||
if VERSION <= v"0.5.0-dev" | ||
promote_op(::Any, T) = T | ||
promote_op{T}(::Type{T}, ::Any) = T | ||
|
||
promote_op{R<:Number}(op, ::Type{R}) = typeof(op(one(R))) | ||
promote_op{R<:Number,S<:Number}(op, ::Type{R}, ::Type{S}) = typeof(op(one(R), one(S))) | ||
end | ||
|
||
# Implement the binary operators: +, -, *, /, %, &, |, ^, <<, and >> | ||
for f in ( | ||
:(@compat Base.:+), | ||
:(@compat Base.:-), | ||
:(@compat Base.:*), | ||
:(@compat Base.:/), | ||
:(@compat Base.:%), | ||
:(@compat Base.:&), | ||
:(@compat Base.:|), | ||
:(@compat Base.:^), | ||
:(@compat Base.:<<), | ||
:(@compat Base.:>>), | ||
) | ||
""" | ||
null_safe_op(f::Any, ::Type)::Bool | ||
null_safe_op(f::Any, ::Type, ::Type)::Bool | ||
Returns whether an operation `f` can safely be applied to any value of the passed type(s). | ||
Returns `false` by default. | ||
Custom types should implement methods for some or all operations `f` when applicable: | ||
returning `true` means that the operation may be called on any bit pattern without | ||
throwing an error (though returning invalid or nonsensical results is not a problem). | ||
In particular, this means that the operation can be applied on the whole domain of the | ||
type *and on uninitialized objects*. As a general rule, these proporties are only true for | ||
safe operations on `isbits` types. | ||
Types declared as safe can benefit from higher performance for operations on nullable: by | ||
always computing the result even for null values, a branch is avoided, which helps | ||
vectorization. | ||
""" | ||
null_safe_op(f::Any, ::Type) = false | ||
null_safe_op(f::Any, ::Type, ::Type) = false | ||
|
||
typealias SafeSignedInts Union{Int128,Int16,Int32,Int64,Int8} | ||
typealias SafeUnsignedInts Union{Bool,UInt128,UInt16,UInt32,UInt64,UInt8} | ||
typealias SafeInts Union{SafeSignedInts,SafeUnsignedInts} | ||
typealias SafeFloats Union{Float16,Float32,Float64} | ||
|
||
# Float types appear in both since they promote to themselves, | ||
# and therefore can't fail due to conversion of negative numbers | ||
typealias SafeSigned Union{SafeSignedInts,SafeFloats} | ||
typealias SafeUnsigned Union{SafeUnsignedInts,SafeFloats} | ||
typealias SafeTypes Union{SafeInts,SafeFloats} | ||
|
||
# Unary operators | ||
|
||
# Note this list does not include sqrt since it can raise an error, | ||
# nor cbrt (for which there is no functor on Julia 0.4) | ||
for op in (:+, :-, :abs, :abs2) | ||
@eval begin | ||
@inline function $(f){S1, S2}(x::Nullable{S1}, y::Nullable{S2}) | ||
if isbits(S1) & isbits(S2) | ||
Nullable($(f)(x.value, y.value), x.isnull | y.isnull) | ||
else | ||
throw_error() | ||
end | ||
end | ||
null_safe_op{T<:SafeTypes}(::typeof(@functorize($op)), ::Type{T}) = true | ||
end | ||
end | ||
|
||
# Implement the binary operators: == and != | ||
for f in ( | ||
:(@compat Base.:(==)), | ||
:(@compat Base.:!=), | ||
) | ||
@eval begin | ||
function $(f){S1, S2}(x::Nullable{S1}, y::Nullable{S2}) | ||
if isbits(S1) & isbits(S2) | ||
Nullable{Bool}($(f)(x.value, y.value), x.isnull | y.isnull) | ||
else | ||
error() | ||
end | ||
end | ||
end | ||
# No functors for these methods on 0.4: use the slow path | ||
if VERSION >= v"0.5.0-dev" | ||
null_safe_op{T<:SafeInts}(::typeof(~), ::Type{T}) = true | ||
null_safe_op{T<:SafeTypes}(::typeof(cbrt), ::Type{T}) = true | ||
null_safe_op(::typeof(!), ::Type{Bool}) = true | ||
end | ||
|
||
# Implement the binary operators: <, >, <=, and >= | ||
for f in ( | ||
:(@compat Base.:<), | ||
:(@compat Base.:>), | ||
:(@compat Base.:<=), | ||
:(@compat Base.:>=), | ||
) | ||
for op in (:+, :-, :!, :~, :abs, :abs2, :sqrt, :cbrt) | ||
@eval begin | ||
function $(f){S1, S2}(x::Nullable{S1}, y::Nullable{S2}) | ||
if isbits(S1) & isbits(S2) | ||
Nullable{Bool}($(f)(x.value, y.value), x.isnull | y.isnull) | ||
@inline function $op{S}(x::Nullable{S}) | ||
R = promote_op($op, S) | ||
if null_safe_op(@functorize($op), S) | ||
Nullable{R}($op(x.value), x.isnull) | ||
else | ||
error() | ||
x.isnull ? Nullable{R}() : | ||
Nullable{R}($op(x.value)) | ||
end | ||
end | ||
$op(x::Nullable{Union{}}) = Nullable() | ||
end | ||
end | ||
|
||
# Miscellaneous lifted operators | ||
# Binary operators | ||
|
||
function Base.abs{T}(x::Nullable{T}) | ||
if isbits(T) | ||
return Nullable(abs(x.value), x.isnull) | ||
else | ||
error() | ||
end | ||
end | ||
|
||
function Base.abs2{T}(x::Nullable{T}) | ||
if isbits(T) | ||
return Nullable(abs2(x.value), x.isnull) | ||
else | ||
error() | ||
# Note this list does not include ^, ÷ and % | ||
# Operations between signed and unsigned types are not safe: promotion to unsigned | ||
# gives an InexactError for negative numbers | ||
if VERSION >= v"0.5.0-dev" | ||
for op in (:+, :-, :*, :/, :&, :|, :<<, :>>, :(>>>), | ||
:(==), :<, :>, :<=, :>=, | ||
:scalarmin, :scalarmax) | ||
@eval begin | ||
# to fix ambiguities | ||
null_safe_op{S<:SafeFloats, | ||
T<:SafeFloats}(::typeof($op), ::Type{S}, ::Type{T}) = true | ||
null_safe_op{S<:SafeSigned, | ||
T<:SafeSigned}(::typeof($op), ::Type{S}, ::Type{T}) = true | ||
null_safe_op{S<:SafeUnsigned, | ||
T<:SafeUnsigned}(::typeof($op), ::Type{S}, ::Type{T}) = true | ||
end | ||
end | ||
end | ||
|
||
function Base.sqrt{T}(x::Nullable{T}) | ||
if isbits(T) | ||
return Nullable(sqrt(x.value), x.isnull) | ||
else | ||
error() | ||
else # No functors for all methods on 0.4: use the slow path for missing ones | ||
for op in (:+, :-, :*, :/, :&, :|, | ||
:<, :>, | ||
:scalarmin, :scalarmax) | ||
@eval begin | ||
# to fix ambiguities | ||
null_safe_op{S<:SafeFloats, | ||
T<:SafeFloats}(::typeof(@functorize($op)), ::Type{S}, ::Type{T}) = true | ||
null_safe_op{S<:SafeSigned, | ||
T<:SafeSigned}(::typeof(@functorize($op)), ::Type{S}, ::Type{T}) = true | ||
null_safe_op{S<:SafeUnsigned, | ||
T<:SafeUnsigned}(::typeof(@functorize($op)), ::Type{S}, ::Type{T}) = true | ||
end | ||
end | ||
end | ||
|
||
## Lifted functors | ||
|
||
@compat function (::typeof(@functorize(scalarmin))){S1, S2}(x::Nullable{S1}, y::Nullable{S2}) | ||
if isbits(S1) & isbits(S2) | ||
return Nullable(Base.scalarmin(x.value, y.value), x.isnull | y.isnull) | ||
else | ||
error() | ||
end | ||
end | ||
@compat function (::typeof(@functorize(scalarmax))){S1, S2}(x::Nullable{S1}, y::Nullable{S2}) | ||
if isbits(S1) & isbits(S2) | ||
return Nullable(Base.scalarmax(x.value, y.value), x.isnull | y.isnull) | ||
else | ||
error() | ||
for op in (:+, :-, :*, :/, :%, :÷, :&, :|, :^, :<<, :>>, :(>>>), | ||
:(==), :<, :>, :<=, :>=, | ||
:scalarmin, :scalarmax) | ||
@eval begin | ||
@inline function $op{S,T}(x::Nullable{S}, y::Nullable{T}) | ||
R = promote_op(@functorize($op), S, T) | ||
if null_safe_op(@functorize($op), S, T) | ||
Nullable{R}($op(x.value, y.value), x.isnull | y.isnull) | ||
else | ||
(x.isnull | y.isnull) ? Nullable{R}() : | ||
Nullable{R}($op(x.value, y.value)) | ||
end | ||
end | ||
$op(x::Nullable{Union{}}, y::Nullable{Union{}}) = Nullable() | ||
$op{S}(x::Nullable{Union{}}, y::Nullable{S}) = Nullable{S}() | ||
$op{S}(x::Nullable{S}, y::Nullable{Union{}}) = Nullable{S}() | ||
end | ||
end |
Oops, something went wrong.