diff --git a/README.md b/README.md index 56b6b36a..c9b623fe 100644 --- a/README.md +++ b/README.md @@ -86,10 +86,12 @@ a = copy(ttask) Notes: -- The [Turing](https://github.com/TuringLang/Turing.jl) probabilistic -programming language uses this task copying feature in an efficient -implementation of the [particle filtering](https://en.wikipedia.org/wiki/Particle_filter) -sampling algorithm for arbitrary order [Markov processes](https://en.wikipedia.org/wiki/Markov_model#Hidden_Markov_model). +- The [Turing](https://github.com/TuringLang/Turing.jl) probabilistic + programming language uses this task copying feature in an efficient + implementation of the [particle + filtering](https://en.wikipedia.org/wiki/Particle_filter) sampling + algorithm for arbitrary order [Markov + processes](https://en.wikipedia.org/wiki/Markov_model#Hidden_Markov_model). - From v0.6.0, Libtask is implemented by recording all the computing to a tape and copying that tape. Before that version, it is based on diff --git a/src/tapedtask.jl b/src/tapedtask.jl index 9a8680d7..d217fa79 100644 --- a/src/tapedtask.jl +++ b/src/tapedtask.jl @@ -197,8 +197,14 @@ function TArray(T::Type, dim) Base.depwarn("`TArray` is deprecated, please use `Array` instead.", :TArray) Array{T}(undef, dim) end -tzeros, tfill = zeros, fill - +function tzeros(args...) + Base.depwarn("`tzeros` is deprecated, please use `zeros` instead.", :tzeros) + zeros(args...) +end +function tfill(args...) + Base.depwarn("`tfill` is deprecated, please use `fill` instead.", :tzeros) + fill(args...) +end function TRef(x) Base.depwarn("`TRef` is deprecated, please use `Ref` instead.", :TArray) Ref(x) diff --git a/test/issues.jl b/test/issues.jl new file mode 100644 index 00000000..7a9d295f --- /dev/null +++ b/test/issues.jl @@ -0,0 +1,51 @@ +@testset "Issues" begin + @testset "Issue: PR-86 (DynamicPPL.jl/pull/261)" begin + function f() + t = Array{Int}(undef, 1) + t[1] = 0 + for _ in 1:4000 + produce(t[1]) + t[1] + t[1] = 1 + t[1] + end + end + + ttask = TapedTask(f) + + ex = try + for _ in 1:999 + consume(ttask) + consume(ttask) + a = copy(ttask) + consume(a) + consume(a) + end + catch ex + ex + end + @test ex === nothing + end + + @testset "Issue-140, copy unstarted task" begin + function f(x) + for i in 1:3 + produce(i + x) + end + end + + ttask = TapedTask(f, 3) + ttask2 = copy(ttask) + @test consume(ttask2) == 4 + ttask3 = copy(ttask; args=(4,)) + @test consume(ttask3) == 5 + end + + @testset "Issue-148, unused argument" begin + function f(x) + produce(1) + end + + ttask = TapedTask(f, 2) + @test consume(ttask) == 1 + end +end diff --git a/test/runtests.jl b/test/runtests.jl index 8cb0fef1..a045454f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -3,8 +3,8 @@ using Test include("tf.jl") include("tapedtask.jl") -include("tarray.jl") -include("tref.jl") +include("tape_copy.jl") +include("issues.jl") if haskey(ENV, "BENCHMARK") include("benchmarks.jl") diff --git a/test/tape_copy.jl b/test/tape_copy.jl new file mode 100644 index 00000000..5c3af3b7 --- /dev/null +++ b/test/tape_copy.jl @@ -0,0 +1,174 @@ +@testset "tape copy" begin + # Test case 1: stack allocated objects are deep copied. + @testset "stack allocated objects shallow copy" begin + function f() + t = 0 + while true + produce(t) + t = 1 + t + end + end + + ttask = TapedTask(f) + @test consume(ttask) == 0 + @test consume(ttask) == 1 + a = copy(ttask) + @test consume(a) == 2 + @test consume(a) == 3 + @test consume(ttask) == 2 + @test consume(ttask) == 3 + + @inferred Libtask.TapedFunction(f) + end + + # Test case 2: Array objects are deeply copied. + @testset "Array objects deep copy" begin + function f() + t = [0 1 2] + while true + produce(t[1]) + t[1] = 1 + t[1] + end + end + + ttask = TapedTask(f) + @test consume(ttask) == 0 + @test consume(ttask) == 1 + a = copy(ttask) + @test consume(a) == 2 + @test consume(a) == 3 + @test consume(ttask) == 2 + @test consume(ttask) == 3 + @test consume(ttask) == 4 + @test consume(ttask) == 5 + end + + # Test case 3: Dict objects are shallowly copied. + @testset "Dict objects shallow copy" begin + function f() + t = Dict(1=>10, 2=>20) + while true + produce(t[1]) + t[1] = 1 + t[1] + end + end + + ttask = TapedTask(f) + + @test consume(ttask) == 10 + @test consume(ttask) == 11 + + a = copy(ttask) + @test consume(a) == 12 + @test consume(a) == 13 + + @test consume(ttask) == 14 + @test consume(ttask) == 15 + end + + @testset "Array deep copy 2" begin + function f() + t = Array{Int}(undef, 1) + t[1] = 0 + while true + produce(t[1]) + t[1] + t[1] = 1 + t[1] + end + end + + ttask = TapedTask(f) + + consume(ttask) + consume(ttask) + a = copy(ttask) + consume(a) + consume(a) + + @test consume(ttask) == 2 + @test consume(a) == 4 + + DATA = Dict{Task, Array}() + function g() + ta = zeros(UInt64, 4) + for i in 1:4 + ta[i] = hash(current_task()) + DATA[current_task()] = ta + produce(ta[i]) + end + end + + ttask = TapedTask(g) + @test consume(ttask) == hash(ttask.task) # index = 1 + @test consume(ttask) == hash(ttask.task) # index = 2 + + a = copy(ttask) + @test consume(a) == hash(a.task) # index = 3 + @test consume(a) == hash(a.task) # index = 4 + + @test consume(ttask) == hash(ttask.task) # index = 3 + + @test DATA[ttask.task] == [hash(ttask.task), hash(ttask.task), hash(ttask.task), 0] + @test DATA[a.task] == [hash(ttask.task), hash(ttask.task), hash(a.task), hash(a.task)] + end + + # Test atomic values. + @testset "ref atomic" begin + function f() + t = Ref(1) + t[] = 0 + for _ in 1:6 + produce(t[]) + t[] + t[] += 1 + end + end + + ctask = TapedTask(f) + + consume(ctask) + consume(ctask) + + a = copy(ctask) + consume(a) + consume(a) + + @test consume(ctask) == 2 + @test consume(a) == 4 + end + + @testset "ref of dictionary deep copy" begin + function f() + t = Ref(Dict("A" => 1, 5 => "B")) + t[]["A"] = 0 + for _ in 1:6 + produce(t[]["A"]) + t[]["A"] += 1 + end + end + + ctask = TapedTask(f) + + consume(ctask) + consume(ctask) + + a = copy(ctask) + consume(a) + consume(a) + + @test consume(ctask) == 2 + @test consume(a) == 4 + end + + @testset "ref of array deep copy" begin + # Create a TRef storing a matrix. + x = TRef([1 2 3; 4 5 6]) + x[][1, 3] = 900 + @test x[][1,3] == 900 + + # TRef holding an array. + y = TRef([1,2,3]) + y[][2] = 19 + @test y[][2] == 19 + end +end diff --git a/test/tapedtask.jl b/test/tapedtask.jl index 20ad21ca..e49d5bb5 100644 --- a/test/tapedtask.jl +++ b/test/tapedtask.jl @@ -1,71 +1,4 @@ @testset "tapedtask" begin - # Test case 1: stack allocated objects are deep copied. - @testset "stack allocated objects" begin - function f() - t = 0 - while true - produce(t) - t = 1 + t - end - end - - ttask = TapedTask(f) - @test consume(ttask) == 0 - @test consume(ttask) == 1 - a = copy(ttask) - @test consume(a) == 2 - @test consume(a) == 3 - @test consume(ttask) == 2 - @test consume(ttask) == 3 - - @inferred Libtask.TapedFunction(f) - end - - # Test case 2: Array objects are deeply copied. - @testset "Array objects" begin - function f() - t = [0 1 2] - while true - produce(t[1]) - t[1] = 1 + t[1] - end - end - - ttask = TapedTask(f) - @test consume(ttask) == 0 - @test consume(ttask) == 1 - a = copy(ttask) - @test consume(a) == 2 - @test consume(a) == 3 - @test consume(ttask) == 2 - @test consume(ttask) == 3 - @test consume(ttask) == 4 - @test consume(ttask) == 5 - end - - # Test case 3: Dict objects are shallowly copied. - @testset "Dict objects" begin - function f() - t = Dict(1=>10, 2=>20) - while true - produce(t[1]) - t[1] = 1 + t[1] - end - end - - ttask = TapedTask(f) - - @test consume(ttask) == 10 - @test consume(ttask) == 11 - - a = copy(ttask) - @test consume(a) == 12 - @test consume(a) == 13 - - @test consume(ttask) == 14 - @test consume(ttask) == 15 - end - @testset "iteration" begin function f() t = 1 @@ -207,29 +140,4 @@ end end end - - @testset "Issues" begin - @testset "Issue-140, copy unstarted task" begin - function f(x) - for i in 1:3 - produce(i + x) - end - end - - ttask = TapedTask(f, 3) - ttask2 = copy(ttask) - @test consume(ttask2) == 4 - ttask3 = copy(ttask; args=(4,)) - @test consume(ttask3) == 5 - end - - @testset "Issue-148, unused argument" begin - function f(x) - produce(1) - end - - ttask = TapedTask(f, 2) - @test consume(ttask) == 1 - end - end end diff --git a/test/tarray.jl b/test/tarray.jl deleted file mode 100644 index 4d09610d..00000000 --- a/test/tarray.jl +++ /dev/null @@ -1,111 +0,0 @@ -@testset "tarray" begin - @testset "tzeros and tfill" begin - a = tzeros(Float64, 10) - @test eltype(a) === Float64 - @test all(iszero, a) - @test size(a) == (10,) - - a = tzeros(10) - @test eltype(a) === Float64 - @test all(iszero, a) - @test size(a) == (10,) - - a = tfill(5, 10) - @test eltype(a) === Int - @test all(==(5), a) - @test size(a) == (10,) - - a = tfill(19, (5, 5, 5, 5)) - @test eltype(a) === Int - @test all(==(19), a) - @test size(a) == (5, 5, 5, 5) - end - - @testset "push! and pop!" begin - ta1 = TArray(Int, 4) - push!(ta1, 1) - push!(ta1, 2) - @test pop!(ta1) == 2 - - # another constructor - ta1_2 = TArray(Int, 4) - push!(ta1_2, 1) - push!(ta1_2, 2) - @test pop!(ta1_2) == 2 - end - - @testset "task copy" begin - function f() - t = TArray(Int, 1) - t[1] = 0 - while true - produce(t[1]) - t[1] - t[1] = 1 + t[1] - end - end - - ttask = TapedTask(f) - - consume(ttask) - consume(ttask) - a = copy(ttask) - consume(a) - consume(a) - - @test consume(ttask) == 2 - @test consume(a) == 4 - - DATA = Dict{Task, Array}() - function g() - ta = tzeros(UInt64, 4) - for i in 1:4 - ta[i] = hash(current_task()) - DATA[current_task()] = convert(Array, ta) - produce(ta[i]) - end - end - - ttask = TapedTask(g) - @test consume(ttask) == hash(ttask.task) # index = 1 - @test consume(ttask) == hash(ttask.task) # index = 2 - - a = copy(ttask) - @test consume(a) == hash(a.task) # index = 3 - @test consume(a) == hash(a.task) # index = 4 - - @test consume(ttask) == hash(ttask.task) # index = 3 - - @test DATA[ttask.task] == [hash(ttask.task), hash(ttask.task), hash(ttask.task), 0] - @test DATA[a.task] == [hash(ttask.task), hash(ttask.task), hash(a.task), hash(a.task)] - end - - @testset "Issue: PR-86 (DynamicPPL.jl/pull/261)" begin - function f() - t = TArray(Int, 1) - t[1] = 0 - for _ in 1:4000 - produce(t[1]) - t[1] - t[1] = 1 + t[1] - end - end - - - ttask = TapedTask(f) - - ex = try - for _ in 1:999 - consume(ttask) - consume(ttask) - a = copy(ttask) - consume(a) - consume(a) - end - catch ex - ex - end - @test ex === nothing - end - -end diff --git a/test/tref.jl b/test/tref.jl deleted file mode 100644 index 100f5e1a..00000000 --- a/test/tref.jl +++ /dev/null @@ -1,62 +0,0 @@ -@testset "tref" begin - # Test atomic values. - @testset "atomic" begin - function f() - t = TRef(1) - t[] = 0 - for _ in 1:6 - produce(t[]) - t[] - t[] += 1 - end - end - - ctask = TapedTask(f) - - consume(ctask) - consume(ctask) - - a = copy(ctask) - consume(a) - consume(a) - - @test consume(ctask) == 2 - @test consume(a) == 4 - end - - # Test dictionary functionality. - @testset "dictionary" begin - function f() - t = TRef(Dict("A" => 1, 5 => "B")) - t[]["A"] = 0 - for _ in 1:6 - produce(t[]["A"]) - t[]["A"] += 1 - end - end - - ctask = TapedTask(f) - - consume(ctask) - consume(ctask) - - a = copy(ctask) - consume(a) - consume(a) - - @test consume(ctask) == 2 - @test consume(a) == 4 - end - - @testset "array" begin - # Create a TRef storing a matrix. - x = TRef([1 2 3; 4 5 6]) - x[][1, 3] = 900 - @test x[][1,3] == 900 - - # TRef holding an array. - y = TRef([1,2,3]) - y[][2] = 19 - @test y[][2] == 19 - end -end