Skip to content

Commit

Permalink
Merge ee137b5 into f4c9df9
Browse files Browse the repository at this point in the history
  • Loading branch information
blegat committed Dec 4, 2019
2 parents f4c9df9 + ee137b5 commit 77b56aa
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 23 deletions.
23 changes: 18 additions & 5 deletions src/bigint.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,34 @@ function mutable_operate_to!(output::BigInt, op::Union{typeof(*), typeof(+)},
mutable_operate_to!(output, op, a, b)
return mutable_operate!(op, output, c...)
end
function mutable_operate!(op::Function, x::BigInt, args::Vararg{Any, N}) where N
mutable_operate_to!(x, op, x, args...)
end

# add_mul
# Buffer to hold the product
buffer_for(::typeof(add_mul), args::Vararg{Type{BigInt}, N}) where {N} = BigInt()
function mutable_operate_to!(output::BigInt, ::typeof(add_mul), args::Vararg{BigInt, N}) where N
return mutable_buffered_operate_to!(BigInt(), output, add_mul, args...)
function mutable_operate_to!(output::BigInt, ::typeof(add_mul), x::BigInt, y::BigInt, z::BigInt, args::Vararg{BigInt, N}) where N
return mutable_buffered_operate_to!(BigInt(), output, add_mul, x, y, z, args...)
end

function mutable_buffered_operate_to!(buffer::BigInt, output::BigInt, ::typeof(add_mul),
a::BigInt, args::Vararg{BigInt, N}) where N
mutable_operate_to!(buffer, *, args...)
a::BigInt, x::BigInt, y::BigInt, args::Vararg{BigInt, N}) where N
mutable_operate_to!(buffer, *, x, y, args...)
return mutable_operate_to!(output, +, a, buffer)
end
function mutable_buffered_operate!(buffer::BigInt, op::typeof(add_mul), x::BigInt, args::Vararg{Any, N}) where N
return mutable_buffered_operate_to!(buffer, x, op, x, args...)
end

scaling_to_bigint(x::BigInt) = x
scaling_to_bigint(x::Number) = convert(BigInt, x)
scaling_to_bigint(J::LinearAlgebra.UniformScaling) = scaling_to_bigint(J.λ)
function mutable_operate_to!(output::BigInt, op::Union{typeof(+), typeof(*), typeof(add_mul)}, args::Vararg{Scaling, N}) where N
function mutable_operate_to!(output::BigInt, op::Union{typeof(+), typeof(*)}, args::Vararg{Scaling, N}) where N
return mutable_operate_to!(output, op, scaling_to_bigint.(args)...)
end
function mutable_operate_to!(output::BigInt, op::typeof(add_mul), x, y, z, args::Vararg{Scaling, N}) where N
return mutable_operate_to!(
output, op, scaling_to_bigint(x), scaling_to_bigint(y),
scaling_to_bigint(z), scaling_to_bigint.(args)...)
end
35 changes: 29 additions & 6 deletions src/interface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,14 @@ copy_if_mutable_fallback(::IsMutable, x) = mutable_copy(x)
copy_if_mutable(x) = copy_if_mutable_fallback(mutability(typeof(x)), x)

function mutable_operate_to_fallback(::NotMutable, output, op::Function, args...)
throw(ArgumentError("Cannot call `mutable_operate_to!($output, $op, $(args...))` as `$output` cannot be modifed to equal the result of the operation. Use `operate!` or `operate_to!` instead which returns the value of the result (possibly modifying the first argument) to write generic code that also works when the type cannot be modified."))
throw(ArgumentError("Cannot call `mutable_operate_to!(::$(typeof(output)), $op, ::$(join(typeof.(args), ", ::")))` as objects of type `$(typeof(output))` cannot be modifed to equal the result of the operation. Use `operate_to!` instead which returns the value of the result (possibly modifying the first argument) to write generic code that also works when the type cannot be modified."))
end

function mutable_operate_to_fallback(::IsMutable, output, op::typeof(add_mul), x, y)
return mutable_operate_to!(output, +, x, y)
end
function mutable_operate_to_fallback(::IsMutable, output, op::Function, args...)
error("`mutable_operate_to!($(typeof(output)), $op, ", join(typeof.(args), ", "),
error("`mutable_operate_to!(::$(typeof(output)), $op, ::", join(typeof.(args), ", ::"),
")` is not implemented yet.")
end

Expand All @@ -79,19 +82,41 @@ end
Modify the value of `output` to be equal to the value of `op(args...)`. Can
only be called if `mutability(output, op, args...)` returns `true`.
If `output === args[i]` for some `i`,
* The user should expect to get an error. `operate!` or `mutable_operate!` should be used instead.
* Any method not supporting this case should throw an error.
For instance, in DynamicPolynomials, `mutable_operate_to!(p, +, p, q)` throws an
error because otherwise, the algorithm would fill `p` while iterating over the
terms of `p` and `q` hence it will never terminate. On the other hand
`mutable_operate!(+, p, q)` uses a different algorithm that efficiently inserts
the terms of `q` in the sorted list of terms of `p` with minimal displacement.
"""
function mutable_operate_to!(output, op::Function, args::Vararg{Any, N}) where N
mutable_operate_to_fallback(mutability(output, op, args...), output, op, args...)
end

function mutable_operate_fallback(::NotMutable, op::Function, args...)
throw(ArgumentError("Cannot call `mutable_operate!($op, ::$(join(typeof.(args), ", ::")))` as objects of type `$(typeof(args[1]))` cannot be modifed to equal the result of the operation. Use `operate!` instead which returns the value of the result (possibly modifying the first argument) to write generic code that also works when the type cannot be modified."))
end

function mutable_operate_fallback(::IsMutable, op::typeof(add_mul), x, y)
return mutable_operate!(+, x, y)
end
function mutable_operate_fallback(::IsMutable, op::Function, args...)
error("`mutable_operate!($op, ::", join(typeof.(args), ", ::"),
")` is not implemented yet.")
end

"""
mutable_operate!(op::Function, args...)
Modify the value of `args[1]` to be equal to the value of `op(args...)`. Can
only be called if `mutability(args[1], op, args...)` returns `true`.
"""
function mutable_operate!(op::Function, args::Vararg{Any, N}) where N
mutable_operate_to!(args[1], op, args...)
mutable_operate_fallback(mutability(args[1], op, args...), op, args...)
end

buffer_for(::Function, args::Vararg{Type, N}) where {N} = nothing
Expand All @@ -114,9 +139,7 @@ Modify the value of `args[1]` to be equal to the value of `op(args...)`,
possibly modifying `buffer`. Can only be called if
`mutability(args[1], op, args...)` returns `true`.
"""
function mutable_buffered_operate!(buffer, op::Function, args::Vararg{Any, N}) where N
mutable_buffered_operate_to!(buffer, args[1], op, args...)
end
function mutable_buffered_operate! end
function mutable_buffered_operate!(::Nothing, op::Function, args::Vararg{Any, N}) where N
return mutable_operate!(op, args...)
end
Expand Down
4 changes: 2 additions & 2 deletions src/linear_algebra.jl
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ end
function _add_mul_array(C::Vector, A::AbstractMatrix, B::AbstractVector)
Astride = size(A, 1)
# We need a buffer to hold the intermediate multiplication.
mul_buffer = buffer_for(add_mul, eltype(A), eltype(B))
mul_buffer = buffer_for(add_mul, eltype(C), eltype(A), eltype(B))

#@inbounds begin
for k = eachindex(B)
Expand All @@ -184,7 +184,7 @@ end

# This is incorrect if `C` is `LinearAlgebra.Symmetric` as we modify twice the same diagonal element.
function _add_mul_array(C::Matrix, A::AbstractMatrix, B::AbstractMatrix)
mul_buffer = buffer_for(add_mul, eltype(A), eltype(B))
mul_buffer = buffer_for(add_mul, eltype(C), eltype(A), eltype(B))

#@inbounds begin
for i = 1:size(A, 1), j = 1:size(B, 2)
Expand Down
2 changes: 0 additions & 2 deletions src/shortcuts.jl
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ function promote_operation(::typeof(add_mul), T::Type, args::Vararg{Type, N}) wh
return promote_operation(+, T, promote_operation(*, args...))
end

mutable_operate!(::typeof(add_mul), x, y) = mutable_operate!(+, x, y)

"""
add_mul_to!(output, args...)
Expand Down
10 changes: 9 additions & 1 deletion test/dummy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,16 @@ MA.promote_operation(::typeof(+), ::Type{DummyBigInt}, ::Type{DummyBigInt}) = Du
_data(x) = x
_data(x::DummyBigInt) = x.data
MA.scaling(x::DummyBigInt) = x

MA.mutable_operate_to!(x::DummyBigInt, op::Function, args...) = DummyBigInt(MA.mutable_operate_to!(x.data, op, _data.(args)...))
MA.mutable_operate!(op::Union{typeof(zero), typeof(one)}, x::DummyBigInt, args...) = DummyBigInt(MA.mutable_operate!(op, x.data, _data.(args)...))
function MA.mutable_operate!(op::Function, x::DummyBigInt, args::Vararg{Any, N}) where N
MA.mutable_operate_to!(x, op, x, args...)
end

function MA.mutable_operate!(op::Union{typeof(zero), typeof(one)}, x::DummyBigInt)
return DummyBigInt(MA.mutable_operate!(op, x.data))
end

MA.promote_operation(::typeof(*), ::Type{DummyBigInt}, ::Type{DummyBigInt}) = DummyBigInt
Base.convert(::Type{DummyBigInt}, x::Int) = DummyBigInt(x)
Base.:(==)(x::DummyBigInt, y::DummyBigInt) = x.data == y.data
Expand Down
21 changes: 21 additions & 0 deletions test/interface.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Test
import MutableArithmetics
const MA = MutableArithmetics

struct DummyMutable
end

MA.promote_operation(::typeof(+), ::Type{DummyMutable}, ::Type{DummyMutable}) = DummyMutable
MA.mutability(::Type{DummyMutable}) = MA.IsMutable()

@testset "Errors" begin
err = ArgumentError("Cannot call `mutable_operate_to!(::$Int, +, ::$Int, ::$Int)` as objects of type `$Int` cannot be modifed to equal the result of the operation. Use `operate_to!` instead which returns the value of the result (possibly modifying the first argument) to write generic code that also works when the type cannot be modified.")
@test_throws err MA.mutable_operate_to!(0, +, 0, 0)
err = ArgumentError("Cannot call `mutable_operate!(+, ::$Int, ::$Int)` as objects of type `$Int` cannot be modifed to equal the result of the operation. Use `operate!` instead which returns the value of the result (possibly modifying the first argument) to write generic code that also works when the type cannot be modified.")
@test_throws err MA.mutable_operate!(+, 0, 0)
x = DummyMutable()
err = ErrorException("`mutable_operate_to!(::DummyMutable, +, ::DummyMutable, ::DummyMutable)` is not implemented yet.")
@test_throws err MA.mutable_operate_to!(x, +, x, x)
err = ErrorException("`mutable_operate!(+, ::DummyMutable, ::DummyMutable)` is not implemented yet.")
@test_throws err MA.mutable_operate!(+, x, x)
end
7 changes: 0 additions & 7 deletions test/operations.jl

This file was deleted.

2 changes: 2 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ const MA = MutableArithmetics

include("utilities.jl")

include("interface.jl")

@testset "Int" begin
include("int.jl")
end
Expand Down

0 comments on commit 77b56aa

Please sign in to comment.