From 799f3ad154274cedd6fc70eb2dd75e52b31987e2 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Wed, 27 Aug 2025 17:00:48 +0200 Subject: [PATCH 1/6] Improve type stability in correctness tests --- DifferentiationInterfaceTest/Project.toml | 2 +- .../src/tests/correctness_eval.jl | 685 ++++++++++-------- 2 files changed, 382 insertions(+), 305 deletions(-) diff --git a/DifferentiationInterfaceTest/Project.toml b/DifferentiationInterfaceTest/Project.toml index 94932b6c9..0c0771166 100644 --- a/DifferentiationInterfaceTest/Project.toml +++ b/DifferentiationInterfaceTest/Project.toml @@ -1,7 +1,7 @@ name = "DifferentiationInterfaceTest" uuid = "a82114a7-5aa3-49a8-9643-716bb13727a3" authors = ["Guillaume Dalle", "Adrian Hill"] -version = "0.10.1" +version = "0.10.2" [deps] ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b" diff --git a/DifferentiationInterfaceTest/src/tests/correctness_eval.jl b/DifferentiationInterfaceTest/src/tests/correctness_eval.jl index 22208bb43..e7ce47127 100644 --- a/DifferentiationInterfaceTest/src/tests/correctness_eval.jl +++ b/DifferentiationInterfaceTest/src/tests/correctness_eval.jl @@ -54,46 +54,53 @@ for op in ALL_OPS sparsity::Bool, reprepare::Bool, ) + ≈(x, y) = isapprox(x, y; atol, rtol) (; f, x, y, res1, contexts, prep_args) = new_scen = deepcopy(scen) - local prep preptup_cands_val, preptup_cands_noval = map(1:2) do _ - prep = $prep_op(f, ba, prep_args.x, prep_args.contexts...) - prep_nostrict = $prep_op( + prep0 = $prep_op(f, ba, prep_args.x, prep_args.contexts...) + prep_nostrict0 = $prep_op( f, ba, prep_args.x, prep_args.contexts...; strict=Val(false) ) if reprepare && has_size(x) && has_size(y) && (size(x) != size(prep_args.x)) - prep = $prep_op!(f, prep, ba, x, contexts...) - prep_nostrict = $prep_op!(f, prep_nostrict, ba, x, contexts...) + prep = $prep_op!(f, prep0, ba, x, contexts...) + prep_nostrict = $prep_op!(f, prep_nostrict0, ba, x, contexts...) + else + prep = prep0 + prep_nostrict = prep_nostrict0 end - [(), (prep,), (prep_nostrict,)] + ((), (prep,), (prep_nostrict,)) end - for (preptup_val, preptup_noval) in zip(preptup_cands_val, preptup_cands_noval) + foreach(preptup_cands_val) do preptup_val y_out1_val, res1_out1_val = $val_and_op( f, preptup_val..., ba, x, contexts... ) y_out2_val, res1_out2_val = $val_and_op( f, preptup_val..., ba, x, contexts... ) - res1_out1_noval = $op(f, preptup_noval..., ba, x, contexts...) - res1_out2_noval = $op(f, preptup_noval..., ba, x, contexts...) - let (≈)(x, y) = isapprox(x, y; atol, rtol) - @test isempty(preptup_noval) || only(preptup_noval) isa $P - @test y_out1_val ≈ scen.y - @test y_out2_val ≈ scen.y - @test res1_out1_val ≈ scen.res1 - @test res1_out2_val ≈ scen.res1 - @test res1_out1_noval ≈ scen.res1 - @test res1_out2_noval ≈ scen.res1 - end + @test y_out1_val ≈ scen.y + @test y_out2_val ≈ scen.y + @test res1_out1_val ≈ scen.res1 + @test res1_out2_val ≈ scen.res1 if sparsity && $op == jacobian @test mynnz(res1_out1_val) == mynnz(scen.res1) @test mynnz(res1_out2_val) == mynnz(scen.res1) + end + end + foreach(preptup_cands_noval) do preptup_noval + res1_out1_noval = $op(f, preptup_noval..., ba, x, contexts...) + res1_out2_noval = $op(f, preptup_noval..., ba, x, contexts...) + @test isempty(preptup_noval) || only(preptup_noval) isa $P + @test res1_out1_noval ≈ scen.res1 + @test res1_out2_noval ≈ scen.res1 + if sparsity && $op == jacobian @test mynnz(res1_out1_noval) == mynnz(scen.res1) @test mynnz(res1_out2_noval) == mynnz(scen.res1) end end - @test_throws PME $val_and_op(nothing, prep, ba, x, contexts...) - @test_throws PME $op(nothing, prep, ba, x, contexts...) + let prep = $prep_op(f, ba, prep_args.x, prep_args.contexts...) + @test_throws PME $val_and_op(nothing, prep, ba, x, contexts...) + @test_throws PME $op(nothing, prep, ba, x, contexts...) + end scenario_intact && @test new_scen == scen return nothing end @@ -108,60 +115,67 @@ for op in ALL_OPS sparsity::Bool, reprepare::Bool, ) + ≈(x, y) = isapprox(x, y; atol, rtol) (; f, x, y, res1, contexts, prep_args) = new_scen = deepcopy(scen) - local prep preptup_cands_val, preptup_cands_noval = map(1:2) do _ - prep = $prep_op(f, ba, prep_args.x, prep_args.contexts...) - prep_nostrict = $prep_op( + prep0 = $prep_op(f, ba, prep_args.x, prep_args.contexts...) + prep_nostrict0 = $prep_op( f, ba, prep_args.x, prep_args.contexts...; strict=Val(false) ) if reprepare && has_size(x) && has_size(y) && (size(x) != size(prep_args.x)) - prep = $prep_op!(f, prep, ba, x, contexts...) - prep_nostrict = $prep_op!(f, prep_nostrict, ba, x, contexts...) + prep = $prep_op!(f, prep0, ba, x, contexts...) + prep_nostrict = $prep_op!(f, prep_nostrict0, ba, x, contexts...) + else + prep = prep0 + prep_nostrict = prep_nostrict0 end - [(), (prep,), (prep_nostrict,)] + ((), (prep,), (prep_nostrict,)) end - for (preptup_val, preptup_noval) in zip(preptup_cands_val, preptup_cands_noval) + foreach(preptup_cands_val) do preptup_val res1_in1_val = mysimilar(res1) res1_in2_val = mysimilar(res1) - res1_in1_noval = mysimilar(res1) - res1_in2_noval = mysimilar(res1) y_out1_val, res1_out1_val = $val_and_op!( f, res1_in1_val, preptup_val..., ba, x, contexts... ) y_out2_val, res1_out2_val = $val_and_op!( f, res1_in2_val, preptup_val..., ba, x, contexts... ) + @test y_out1_val ≈ scen.y + @test y_out2_val ≈ scen.y + @test res1_in1_val === res1_out1_val + @test res1_in2_val === res1_out2_val + @test res1_out1_val ≈ scen.res1 + @test res1_out2_val ≈ scen.res1 + if sparsity && $op == jacobian + @test mynnz(res1_out1_val) == mynnz(scen.res1) + @test mynnz(res1_out2_val) == mynnz(scen.res1) + end + end + foreach(preptup_cands_noval) do preptup_noval + res1_in1_noval = mysimilar(res1) + res1_in2_noval = mysimilar(res1) res1_out1_noval = $op!( f, res1_in1_noval, preptup_noval..., ba, x, contexts... ) res1_out2_noval = $op!( f, res1_in2_noval, preptup_noval..., ba, x, contexts... ) - let (≈)(x, y) = isapprox(x, y; atol, rtol) - @test isempty(preptup_noval) || only(preptup_noval) isa $P - @test y_out1_val ≈ scen.y - @test y_out2_val ≈ scen.y - @test res1_in1_val === res1_out1_val - @test res1_in2_val === res1_out2_val - @test res1_out1_val ≈ scen.res1 - @test res1_out2_val ≈ scen.res1 - @test res1_in1_noval === res1_out1_noval - @test res1_in2_noval === res1_out2_noval - @test res1_out1_noval ≈ scen.res1 - @test res1_out2_noval ≈ scen.res1 - end + @test isempty(preptup_noval) || only(preptup_noval) isa $P + @test res1_in1_noval === res1_out1_noval + @test res1_in2_noval === res1_out2_noval + @test res1_out1_noval ≈ scen.res1 + @test res1_out2_noval ≈ scen.res1 if sparsity && $op == jacobian - @test mynnz(res1_out1_val) == mynnz(scen.res1) - @test mynnz(res1_out2_val) == mynnz(scen.res1) @test mynnz(res1_out1_noval) == mynnz(scen.res1) @test mynnz(res1_out2_noval) == mynnz(scen.res1) end end - @test_throws PME $val_and_op!( - nothing, mysimilar(res1), prep, ba, x, contexts... - ) - @test_throws PME $op!(nothing, mysimilar(res1), prep, ba, x, contexts...) + let prep = $prep_op(f, ba, prep_args.x, prep_args.contexts...) + @test_throws PME $val_and_op!( + nothing, mysimilar(res1), prep, ba, x, contexts... + ) + @test_throws PME $op!(nothing, mysimilar(res1), prep, ba, x, contexts...) + end scenario_intact && @test new_scen == scen return nothing end @@ -178,11 +192,11 @@ for op in ALL_OPS sparsity::Bool, reprepare::Bool, ) + ≈(x, y) = isapprox(x, y; atol, rtol) (; f, x, y, res1, contexts, prep_args) = new_scen = deepcopy(scen) - local prep preptup_cands_val, preptup_cands_noval = map(1:2) do _ - prep = $prep_op(f, prep_args.y, ba, prep_args.x, prep_args.contexts...) - prep_nostrict = $prep_op( + prep0 = $prep_op(f, prep_args.y, ba, prep_args.x, prep_args.contexts...) + prep_nostrict0 = $prep_op( f, prep_args.y, ba, @@ -194,44 +208,53 @@ for op in ALL_OPS has_size(x) && has_size(y) && (size(x) != size(prep_args.x) || size(y) != prep_args.y) - prep = $prep_op!(f, y, prep, ba, x, contexts...) - prep_nostrict = $prep_op!(f, y, prep_nostrict, ba, x, contexts...) + prep = $prep_op!(f, y, prep0, ba, x, contexts...) + prep_nostrict = $prep_op!(f, y, prep_nostrict0, ba, x, contexts...) + else + prep = prep0 + prep_nostrict = prep_nostrict0 end - [(), (prep,), (prep_nostrict,)] + ((), (prep,), (prep_nostrict,)) end - for (preptup_val, preptup_noval) in zip(preptup_cands_val, preptup_cands_noval) + foreach(preptup_cands_val) do preptup_val y_in1_val = mysimilar(y) y_in2_val = mysimilar(y) - y_in1_noval = mysimilar(y) - y_in2_noval = mysimilar(y) y_out1_val, res1_out1_val = $val_and_op( f, y_in1_val, preptup_val..., ba, x, contexts... ) y_out2_val, res1_out2_val = $val_and_op( f, y_in2_val, preptup_val..., ba, x, contexts... ) - res1_out1_noval = $op(f, y_in1_noval, preptup_noval..., ba, x, contexts...) - res1_out2_noval = $op(f, y_in2_noval, preptup_noval..., ba, x, contexts...) - let (≈)(x, y) = isapprox(x, y; atol, rtol) - @test isempty(preptup_noval) || only(preptup_noval) isa $P - @test y_in1_val === y_out1_val - @test y_in2_val === y_out2_val - @test y_out1_val ≈ scen.y - @test y_out2_val ≈ scen.y - @test res1_out1_val ≈ scen.res1 - @test res1_out2_val ≈ scen.res1 - @test res1_out1_noval ≈ scen.res1 - @test res1_out2_noval ≈ scen.res1 - end + @test y_in1_val === y_out1_val + @test y_in2_val === y_out2_val + @test y_out1_val ≈ scen.y + @test y_out2_val ≈ scen.y + @test res1_out1_val ≈ scen.res1 + @test res1_out2_val ≈ scen.res1 if sparsity && $op == jacobian @test mynnz(res1_out1_val) == mynnz(scen.res1) @test mynnz(res1_out2_val) == mynnz(scen.res1) + end + end + foreach(preptup_cands_noval) do preptup_noval + y_in1_noval = mysimilar(y) + y_in2_noval = mysimilar(y) + res1_out1_noval = $op(f, y_in1_noval, preptup_noval..., ba, x, contexts...) + res1_out2_noval = $op(f, y_in2_noval, preptup_noval..., ba, x, contexts...) + @test isempty(preptup_noval) || only(preptup_noval) isa $P + @test res1_out1_noval ≈ scen.res1 + @test res1_out2_noval ≈ scen.res1 + if sparsity && $op == jacobian @test mynnz(res1_out1_noval) == mynnz(scen.res1) @test mynnz(res1_out2_noval) == mynnz(scen.res1) end end - @test_throws PME $val_and_op(nothing, mysimilar(y), prep, ba, x, contexts...) - @test_throws PME $op(nothing, mysimilar(y), prep, ba, x, contexts...) + let prep = $prep_op(f, prep_args.y, ba, prep_args.x, prep_args.contexts...) + @test_throws PME $val_and_op( + nothing, mysimilar(y), prep, ba, x, contexts... + ) + @test_throws PME $op(nothing, mysimilar(y), prep, ba, x, contexts...) + end scenario_intact && @test new_scen == scen return nothing end @@ -246,11 +269,11 @@ for op in ALL_OPS sparsity::Bool, reprepare::Bool, ) + ≈(x, y) = isapprox(x, y; atol, rtol) (; f, x, y, res1, contexts, prep_args) = new_scen = deepcopy(scen) - local prep preptup_cands_val, preptup_cands_noval = map(1:2) do _ - prep = $prep_op(f, prep_args.y, ba, prep_args.x, prep_args.contexts...) - prep_nostrict = $prep_op( + prep0 = $prep_op(f, prep_args.y, ba, prep_args.x, prep_args.contexts...) + prep_nostrict0 = $prep_op( f, prep_args.y, ba, @@ -262,56 +285,64 @@ for op in ALL_OPS has_size(x) && has_size(y) && (size(x) != size(prep_args.x) || size(y) != prep_args.y) - prep = $prep_op!(f, y, prep, ba, x, contexts...) - prep_nostrict = $prep_op!(f, y, prep_nostrict, ba, x, contexts...) + prep = $prep_op!(f, y, prep0, ba, x, contexts...) + prep_nostrict = $prep_op!(f, y, prep_nostrict0, ba, x, contexts...) + else + prep = prep0 + prep_nostrict = prep_nostrict0 end - [(), (prep,), (prep_nostrict,)] + ((), (prep,), (prep_nostrict,)) end - for (preptup_val, preptup_noval) in zip(preptup_cands_val, preptup_cands_noval) + foreach(preptup_cands_val) do preptup_val y_in1_val, res1_in1_val = mysimilar(y), mysimilar(res1) y_in2_val, res1_in2_val = mysimilar(y), mysimilar(res1) - y_in1_noval, res1_in1_noval = mysimilar(y), mysimilar(res1) - y_in2_noval, res1_in2_noval = mysimilar(y), mysimilar(res1) y_out1_val, res1_out1_val = $val_and_op!( f, y_in1_val, res1_in1_val, preptup_val..., ba, x, contexts... ) y_out2_val, res1_out2_val = $val_and_op!( f, y_in2_val, res1_in2_val, preptup_val..., ba, x, contexts... ) + @test isempty(preptup_noval) || only(preptup_noval) isa $P + @test y_in1_val === y_out1_val + @test y_in2_val === y_out2_val + @test y_out1_val ≈ scen.y + @test y_out2_val ≈ scen.y + @test res1_in1_val === res1_out1_val + @test res1_in2_val === res1_out2_val + @test res1_out1_val ≈ scen.res1 + @test res1_out2_val ≈ scen.res1 + if sparsity && $op == jacobian + @test mynnz(res1_out1_val) == mynnz(scen.res1) + @test mynnz(res1_out2_val) == mynnz(scen.res1) + end + end + foreach(preptup_cands_noval) do preptup_noval + y_in1_noval, res1_in1_noval = mysimilar(y), mysimilar(res1) + y_in2_noval, res1_in2_noval = mysimilar(y), mysimilar(res1) res1_out1_noval = $op!( f, y_in1_noval, res1_in1_noval, preptup_noval..., ba, x, contexts... ) res1_out2_noval = $op!( f, y_in2_noval, res1_in2_noval, preptup_noval..., ba, x, contexts... ) - let (≈)(x, y) = isapprox(x, y; atol, rtol) - @test isempty(preptup_noval) || only(preptup_noval) isa $P - @test y_in1_val === y_out1_val - @test y_in2_val === y_out2_val - @test y_out1_val ≈ scen.y - @test y_out2_val ≈ scen.y - @test res1_in1_val === res1_out1_val - @test res1_in2_val === res1_out2_val - @test res1_out1_val ≈ scen.res1 - @test res1_out2_val ≈ scen.res1 - @test res1_in1_noval === res1_out1_noval - @test res1_in2_noval === res1_out2_noval - @test res1_out1_noval ≈ scen.res1 - @test res1_out2_noval ≈ scen.res1 - end + @test isempty(preptup_noval) || only(preptup_noval) isa $P + @test res1_in1_noval === res1_out1_noval + @test res1_in2_noval === res1_out2_noval + @test res1_out1_noval ≈ scen.res1 + @test res1_out2_noval ≈ scen.res1 if sparsity && $op == jacobian - @test mynnz(res1_out1_val) == mynnz(scen.res1) - @test mynnz(res1_out2_val) == mynnz(scen.res1) @test mynnz(res1_out1_noval) == mynnz(scen.res1) @test mynnz(res1_out2_noval) == mynnz(scen.res1) end end - @test_throws PME $val_and_op!( - nothing, mysimilar(y), mysimilar(res1), prep, ba, x, contexts... - ) - @test_throws PME $op!( - nothing, mysimilar(y), mysimilar(res1), prep, ba, x, contexts... - ) + let prep = $prep_op(f, prep_args.y, ba, prep_args.x, prep_args.contexts...) + @test_throws PME $val_and_op!( + nothing, mysimilar(y), mysimilar(res1), prep, ba, x, contexts... + ) + @test_throws PME $op!( + nothing, mysimilar(y), mysimilar(res1), prep, ba, x, contexts... + ) + end scenario_intact && @test new_scen == scen return nothing end @@ -327,18 +358,21 @@ for op in ALL_OPS sparsity::Bool, reprepare::Bool, ) + ≈(x, y) = isapprox(x, y; atol, rtol) (; f, x, y, res1, res2, contexts, prep_args) = new_scen = deepcopy(scen) - local prep preptup_cands_val, preptup_cands_noval = map(1:2) do _ - prep = $prep_op(f, ba, prep_args.x, prep_args.contexts...) - prep_nostrict = $prep_op( + prep0 = $prep_op(f, ba, prep_args.x, prep_args.contexts...) + prep_nostrict0 = $prep_op( f, ba, prep_args.x, prep_args.contexts...; strict=Val(false) ) if reprepare && has_size(x) && has_size(y) && (size(x) != size(prep_args.x)) - prep = $prep_op!(f, prep, ba, x, contexts...) - prep_nostrict = $prep_op!(f, prep_nostrict, ba, x, contexts...) + prep = $prep_op!(f, prep0, ba, x, contexts...) + prep_nostrict = $prep_op!(f, prep_nostrict0, ba, x, contexts...) + else + prep = prep0 + prep_nostrict = prep_nostrict0 end - [(), (prep,), (prep_nostrict,)] + ((), (prep,), (prep_nostrict,)) end for (preptup_val, preptup_noval) in zip(preptup_cands_val, preptup_cands_noval) y_out1_val, res1_out1_val, res2_out1_val = $val_and_op( @@ -349,17 +383,15 @@ for op in ALL_OPS ) res2_out1_noval = $op(f, preptup_noval..., ba, x, contexts...) res2_out2_noval = $op(f, preptup_noval..., ba, x, contexts...) - let (≈)(x, y) = isapprox(x, y; atol, rtol) - @test isempty(preptup_noval) || only(preptup_noval) isa $P - @test y_out1_val ≈ scen.y - @test y_out2_val ≈ scen.y - @test res1_out1_val ≈ scen.res1 - @test res1_out2_val ≈ scen.res1 - @test res2_out1_val ≈ scen.res2 - @test res2_out2_val ≈ scen.res2 - @test res2_out1_noval ≈ scen.res2 - @test res2_out2_noval ≈ scen.res2 - end + @test isempty(preptup_noval) || only(preptup_noval) isa $P + @test y_out1_val ≈ scen.y + @test y_out2_val ≈ scen.y + @test res1_out1_val ≈ scen.res1 + @test res1_out2_val ≈ scen.res1 + @test res2_out1_val ≈ scen.res2 + @test res2_out2_val ≈ scen.res2 + @test res2_out1_noval ≈ scen.res2 + @test res2_out2_noval ≈ scen.res2 if sparsity && $op == hessian @test mynnz(res2_out1_val) == mynnz(scen.res2) @test mynnz(res2_out2_val) == mynnz(scen.res2) @@ -367,8 +399,10 @@ for op in ALL_OPS @test mynnz(res2_out2_noval) == mynnz(scen.res2) end end - @test_throws PME $val_and_op(nothing, prep, ba, x, contexts...) - @test_throws PME $op(nothing, prep, ba, x, contexts...) + let prep = $prep_op(f, ba, prep_args.x, prep_args.contexts...) + @test_throws PME $val_and_op(nothing, prep, ba, x, contexts...) + @test_throws PME $op(nothing, prep, ba, x, contexts...) + end scenario_intact && @test new_scen == scen return nothing end @@ -383,18 +417,21 @@ for op in ALL_OPS sparsity::Bool, reprepare::Bool, ) + ≈(x, y) = isapprox(x, y; atol, rtol) (; f, x, y, res1, res2, contexts, prep_args) = new_scen = deepcopy(scen) - local prep preptup_cands_val, preptup_cands_noval = map(1:2) do _ - prep = $prep_op(f, ba, prep_args.x, prep_args.contexts...) - prep_nostrict = $prep_op( + prep0 = $prep_op(f, ba, prep_args.x, prep_args.contexts...) + prep_nostrict0 = $prep_op( f, ba, prep_args.x, prep_args.contexts...; strict=Val(false) ) if reprepare && has_size(x) && has_size(y) && (size(x) != size(prep_args.x)) - prep = $prep_op!(f, prep, ba, x, contexts...) - prep_nostrict = $prep_op!(f, prep_nostrict, ba, x, contexts...) + prep = $prep_op!(f, prep0, ba, x, contexts...) + prep_nostrict = $prep_op!(f, prep_nostrict0, ba, x, contexts...) + else + prep = prep0 + prep_nostrict = prep_nostrict0 end - [(), (prep,), (prep_nostrict,)] + ((), (prep,), (prep_nostrict,)) end for (preptup_val, preptup_noval) in zip(preptup_cands_val, preptup_cands_noval) res1_in1_val, res2_in1_val = mysimilar(res1), mysimilar(res2) @@ -413,23 +450,21 @@ for op in ALL_OPS res2_out2_noval = $op!( f, res2_in2_noval, preptup_noval..., ba, x, contexts... ) - let (≈)(x, y) = isapprox(x, y; atol, rtol) - @test isempty(preptup_noval) || only(preptup_noval) isa $P - @test y_out1_val ≈ scen.y - @test y_out2_val ≈ scen.y - @test res1_in1_val === res1_out1_val - @test res1_in2_val === res1_out2_val - @test res1_out1_val ≈ scen.res1 - @test res1_out2_val ≈ scen.res1 - @test res2_in1_val === res2_out1_val - @test res2_in2_val === res2_out2_val - @test res2_out1_val ≈ scen.res2 - @test res2_out2_val ≈ scen.res2 - @test res2_in1_noval === res2_out1_noval - @test res2_in2_noval === res2_out2_noval - @test res2_out1_noval ≈ scen.res2 - @test res2_out2_noval ≈ scen.res2 - end + @test isempty(preptup_noval) || only(preptup_noval) isa $P + @test y_out1_val ≈ scen.y + @test y_out2_val ≈ scen.y + @test res1_in1_val === res1_out1_val + @test res1_in2_val === res1_out2_val + @test res1_out1_val ≈ scen.res1 + @test res1_out2_val ≈ scen.res1 + @test res2_in1_val === res2_out1_val + @test res2_in2_val === res2_out2_val + @test res2_out1_val ≈ scen.res2 + @test res2_out2_val ≈ scen.res2 + @test res2_in1_noval === res2_out1_noval + @test res2_in2_noval === res2_out2_noval + @test res2_out1_noval ≈ scen.res2 + @test res2_out2_noval ≈ scen.res2 if sparsity && $op == hessian @test mynnz(res2_out1_val) == mynnz(scen.res2) @test mynnz(res2_out2_val) == mynnz(scen.res2) @@ -437,10 +472,12 @@ for op in ALL_OPS @test mynnz(res2_out2_noval) == mynnz(scen.res2) end end - @test_throws PME $val_and_op!( - nothing, mysimilar(res1), mysimilar(res2), prep, ba, x, contexts... - ) - @test_throws PME $op!(nothing, mysimilar(res2), prep, ba, x, contexts...) + let prep = $prep_op(f, ba, prep_args.x, prep_args.contexts...) + @test_throws PME $val_and_op!( + nothing, mysimilar(res1), mysimilar(res2), prep, ba, x, contexts... + ) + @test_throws PME $op!(nothing, mysimilar(res2), prep, ba, x, contexts...) + end scenario_intact && @test new_scen == scen return nothing end @@ -456,11 +493,11 @@ for op in ALL_OPS sparsity::Bool, reprepare::Bool, ) + ≈(x, y) = isapprox(x, y; atol, rtol) (; f, x, y, t, res1, contexts, prep_args) = new_scen = deepcopy(scen) - local prep preptup_cands_val, preptup_cands_noval = map(1:2) do _ - prep = $prep_op(f, ba, prep_args.x, prep_args.t, prep_args.contexts...) - prep_nostrict = $prep_op( + prep0 = $prep_op(f, ba, prep_args.x, prep_args.t, prep_args.contexts...) + prep_nostrict0 = $prep_op( f, ba, prep_args.x, @@ -470,34 +507,41 @@ for op in ALL_OPS ) prep_same = $prep_op_same(f, ba, x, map(zero, t), contexts...) if reprepare && has_size(x) && has_size(y) && (size(x) != size(prep_args.x)) - prep = $prep_op!(f, prep, ba, x, t, contexts...) - prep_nostrict = $prep_op!(f, prep_nostrict, ba, x, t, contexts...) + prep = $prep_op!(f, prep0, ba, x, t, contexts...) + prep_nostrict = $prep_op!(f, prep_nostrict0, ba, x, t, contexts...) + else + prep = prep0 + prep_nostrict = prep_nostrict0 end - [(), (prep,), (prep_nostrict,), (prep_same,)] + ((), (prep,), (prep_nostrict,), (prep_same,)) end - for (preptup_val, preptup_noval) in zip(preptup_cands_val, preptup_cands_noval) + foreach(preptup_cands_val) do preptup_val y_out1_val, res1_out1_val = $val_and_op( f, preptup_val..., ba, x, t, contexts... ) y_out2_val, res1_out2_val = $val_and_op( f, preptup_val..., ba, x, t, contexts... ) + @test y_out1_val ≈ scen.y + @test y_out2_val ≈ scen.y + for b in eachindex(scen.res1) + @test res1_out1_val[b] ≈ scen.res1[b] + @test res1_out2_val[b] ≈ scen.res1[b] + end + end + foreach(preptup_cands_noval) do preptup_noval res1_out1_noval = $op(f, preptup_noval..., ba, x, t, contexts...) res1_out2_noval = $op(f, preptup_noval..., ba, x, t, contexts...) - let (≈)(x, y) = isapprox(x, y; atol, rtol) - @test isempty(preptup_noval) || only(preptup_noval) isa $P - @test y_out1_val ≈ scen.y - @test y_out2_val ≈ scen.y - for b in eachindex(scen.res1) - res1_out1_val[b] ≈ scen.res1[b] - res1_out2_val[b] ≈ scen.res1[b] - res1_out1_noval[b] ≈ scen.res1[b] - res1_out2_noval[b] ≈ scen.res1[b] - end - end - end - @test_throws PME $val_and_op(nothing, prep, ba, x, t, contexts...) - @test_throws PME $op(nothing, prep, ba, x, t, contexts...) + @test isempty(preptup_noval) || only(preptup_noval) isa $P + for b in eachindex(scen.res1) + @test res1_out1_noval[b] ≈ scen.res1[b] + @test res1_out2_noval[b] ≈ scen.res1[b] + end + end + let prep = $prep_op(f, ba, prep_args.x, prep_args.t, prep_args.contexts...) + @test_throws PME $val_and_op(nothing, prep, ba, x, t, contexts...) + @test_throws PME $op(nothing, prep, ba, x, t, contexts...) + end scenario_intact && @test new_scen == scen return nothing end @@ -512,11 +556,11 @@ for op in ALL_OPS sparsity::Bool, reprepare::Bool, ) + ≈(x, y) = isapprox(x, y; atol, rtol) (; f, x, y, t, res1, contexts, prep_args) = new_scen = deepcopy(scen) - local prep preptup_cands_val, preptup_cands_noval = map(1:2) do _ - prep = $prep_op(f, ba, prep_args.x, prep_args.t, prep_args.contexts...) - prep_nostrict = $prep_op( + prep0 = $prep_op(f, ba, prep_args.x, prep_args.t, prep_args.contexts...) + prep_nostrict0 = $prep_op( f, ba, prep_args.x, @@ -526,48 +570,55 @@ for op in ALL_OPS ) prep_same = $prep_op_same(f, ba, x, map(zero, t), contexts...) if reprepare && has_size(x) && has_size(y) && (size(x) != size(prep_args.x)) - prep = $prep_op!(f, prep, ba, x, t, contexts...) - prep_nostrict = $prep_op!(f, prep_nostrict, ba, x, t, contexts...) + prep = $prep_op!(f, prep0, ba, x, t, contexts...) + prep_nostrict = $prep_op!(f, prep_nostrict0, ba, x, t, contexts...) + else + prep = prep0 + prep_nostrict = prep_nostrict0 end - [(), (prep,), (prep_nostrict,), (prep_same,)] + ((), (prep,), (prep_nostrict,), (prep_same,)) end - for (preptup_val, preptup_noval) in zip(preptup_cands_val, preptup_cands_noval) + foreach(preptup_cands_val) do preptup_val res1_in1_val = mysimilar(res1) res1_in2_val = mysimilar(res1) - res1_in1_noval = mysimilar(res1) - res1_in2_noval = mysimilar(res1) y_out1_val, res1_out1_val = $val_and_op!( f, res1_in1_val, preptup_val..., ba, x, t, contexts... ) y_out2_val, res1_out2_val = $val_and_op!( f, res1_in2_val, preptup_val..., ba, x, t, contexts... ) + @test y_out1_val ≈ scen.y + @test y_out2_val ≈ scen.y + @test res1_in1_val === res1_out1_val + @test res1_in2_val === res1_out2_val + for b in eachindex(scen.res1) + @test res1_out1_val[b] ≈ scen.res1[b] + @test res1_out2_val[b] ≈ scen.res1[b] + end + end + foreach(preptup_cands_noval) do preptup_noval + res1_in1_noval = mysimilar(res1) + res1_in2_noval = mysimilar(res1) res1_out1_noval = $op!( f, res1_in1_noval, preptup_noval..., ba, x, t, contexts... ) res1_out2_noval = $op!( f, res1_in2_noval, preptup_noval..., ba, x, t, contexts... ) - let (≈)(x, y) = isapprox(x, y; atol, rtol) - @test isempty(preptup_noval) || only(preptup_noval) isa $P - @test y_out1_val ≈ scen.y - @test y_out2_val ≈ scen.y - @test res1_in1_val === res1_out1_val - @test res1_in2_val === res1_out2_val - @test res1_in1_noval === res1_out1_noval - @test res1_in2_noval === res1_out2_noval - for b in eachindex(scen.res1) - @test res1_out1_val[b] ≈ scen.res1[b] - @test res1_out2_val[b] ≈ scen.res1[b] - @test res1_out1_noval[b] ≈ scen.res1[b] - @test res1_out2_noval[b] ≈ scen.res1[b] - end - end - end - @test_throws PME $val_and_op!( - nothing, mysimilar(res1), prep, ba, x, t, contexts... - ) - @test_throws PME $op!(nothing, mysimilar(res1), prep, ba, x, t, contexts...) + @test isempty(preptup_noval) || only(preptup_noval) isa $P + @test res1_in1_noval === res1_out1_noval + @test res1_in2_noval === res1_out2_noval + for b in eachindex(scen.res1) + @test res1_out1_noval[b] ≈ scen.res1[b] + @test res1_out2_noval[b] ≈ scen.res1[b] + end + end + let prep = $prep_op(f, ba, prep_args.x, prep_args.t, prep_args.contexts...) + @test_throws PME $val_and_op!( + nothing, mysimilar(res1), prep, ba, x, t, contexts... + ) + @test_throws PME $op!(nothing, mysimilar(res1), prep, ba, x, t, contexts...) + end scenario_intact && @test new_scen == scen return nothing end @@ -582,13 +633,13 @@ for op in ALL_OPS sparsity::Bool, reprepare::Bool, ) + ≈(x, y) = isapprox(x, y; atol, rtol) (; f, x, y, t, res1, contexts, prep_args) = new_scen = deepcopy(scen) - local prep preptup_cands_val, preptup_cands_noval = map(1:2) do _ - prep = $prep_op( + prep0 = $prep_op( f, prep_args.y, ba, prep_args.x, prep_args.t, prep_args.contexts... ) - prep_nostrict = $prep_op( + prep_nostrict0 = $prep_op( f, prep_args.y, ba, @@ -602,44 +653,55 @@ for op in ALL_OPS has_size(x) && has_size(y) && (size(x) != size(prep_args.x) || size(y) != prep_args.y) - prep = $prep_op!(f, y, prep, ba, x, t, contexts...) - prep_nostrict = $prep_op!(f, y, prep_nostrict, ba, x, t, contexts...) + prep = $prep_op!(f, y, prep0, ba, x, t, contexts...) + prep_nostrict = $prep_op!(f, y, prep_nostrict0, ba, x, t, contexts...) + else + prep = prep0 + prep_nostrict = prep_nostrict0 end - [(), (prep,), (prep_nostrict,), (prep_same,)] + ((), (prep,), (prep_nostrict,), (prep_same,)) end - for (preptup_val, preptup_noval) in zip(preptup_cands_val, preptup_cands_noval) + foreach(preptup_cands_val) do preptup_val y_in1_val = mysimilar(y) y_in2_val = mysimilar(y) - y_in1_noval = mysimilar(y) - y_in2_noval = mysimilar(y) y_out1_val, res1_out1_val = $val_and_op( f, y_in1_val, preptup_val..., ba, x, t, contexts... ) y_out2_val, res1_out2_val = $val_and_op( f, y_in2_val, preptup_val..., ba, x, t, contexts... ) + @test y_in1_val === y_out1_val + @test y_in2_val === y_out2_val + @test y_out1_val ≈ scen.y + @test y_out2_val ≈ scen.y + for b in eachindex(scen.res1) + @test res1_out1_val[b] ≈ scen.res1[b] + @test res1_out2_val[b] ≈ scen.res1[b] + end + end + foreach(preptup_cands_noval) do preptup_noval + y_in1_noval = mysimilar(y) + y_in2_noval = mysimilar(y) res1_out1_noval = $op( f, y_in1_noval, preptup_noval..., ba, x, t, contexts... ) res1_out2_noval = $op( f, y_in2_noval, preptup_noval..., ba, x, t, contexts... ) - let (≈)(x, y) = isapprox(x, y; atol, rtol) - @test isempty(preptup_noval) || only(preptup_noval) isa $P - @test y_in1_val === y_out1_val - @test y_in2_val === y_out2_val - @test y_out1_val ≈ scen.y - @test y_out2_val ≈ scen.y - for b in eachindex(scen.res1) - @test res1_out1_val[b] ≈ scen.res1[b] - @test res1_out2_val[b] ≈ scen.res1[b] - @test res1_out1_noval[b] ≈ scen.res1[b] - @test res1_out2_noval[b] ≈ scen.res1[b] - end - end - end - @test_throws PME $val_and_op(nothing, mysimilar(y), prep, ba, x, t, contexts...) - @test_throws PME $op(nothing, mysimilar(y), prep, ba, x, t, contexts...) + @test isempty(preptup_noval) || only(preptup_noval) isa $P + for b in eachindex(scen.res1) + @test res1_out1_noval[b] ≈ scen.res1[b] + @test res1_out2_noval[b] ≈ scen.res1[b] + end + end + let prep = $prep_op( + f, prep_args.y, ba, prep_args.x, prep_args.t, prep_args.contexts... + ) + @test_throws PME $val_and_op( + nothing, mysimilar(y), prep, ba, x, t, contexts... + ) + @test_throws PME $op(nothing, mysimilar(y), prep, ba, x, t, contexts...) + end scenario_intact && @test new_scen == scen return nothing end @@ -654,13 +716,13 @@ for op in ALL_OPS sparsity::Bool, reprepare::Bool, ) + ≈(x, y) = isapprox(x, y; atol, rtol) (; f, x, y, t, res1, contexts, prep_args) = new_scen = deepcopy(scen) - local prep preptup_cands_val, preptup_cands_noval = map(1:2) do _ - prep = $prep_op( + prep0 = $prep_op( f, prep_args.y, ba, prep_args.x, prep_args.t, prep_args.contexts... ) - prep_nostrict = $prep_op( + prep_nostrict0 = $prep_op( f, prep_args.y, ba, @@ -674,52 +736,61 @@ for op in ALL_OPS has_size(x) && has_size(y) && (size(x) != size(prep_args.x) || size(y) != prep_args.y) - prep = $prep_op!(f, y, prep, ba, x, t, contexts...) - prep_nostrict = $prep_op!(f, y, prep_nostrict, ba, x, t, contexts...) + prep = $prep_op!(f, y, prep0, ba, x, t, contexts...) + prep_nostrict = $prep_op!(f, y, prep_nostrict0, ba, x, t, contexts...) + else + prep = prep0 + prep_nostrict = prep_nostrict0 end - [(), (prep,), (prep_nostrict,), (prep_same,)] + ((), (prep,), (prep_nostrict,), (prep_same,)) end - for (preptup_val, preptup_noval) in zip(preptup_cands_val, preptup_cands_noval) + foreach(preptup_cands_val) do preptup_val y_in1_val, res1_in1_val = mysimilar(y), mysimilar(res1) y_in2_val, res1_in2_val = mysimilar(y), mysimilar(res1) - y_in1_noval, res1_in1_noval = mysimilar(y), mysimilar(res1) - y_in2_noval, res1_in2_noval = mysimilar(y), mysimilar(res1) y_out1_val, res1_out1_val = $val_and_op!( f, y_in1_val, res1_in1_val, preptup_val..., ba, x, t, contexts... ) y_out2_val, res1_out2_val = $val_and_op!( f, y_in2_val, res1_in2_val, preptup_val..., ba, x, t, contexts... ) + @test y_in1_val === y_out1_val + @test y_in2_val === y_out2_val + @test y_out1_val ≈ scen.y + @test y_out2_val ≈ scen.y + @test res1_in1_val === res1_out1_val + @test res1_in2_val === res1_out2_val + for b in eachindex(scen.res1) + @test res1_out1_val[b] ≈ scen.res1[b] + @test res1_out2_val[b] ≈ scen.res1[b] + end + end + foreach(preptup_cands_noval) do preptup_noval + y_in1_noval, res1_in1_noval = mysimilar(y), mysimilar(res1) + y_in2_noval, res1_in2_noval = mysimilar(y), mysimilar(res1) res1_out1_noval = $op!( f, y_in1_noval, res1_in1_noval, preptup_noval..., ba, x, t, contexts... ) res1_out2_noval = $op!( f, y_in2_noval, res1_in2_noval, preptup_noval..., ba, x, t, contexts... ) - let (≈)(x, y) = isapprox(x, y; atol, rtol) - @test isempty(preptup_noval) || only(preptup_noval) isa $P - @test y_in1_val === y_out1_val - @test y_in2_val === y_out2_val - @test y_out1_val ≈ scen.y - @test y_out2_val ≈ scen.y - @test res1_in1_val === res1_out1_val - @test res1_in2_val === res1_out2_val - @test res1_in1_noval === res1_out1_noval - @test res1_in2_noval === res1_out2_noval - for b in eachindex(scen.res1) - @test res1_out1_val[b] ≈ scen.res1[b] - @test res1_out2_val[b] ≈ scen.res1[b] - @test res1_out1_noval[b] ≈ scen.res1[b] - @test res1_out2_noval[b] ≈ scen.res1[b] - end - end - end - @test_throws PME $val_and_op!( - nothing, mysimilar(y), mysimilar(res1), prep, ba, x, t, contexts... - ) - @test_throws PME $op!( - nothing, mysimilar(y), mysimilar(res1), prep, ba, x, t, contexts... - ) + @test isempty(preptup_noval) || only(preptup_noval) isa $P + @test res1_in1_noval === res1_out1_noval + @test res1_in2_noval === res1_out2_noval + for b in eachindex(scen.res1) + @test res1_out1_noval[b] ≈ scen.res1[b] + @test res1_out2_noval[b] ≈ scen.res1[b] + end + end + let prep = $prep_op( + f, prep_args.y, ba, prep_args.x, prep_args.t, prep_args.contexts... + ) + @test_throws PME $val_and_op!( + nothing, mysimilar(y), mysimilar(res1), prep, ba, x, t, contexts... + ) + @test_throws PME $op!( + nothing, mysimilar(y), mysimilar(res1), prep, ba, x, t, contexts... + ) + end scenario_intact && @test new_scen == scen return nothing end @@ -735,11 +806,11 @@ for op in ALL_OPS sparsity::Bool, reprepare::Bool, ) + ≈(x, y) = isapprox(x, y; atol, rtol) (; f, x, y, t, res1, res2, contexts, prep_args) = new_scen = deepcopy(scen) - local prep preptup_cands_val, preptup_cands_noval = map(1:2) do _ - prep = $prep_op(f, ba, prep_args.x, prep_args.t, prep_args.contexts...) - prep_nostrict = $prep_op( + prep0 = $prep_op(f, ba, prep_args.x, prep_args.t, prep_args.contexts...) + prep_nostrict0 = $prep_op( f, ba, prep_args.x, @@ -749,10 +820,13 @@ for op in ALL_OPS ) prep_same = $prep_op_same(f, ba, x, map(zero, t), contexts...) if reprepare && has_size(x) && has_size(y) && (size(x) != size(prep_args.x)) - prep = $prep_op!(f, prep, ba, x, t, contexts...) - prep_nostrict = $prep_op!(f, prep_nostrict, ba, x, t, contexts...) + prep = $prep_op!(f, prep0, ba, x, t, contexts...) + prep_nostrict = $prep_op!(f, prep_nostrict0, ba, x, t, contexts...) + else + prep = prep0 + prep_nostrict = prep_nostrict0 end - [(), (prep,), (prep_nostrict,), (prep_same,)] + ((), (prep,), (prep_nostrict,), (prep_same,)) end for (preptup_val, preptup_noval) in zip(preptup_cands_val, preptup_cands_noval) res2_out1_noval = $op(f, preptup_noval..., ba, x, t, contexts...) @@ -763,20 +837,20 @@ for op in ALL_OPS res1_out2_val, res2_out2_val = $val_and_op( f, preptup_noval..., ba, x, t, contexts... ) - let (≈)(x, y) = isapprox(x, y; atol, rtol) - @test isempty(preptup_noval) || only(preptup_noval) isa $P - @test res1_out1_val ≈ scen.res1 - @test res1_out2_val ≈ scen.res1 - for b in eachindex(scen.res2) - @test res2_out1_noval[b] ≈ scen.res2[b] - @test res2_out2_noval[b] ≈ scen.res2[b] - @test res2_out1_val[b] ≈ scen.res2[b] - @test res2_out2_val[b] ≈ scen.res2[b] - end + @test isempty(preptup_noval) || only(preptup_noval) isa $P + @test res1_out1_val ≈ scen.res1 + @test res1_out2_val ≈ scen.res1 + for b in eachindex(scen.res2) + @test res2_out1_noval[b] ≈ scen.res2[b] + @test res2_out2_noval[b] ≈ scen.res2[b] + @test res2_out1_val[b] ≈ scen.res2[b] + @test res2_out2_val[b] ≈ scen.res2[b] end end - @test_throws PME $val_and_op(nothing, prep, ba, x, t, contexts...) - @test_throws PME $op(nothing, prep, ba, x, t, contexts...) + let prep = $prep_op(f, ba, prep_args.x, prep_args.t, prep_args.contexts...) + @test_throws PME $val_and_op(nothing, prep, ba, x, t, contexts...) + @test_throws PME $op(nothing, prep, ba, x, t, contexts...) + end scenario_intact && @test new_scen == scen return nothing end @@ -791,11 +865,11 @@ for op in ALL_OPS sparsity::Bool, reprepare::Bool, ) + ≈(x, y) = isapprox(x, y; atol, rtol) (; f, x, y, t, res1, res2, contexts, prep_args) = new_scen = deepcopy(scen) - local prep preptup_cands_val, preptup_cands_noval = map(1:2) do _ - prep = $prep_op(f, ba, prep_args.x, prep_args.t, prep_args.contexts...) - prep_nostrict = $prep_op( + prep0 = $prep_op(f, ba, prep_args.x, prep_args.t, prep_args.contexts...) + prep_nostrict0 = $prep_op( f, ba, prep_args.x, @@ -805,10 +879,13 @@ for op in ALL_OPS ) prep_same = $prep_op_same(f, ba, x, map(zero, t), contexts...) if reprepare && has_size(x) && has_size(y) && (size(x) != size(prep_args.x)) - prep = $prep_op!(f, prep, ba, x, t, contexts...) - prep_nostrict = $prep_op!(f, prep_nostrict, ba, x, t, contexts...) + prep = $prep_op!(f, prep0, ba, x, t, contexts...) + prep_nostrict = $prep_op!(f, prep_nostrict0, ba, x, t, contexts...) + else + prep = prep0 + prep_nostrict = prep_nostrict0 end - [(), (prep,), (prep_nostrict,), (prep_same,)] + ((), (prep,), (prep_nostrict,), (prep_same,)) end for (preptup_val, preptup_noval) in zip(preptup_cands_val, preptup_cands_noval) res2_in1_noval = mysimilar(res2) @@ -827,28 +904,28 @@ for op in ALL_OPS res1_out2_val, res2_out2_val = $val_and_op!( f, res1_in2_val, res2_in2_val, preptup_noval..., ba, x, t, contexts... ) - let (≈)(x, y) = isapprox(x, y; atol, rtol) - @test isempty(preptup_noval) || only(preptup_noval) isa $P - @test res1_in1_val === res1_out1_val - @test res1_in2_val === res1_out2_val - @test res1_out1_val ≈ scen.res1 - @test res1_out2_val ≈ scen.res1 - @test res2_in1_noval === res2_out1_noval - @test res2_in2_noval === res2_out2_noval - @test res2_in1_val === res2_out1_val - @test res2_in2_val === res2_out2_val - for b in eachindex(scen.res2) - @test res2_out1_noval[b] ≈ scen.res2[b] - @test res2_out2_noval[b] ≈ scen.res2[b] - @test res2_out1_val[b] ≈ scen.res2[b] - @test res2_out2_val[b] ≈ scen.res2[b] - end - end - end - @test_throws PME $op!(nothing, mysimilar(res2), prep, ba, x, t, contexts...) - @test_throws PME $val_and_op!( - nothing, mysimilar(res1), mysimilar(res2), prep, ba, x, t, contexts... - ) + @test isempty(preptup_noval) || only(preptup_noval) isa $P + @test res1_in1_val === res1_out1_val + @test res1_in2_val === res1_out2_val + @test res1_out1_val ≈ scen.res1 + @test res1_out2_val ≈ scen.res1 + @test res2_in1_noval === res2_out1_noval + @test res2_in2_noval === res2_out2_noval + @test res2_in1_val === res2_out1_val + @test res2_in2_val === res2_out2_val + for b in eachindex(scen.res2) + @test res2_out1_noval[b] ≈ scen.res2[b] + @test res2_out2_noval[b] ≈ scen.res2[b] + @test res2_out1_val[b] ≈ scen.res2[b] + @test res2_out2_val[b] ≈ scen.res2[b] + end + end + let prep = $prep_op(f, ba, prep_args.x, prep_args.t, prep_args.contexts...) + @test_throws PME $op!(nothing, mysimilar(res2), prep, ba, x, t, contexts...) + @test_throws PME $val_and_op!( + nothing, mysimilar(res1), mysimilar(res2), prep, ba, x, t, contexts... + ) + end scenario_intact && @test new_scen == scen return nothing end From 2cfb3ceaa9e9506d91f717a64cd8c250b08b137b Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Fri, 29 Aug 2025 12:47:20 +0200 Subject: [PATCH 2/6] Add some fixes --- DifferentiationInterfaceTest/Project.toml | 9 +- ...DifferentiationInterfaceTestJLArraysExt.jl | 5 + ...erentiationInterfaceTestStaticArraysExt.jl | 5 + .../src/DifferentiationInterfaceTest.jl | 17 +- .../src/test_differentiation.jl | 1 + .../src/tests/correctness_eval.jl | 183 ++++++------------ .../src/tests/prep_eval.jl | 177 +++++++++++++++++ 7 files changed, 265 insertions(+), 132 deletions(-) create mode 100644 DifferentiationInterfaceTest/src/tests/prep_eval.jl diff --git a/DifferentiationInterfaceTest/Project.toml b/DifferentiationInterfaceTest/Project.toml index 0c0771166..28a1efa07 100644 --- a/DifferentiationInterfaceTest/Project.toml +++ b/DifferentiationInterfaceTest/Project.toml @@ -12,6 +12,7 @@ DifferentiationInterface = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a" ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" @@ -34,12 +35,7 @@ Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" DifferentiationInterfaceTestComponentArraysExt = "ComponentArrays" DifferentiationInterfaceTestFluxExt = ["FiniteDifferences", "Flux", "Functors"] DifferentiationInterfaceTestJLArraysExt = "JLArrays" -DifferentiationInterfaceTestLuxExt = [ - "ComponentArrays", - "ForwardDiff", - "Lux", - "LuxTestUtils", -] +DifferentiationInterfaceTestLuxExt = ["ComponentArrays", "ForwardDiff", "Lux", "LuxTestUtils"] DifferentiationInterfaceTestStaticArraysExt = "StaticArrays" [compat] @@ -59,6 +55,7 @@ JLArrays = "0.1, 0.2" LinearAlgebra = "1" Lux = "1.1.0" LuxTestUtils = "1.3.1" +PrecompileTools = "1.2.1" ProgressMeter = "1" Random = "1" SparseArrays = "1" diff --git a/DifferentiationInterfaceTest/ext/DifferentiationInterfaceTestJLArraysExt/DifferentiationInterfaceTestJLArraysExt.jl b/DifferentiationInterfaceTest/ext/DifferentiationInterfaceTestJLArraysExt/DifferentiationInterfaceTestJLArraysExt.jl index 9800889f9..c2b3755fc 100644 --- a/DifferentiationInterfaceTest/ext/DifferentiationInterfaceTestJLArraysExt/DifferentiationInterfaceTestJLArraysExt.jl +++ b/DifferentiationInterfaceTest/ext/DifferentiationInterfaceTestJLArraysExt/DifferentiationInterfaceTestJLArraysExt.jl @@ -3,6 +3,7 @@ module DifferentiationInterfaceTestJLArraysExt import DifferentiationInterface as DI import DifferentiationInterfaceTest as DIT using JLArrays: JLArray, JLVector, JLMatrix, jl +using PrecompileTools: @compile_workload jl_num_to_vec(x::Number) = sin.(jl([1, 2]) .* x) jl_num_to_mat(x::Number) = hcat(jl_num_to_vec(x), jl_num_to_vec(3x)) @@ -42,4 +43,8 @@ function DIT.gpu_scenarios(args...; kwargs...) return myjl.(scens) end +@compile_workload begin + DIT.gpu_scenarios(; include_constantified=true, include_cachified=true) +end + end diff --git a/DifferentiationInterfaceTest/ext/DifferentiationInterfaceTestStaticArraysExt/DifferentiationInterfaceTestStaticArraysExt.jl b/DifferentiationInterfaceTest/ext/DifferentiationInterfaceTestStaticArraysExt/DifferentiationInterfaceTestStaticArraysExt.jl index af862f81b..c1f28c8ec 100644 --- a/DifferentiationInterfaceTest/ext/DifferentiationInterfaceTestStaticArraysExt/DifferentiationInterfaceTestStaticArraysExt.jl +++ b/DifferentiationInterfaceTest/ext/DifferentiationInterfaceTestStaticArraysExt/DifferentiationInterfaceTestStaticArraysExt.jl @@ -4,6 +4,7 @@ import DifferentiationInterface as DI import DifferentiationInterfaceTest as DIT using SparseArrays: SparseArrays, SparseMatrixCSC, nnz, spdiagm using StaticArrays: StaticArray, MArray, MMatrix, MVector, SArray, SMatrix, SVector +using PrecompileTools: @compile_workload static_num_to_vec(x::Number) = sin.(SVector(1, 2) .* x) static_num_to_mat(x::Number) = hcat(static_num_to_vec(x), static_num_to_vec(3x)) @@ -61,4 +62,8 @@ function DIT.static_scenarios(args...; kwargs...) return mystatic.(scens) end +@compile_workload begin + DIT.static_scenarios(; include_constantified=true, include_cachified=true) +end + end diff --git a/DifferentiationInterfaceTest/src/DifferentiationInterfaceTest.jl b/DifferentiationInterfaceTest/src/DifferentiationInterfaceTest.jl index 18ce86ee7..bfe491ef9 100644 --- a/DifferentiationInterfaceTest/src/DifferentiationInterfaceTest.jl +++ b/DifferentiationInterfaceTest/src/DifferentiationInterfaceTest.jl @@ -94,6 +94,7 @@ using DifferentiationInterface: PreparationMismatchError using DocStringExtensions: TYPEDFIELDS, TYPEDSIGNATURES using JET: @test_opt using LinearAlgebra: Adjoint, Diagonal, Transpose, I, dot, parent +using PrecompileTools: @compile_workload using ProgressMeter: ProgressUnknown, next! using Random: AbstractRNG, default_rng, rand! using SparseArrays: @@ -114,7 +115,16 @@ List of all second-order operators, to facilitate exclusion during tests. """ const SECOND_ORDER = [:hvp, :second_derivative, :hessian] -const ALL_OPS = vcat(FIRST_ORDER, SECOND_ORDER) +const ALL_OPS = ( + :pushforward, + :pullback, + :derivative, + :gradient, + :jacobian, + :hvp, + :second_derivative, + :hessian, +) include("utils.jl") @@ -128,6 +138,7 @@ include("scenarios/empty.jl") include("scenarios/extensions.jl") include("tests/correctness_eval.jl") +include("tests/prep_eval.jl") include("tests/type_stability_eval.jl") include("tests/benchmark.jl") include("tests/benchmark_eval.jl") @@ -140,4 +151,8 @@ export Scenario, compute_results export test_differentiation, benchmark_differentiation export DifferentiationBenchmarkDataRow +@compile_workload begin + default_scenarios(; include_constantified=true, include_cachified=true) +end + end diff --git a/DifferentiationInterfaceTest/src/test_differentiation.jl b/DifferentiationInterfaceTest/src/test_differentiation.jl index 3db0a5aab..fcc038ddf 100644 --- a/DifferentiationInterfaceTest/src/test_differentiation.jl +++ b/DifferentiationInterfaceTest/src/test_differentiation.jl @@ -164,6 +164,7 @@ function test_differentiation( sparsity, reprepare, ) + test_prep(adapted_backend, scen) end yield() (type_stability != :none) && @testset "Type stability" begin diff --git a/DifferentiationInterfaceTest/src/tests/correctness_eval.jl b/DifferentiationInterfaceTest/src/tests/correctness_eval.jl index e7ce47127..6b06266ea 100644 --- a/DifferentiationInterfaceTest/src/tests/correctness_eval.jl +++ b/DifferentiationInterfaceTest/src/tests/correctness_eval.jl @@ -1,7 +1,9 @@ has_size(::Union{Number,AbstractArray}) = true has_size(_x) = false -const PME = PreparationMismatchError +function should_reprepare(scen) + return has_size(scen.x) && has_size(scen.y) && (size(scen.x) != size(scen.prep_args.x)) +end for op in ALL_OPS op! = Symbol(op, "!") @@ -61,7 +63,7 @@ for op in ALL_OPS prep_nostrict0 = $prep_op( f, ba, prep_args.x, prep_args.contexts...; strict=Val(false) ) - if reprepare && has_size(x) && has_size(y) && (size(x) != size(prep_args.x)) + if reprepare && should_reprepare(scen) prep = $prep_op!(f, prep0, ba, x, contexts...) prep_nostrict = $prep_op!(f, prep_nostrict0, ba, x, contexts...) else @@ -89,7 +91,6 @@ for op in ALL_OPS foreach(preptup_cands_noval) do preptup_noval res1_out1_noval = $op(f, preptup_noval..., ba, x, contexts...) res1_out2_noval = $op(f, preptup_noval..., ba, x, contexts...) - @test isempty(preptup_noval) || only(preptup_noval) isa $P @test res1_out1_noval ≈ scen.res1 @test res1_out2_noval ≈ scen.res1 if sparsity && $op == jacobian @@ -97,10 +98,6 @@ for op in ALL_OPS @test mynnz(res1_out2_noval) == mynnz(scen.res1) end end - let prep = $prep_op(f, ba, prep_args.x, prep_args.contexts...) - @test_throws PME $val_and_op(nothing, prep, ba, x, contexts...) - @test_throws PME $op(nothing, prep, ba, x, contexts...) - end scenario_intact && @test new_scen == scen return nothing end @@ -122,7 +119,7 @@ for op in ALL_OPS prep_nostrict0 = $prep_op( f, ba, prep_args.x, prep_args.contexts...; strict=Val(false) ) - if reprepare && has_size(x) && has_size(y) && (size(x) != size(prep_args.x)) + if reprepare && should_reprepare(scen) prep = $prep_op!(f, prep0, ba, x, contexts...) prep_nostrict = $prep_op!(f, prep_nostrict0, ba, x, contexts...) else @@ -160,7 +157,6 @@ for op in ALL_OPS res1_out2_noval = $op!( f, res1_in2_noval, preptup_noval..., ba, x, contexts... ) - @test isempty(preptup_noval) || only(preptup_noval) isa $P @test res1_in1_noval === res1_out1_noval @test res1_in2_noval === res1_out2_noval @test res1_out1_noval ≈ scen.res1 @@ -170,12 +166,6 @@ for op in ALL_OPS @test mynnz(res1_out2_noval) == mynnz(scen.res1) end end - let prep = $prep_op(f, ba, prep_args.x, prep_args.contexts...) - @test_throws PME $val_and_op!( - nothing, mysimilar(res1), prep, ba, x, contexts... - ) - @test_throws PME $op!(nothing, mysimilar(res1), prep, ba, x, contexts...) - end scenario_intact && @test new_scen == scen return nothing end @@ -241,7 +231,6 @@ for op in ALL_OPS y_in2_noval = mysimilar(y) res1_out1_noval = $op(f, y_in1_noval, preptup_noval..., ba, x, contexts...) res1_out2_noval = $op(f, y_in2_noval, preptup_noval..., ba, x, contexts...) - @test isempty(preptup_noval) || only(preptup_noval) isa $P @test res1_out1_noval ≈ scen.res1 @test res1_out2_noval ≈ scen.res1 if sparsity && $op == jacobian @@ -249,12 +238,6 @@ for op in ALL_OPS @test mynnz(res1_out2_noval) == mynnz(scen.res1) end end - let prep = $prep_op(f, prep_args.y, ba, prep_args.x, prep_args.contexts...) - @test_throws PME $val_and_op( - nothing, mysimilar(y), prep, ba, x, contexts... - ) - @test_throws PME $op(nothing, mysimilar(y), prep, ba, x, contexts...) - end scenario_intact && @test new_scen == scen return nothing end @@ -302,7 +285,6 @@ for op in ALL_OPS y_out2_val, res1_out2_val = $val_and_op!( f, y_in2_val, res1_in2_val, preptup_val..., ba, x, contexts... ) - @test isempty(preptup_noval) || only(preptup_noval) isa $P @test y_in1_val === y_out1_val @test y_in2_val === y_out2_val @test y_out1_val ≈ scen.y @@ -325,7 +307,6 @@ for op in ALL_OPS res1_out2_noval = $op!( f, y_in2_noval, res1_in2_noval, preptup_noval..., ba, x, contexts... ) - @test isempty(preptup_noval) || only(preptup_noval) isa $P @test res1_in1_noval === res1_out1_noval @test res1_in2_noval === res1_out2_noval @test res1_out1_noval ≈ scen.res1 @@ -335,14 +316,6 @@ for op in ALL_OPS @test mynnz(res1_out2_noval) == mynnz(scen.res1) end end - let prep = $prep_op(f, prep_args.y, ba, prep_args.x, prep_args.contexts...) - @test_throws PME $val_and_op!( - nothing, mysimilar(y), mysimilar(res1), prep, ba, x, contexts... - ) - @test_throws PME $op!( - nothing, mysimilar(y), mysimilar(res1), prep, ba, x, contexts... - ) - end scenario_intact && @test new_scen == scen return nothing end @@ -365,7 +338,7 @@ for op in ALL_OPS prep_nostrict0 = $prep_op( f, ba, prep_args.x, prep_args.contexts...; strict=Val(false) ) - if reprepare && has_size(x) && has_size(y) && (size(x) != size(prep_args.x)) + if reprepare && should_reprepare(scen) prep = $prep_op!(f, prep0, ba, x, contexts...) prep_nostrict = $prep_op!(f, prep_nostrict0, ba, x, contexts...) else @@ -374,35 +347,34 @@ for op in ALL_OPS end ((), (prep,), (prep_nostrict,)) end - for (preptup_val, preptup_noval) in zip(preptup_cands_val, preptup_cands_noval) + foreach(preptup_cands_val) do preptup_val y_out1_val, res1_out1_val, res2_out1_val = $val_and_op( f, preptup_val..., ba, x, contexts... ) y_out2_val, res1_out2_val, res2_out2_val = $val_and_op( f, preptup_val..., ba, x, contexts... ) - res2_out1_noval = $op(f, preptup_noval..., ba, x, contexts...) - res2_out2_noval = $op(f, preptup_noval..., ba, x, contexts...) - @test isempty(preptup_noval) || only(preptup_noval) isa $P @test y_out1_val ≈ scen.y @test y_out2_val ≈ scen.y @test res1_out1_val ≈ scen.res1 @test res1_out2_val ≈ scen.res1 @test res2_out1_val ≈ scen.res2 @test res2_out2_val ≈ scen.res2 - @test res2_out1_noval ≈ scen.res2 - @test res2_out2_noval ≈ scen.res2 if sparsity && $op == hessian @test mynnz(res2_out1_val) == mynnz(scen.res2) @test mynnz(res2_out2_val) == mynnz(scen.res2) + end + end + foreach(preptup_cands_noval) do preptup_noval + res2_out1_noval = $op(f, preptup_noval..., ba, x, contexts...) + res2_out2_noval = $op(f, preptup_noval..., ba, x, contexts...) + @test res2_out1_noval ≈ scen.res2 + @test res2_out2_noval ≈ scen.res2 + if sparsity && $op == hessian @test mynnz(res2_out1_noval) == mynnz(scen.res2) @test mynnz(res2_out2_noval) == mynnz(scen.res2) end end - let prep = $prep_op(f, ba, prep_args.x, prep_args.contexts...) - @test_throws PME $val_and_op(nothing, prep, ba, x, contexts...) - @test_throws PME $op(nothing, prep, ba, x, contexts...) - end scenario_intact && @test new_scen == scen return nothing end @@ -424,7 +396,7 @@ for op in ALL_OPS prep_nostrict0 = $prep_op( f, ba, prep_args.x, prep_args.contexts...; strict=Val(false) ) - if reprepare && has_size(x) && has_size(y) && (size(x) != size(prep_args.x)) + if reprepare && should_reprepare(scen) prep = $prep_op!(f, prep0, ba, x, contexts...) prep_nostrict = $prep_op!(f, prep_nostrict0, ba, x, contexts...) else @@ -433,24 +405,15 @@ for op in ALL_OPS end ((), (prep,), (prep_nostrict,)) end - for (preptup_val, preptup_noval) in zip(preptup_cands_val, preptup_cands_noval) + foreach(preptup_cands_val) do preptup_val res1_in1_val, res2_in1_val = mysimilar(res1), mysimilar(res2) res1_in2_val, res2_in2_val = mysimilar(res1), mysimilar(res2) - res2_in1_noval = mysimilar(res2) - res2_in2_noval = mysimilar(res2) y_out1_val, res1_out1_val, res2_out1_val = $val_and_op!( f, res1_in1_val, res2_in1_val, preptup_val..., ba, x, contexts... ) y_out2_val, res1_out2_val, res2_out2_val = $val_and_op!( f, res1_in2_val, res2_in2_val, preptup_val..., ba, x, contexts... ) - res2_out1_noval = $op!( - f, res2_in1_noval, preptup_noval..., ba, x, contexts... - ) - res2_out2_noval = $op!( - f, res2_in2_noval, preptup_noval..., ba, x, contexts... - ) - @test isempty(preptup_noval) || only(preptup_noval) isa $P @test y_out1_val ≈ scen.y @test y_out2_val ≈ scen.y @test res1_in1_val === res1_out1_val @@ -461,23 +424,29 @@ for op in ALL_OPS @test res2_in2_val === res2_out2_val @test res2_out1_val ≈ scen.res2 @test res2_out2_val ≈ scen.res2 + if sparsity && $op == hessian + @test mynnz(res2_out1_val) == mynnz(scen.res2) + @test mynnz(res2_out2_val) == mynnz(scen.res2) + end + end + foreach(preptup_cands_noval) do preptup_noval + res2_in1_noval = mysimilar(res2) + res2_in2_noval = mysimilar(res2) + res2_out1_noval = $op!( + f, res2_in1_noval, preptup_noval..., ba, x, contexts... + ) + res2_out2_noval = $op!( + f, res2_in2_noval, preptup_noval..., ba, x, contexts... + ) @test res2_in1_noval === res2_out1_noval @test res2_in2_noval === res2_out2_noval @test res2_out1_noval ≈ scen.res2 @test res2_out2_noval ≈ scen.res2 if sparsity && $op == hessian - @test mynnz(res2_out1_val) == mynnz(scen.res2) - @test mynnz(res2_out2_val) == mynnz(scen.res2) @test mynnz(res2_out1_noval) == mynnz(scen.res2) @test mynnz(res2_out2_noval) == mynnz(scen.res2) end end - let prep = $prep_op(f, ba, prep_args.x, prep_args.contexts...) - @test_throws PME $val_and_op!( - nothing, mysimilar(res1), mysimilar(res2), prep, ba, x, contexts... - ) - @test_throws PME $op!(nothing, mysimilar(res2), prep, ba, x, contexts...) - end scenario_intact && @test new_scen == scen return nothing end @@ -506,7 +475,7 @@ for op in ALL_OPS strict=Val(false), ) prep_same = $prep_op_same(f, ba, x, map(zero, t), contexts...) - if reprepare && has_size(x) && has_size(y) && (size(x) != size(prep_args.x)) + if reprepare && should_reprepare(scen) prep = $prep_op!(f, prep0, ba, x, t, contexts...) prep_nostrict = $prep_op!(f, prep_nostrict0, ba, x, t, contexts...) else @@ -532,16 +501,11 @@ for op in ALL_OPS foreach(preptup_cands_noval) do preptup_noval res1_out1_noval = $op(f, preptup_noval..., ba, x, t, contexts...) res1_out2_noval = $op(f, preptup_noval..., ba, x, t, contexts...) - @test isempty(preptup_noval) || only(preptup_noval) isa $P for b in eachindex(scen.res1) @test res1_out1_noval[b] ≈ scen.res1[b] @test res1_out2_noval[b] ≈ scen.res1[b] end end - let prep = $prep_op(f, ba, prep_args.x, prep_args.t, prep_args.contexts...) - @test_throws PME $val_and_op(nothing, prep, ba, x, t, contexts...) - @test_throws PME $op(nothing, prep, ba, x, t, contexts...) - end scenario_intact && @test new_scen == scen return nothing end @@ -569,7 +533,7 @@ for op in ALL_OPS strict=Val(false), ) prep_same = $prep_op_same(f, ba, x, map(zero, t), contexts...) - if reprepare && has_size(x) && has_size(y) && (size(x) != size(prep_args.x)) + if reprepare && should_reprepare(scen) prep = $prep_op!(f, prep0, ba, x, t, contexts...) prep_nostrict = $prep_op!(f, prep_nostrict0, ba, x, t, contexts...) else @@ -605,7 +569,6 @@ for op in ALL_OPS res1_out2_noval = $op!( f, res1_in2_noval, preptup_noval..., ba, x, t, contexts... ) - @test isempty(preptup_noval) || only(preptup_noval) isa $P @test res1_in1_noval === res1_out1_noval @test res1_in2_noval === res1_out2_noval for b in eachindex(scen.res1) @@ -613,12 +576,6 @@ for op in ALL_OPS @test res1_out2_noval[b] ≈ scen.res1[b] end end - let prep = $prep_op(f, ba, prep_args.x, prep_args.t, prep_args.contexts...) - @test_throws PME $val_and_op!( - nothing, mysimilar(res1), prep, ba, x, t, contexts... - ) - @test_throws PME $op!(nothing, mysimilar(res1), prep, ba, x, t, contexts...) - end scenario_intact && @test new_scen == scen return nothing end @@ -688,20 +645,11 @@ for op in ALL_OPS res1_out2_noval = $op( f, y_in2_noval, preptup_noval..., ba, x, t, contexts... ) - @test isempty(preptup_noval) || only(preptup_noval) isa $P for b in eachindex(scen.res1) @test res1_out1_noval[b] ≈ scen.res1[b] @test res1_out2_noval[b] ≈ scen.res1[b] end end - let prep = $prep_op( - f, prep_args.y, ba, prep_args.x, prep_args.t, prep_args.contexts... - ) - @test_throws PME $val_and_op( - nothing, mysimilar(y), prep, ba, x, t, contexts... - ) - @test_throws PME $op(nothing, mysimilar(y), prep, ba, x, t, contexts...) - end scenario_intact && @test new_scen == scen return nothing end @@ -773,7 +721,6 @@ for op in ALL_OPS res1_out2_noval = $op!( f, y_in2_noval, res1_in2_noval, preptup_noval..., ba, x, t, contexts... ) - @test isempty(preptup_noval) || only(preptup_noval) isa $P @test res1_in1_noval === res1_out1_noval @test res1_in2_noval === res1_out2_noval for b in eachindex(scen.res1) @@ -781,16 +728,6 @@ for op in ALL_OPS @test res1_out2_noval[b] ≈ scen.res1[b] end end - let prep = $prep_op( - f, prep_args.y, ba, prep_args.x, prep_args.t, prep_args.contexts... - ) - @test_throws PME $val_and_op!( - nothing, mysimilar(y), mysimilar(res1), prep, ba, x, t, contexts... - ) - @test_throws PME $op!( - nothing, mysimilar(y), mysimilar(res1), prep, ba, x, t, contexts... - ) - end scenario_intact && @test new_scen == scen return nothing end @@ -819,7 +756,7 @@ for op in ALL_OPS strict=Val(false), ) prep_same = $prep_op_same(f, ba, x, map(zero, t), contexts...) - if reprepare && has_size(x) && has_size(y) && (size(x) != size(prep_args.x)) + if reprepare && should_reprepare(scen) prep = $prep_op!(f, prep0, ba, x, t, contexts...) prep_nostrict = $prep_op!(f, prep_nostrict0, ba, x, t, contexts...) else @@ -828,28 +765,27 @@ for op in ALL_OPS end ((), (prep,), (prep_nostrict,), (prep_same,)) end - for (preptup_val, preptup_noval) in zip(preptup_cands_val, preptup_cands_noval) - res2_out1_noval = $op(f, preptup_noval..., ba, x, t, contexts...) - res2_out2_noval = $op(f, preptup_noval..., ba, x, t, contexts...) + foreach(preptup_cands_val) do preptup_val res1_out1_val, res2_out1_val = $val_and_op( f, preptup_noval..., ba, x, t, contexts... ) res1_out2_val, res2_out2_val = $val_and_op( f, preptup_noval..., ba, x, t, contexts... ) - @test isempty(preptup_noval) || only(preptup_noval) isa $P @test res1_out1_val ≈ scen.res1 @test res1_out2_val ≈ scen.res1 for b in eachindex(scen.res2) - @test res2_out1_noval[b] ≈ scen.res2[b] - @test res2_out2_noval[b] ≈ scen.res2[b] @test res2_out1_val[b] ≈ scen.res2[b] @test res2_out2_val[b] ≈ scen.res2[b] end end - let prep = $prep_op(f, ba, prep_args.x, prep_args.t, prep_args.contexts...) - @test_throws PME $val_and_op(nothing, prep, ba, x, t, contexts...) - @test_throws PME $op(nothing, prep, ba, x, t, contexts...) + foreach(preptup_cands_noval) do preptup_noval + res2_out1_noval = $op(f, preptup_noval..., ba, x, t, contexts...) + res2_out2_noval = $op(f, preptup_noval..., ba, x, t, contexts...) + for b in eachindex(scen.res2) + @test res2_out1_noval[b] ≈ scen.res2[b] + @test res2_out2_noval[b] ≈ scen.res2[b] + end end scenario_intact && @test new_scen == scen return nothing @@ -878,7 +814,7 @@ for op in ALL_OPS strict=Val(false), ) prep_same = $prep_op_same(f, ba, x, map(zero, t), contexts...) - if reprepare && has_size(x) && has_size(y) && (size(x) != size(prep_args.x)) + if reprepare && should_reprepare(scen) prep = $prep_op!(f, prep0, ba, x, t, contexts...) prep_nostrict = $prep_op!(f, prep_nostrict0, ba, x, t, contexts...) else @@ -887,44 +823,41 @@ for op in ALL_OPS end ((), (prep,), (prep_nostrict,), (prep_same,)) end - for (preptup_val, preptup_noval) in zip(preptup_cands_val, preptup_cands_noval) - res2_in1_noval = mysimilar(res2) - res2_in2_noval = mysimilar(res2) + foreach(preptup_cands_val) do preptup_val res1_in1_val, res2_in1_val = mysimilar(res1), mysimilar(res2) res1_in2_val, res2_in2_val = mysimilar(res1), mysimilar(res2) - res2_out1_noval = $op!( - f, res2_in1_noval, preptup_noval..., ba, x, t, contexts... - ) - res2_out2_noval = $op!( - f, res2_in2_noval, preptup_noval..., ba, x, t, contexts... - ) res1_out1_val, res2_out1_val = $val_and_op!( f, res1_in1_val, res2_in1_val, preptup_noval..., ba, x, t, contexts... ) res1_out2_val, res2_out2_val = $val_and_op!( f, res1_in2_val, res2_in2_val, preptup_noval..., ba, x, t, contexts... ) - @test isempty(preptup_noval) || only(preptup_noval) isa $P @test res1_in1_val === res1_out1_val @test res1_in2_val === res1_out2_val @test res1_out1_val ≈ scen.res1 @test res1_out2_val ≈ scen.res1 - @test res2_in1_noval === res2_out1_noval - @test res2_in2_noval === res2_out2_noval @test res2_in1_val === res2_out1_val @test res2_in2_val === res2_out2_val for b in eachindex(scen.res2) - @test res2_out1_noval[b] ≈ scen.res2[b] - @test res2_out2_noval[b] ≈ scen.res2[b] @test res2_out1_val[b] ≈ scen.res2[b] @test res2_out2_val[b] ≈ scen.res2[b] end end - let prep = $prep_op(f, ba, prep_args.x, prep_args.t, prep_args.contexts...) - @test_throws PME $op!(nothing, mysimilar(res2), prep, ba, x, t, contexts...) - @test_throws PME $val_and_op!( - nothing, mysimilar(res1), mysimilar(res2), prep, ba, x, t, contexts... + foreach(preptup_cands_noval) do preptup_noval + res2_in1_noval = mysimilar(res2) + res2_in2_noval = mysimilar(res2) + res2_out1_noval = $op!( + f, res2_in1_noval, preptup_noval..., ba, x, t, contexts... + ) + res2_out2_noval = $op!( + f, res2_in2_noval, preptup_noval..., ba, x, t, contexts... ) + @test res2_in1_noval === res2_out1_noval + @test res2_in2_noval === res2_out2_noval + for b in eachindex(scen.res2) + @test res2_out1_noval[b] ≈ scen.res2[b] + @test res2_out2_noval[b] ≈ scen.res2[b] + end end scenario_intact && @test new_scen == scen return nothing diff --git a/DifferentiationInterfaceTest/src/tests/prep_eval.jl b/DifferentiationInterfaceTest/src/tests/prep_eval.jl new file mode 100644 index 000000000..52ad19473 --- /dev/null +++ b/DifferentiationInterfaceTest/src/tests/prep_eval.jl @@ -0,0 +1,177 @@ +const PME = PreparationMismatchError + +for op in ALL_OPS + op! = Symbol(op, "!") + val_prefix = if op == :second_derivative + "value_derivative_and_" + elseif op == :hessian + "value_gradient_and_" + elseif op == :hvp + "gradient_and_" + else + "value_and_" + end + val_and_op = Symbol(val_prefix, op) + val_and_op! = Symbol(val_prefix, op!) + prep_op = Symbol("prepare_", op) + prep_op! = Symbol("prepare!_", op) + prep_op_same = Symbol("prepare_", op, "_same_point") + + P = if op == :derivative + DerivativePrep + elseif op == :gradient + GradientPrep + elseif op == :hessian + HessianPrep + elseif op == :hvp + HVPPrep + elseif op == :jacobian + JacobianPrep + elseif op == :pullback + PullbackPrep + elseif op == :pushforward + PushforwardPrep + elseif op == :second_derivative + SecondDerivativePrep + end + + S1out = Scenario{op,:out,:out} + S1in = Scenario{op,:in,:out} + S2out = Scenario{op,:out,:in} + S2in = Scenario{op,:in,:in} + + if op in [:derivative, :gradient, :jacobian] + @eval function test_prep(ba::AbstractADType, scen::$S1out;) + (; f, x, contexts, prep_args) = new_scen = deepcopy(scen) + prep = $prep_op(f, ba, prep_args.x, prep_args.contexts...) + @test prep isa $P + @test_throws PME $val_and_op(nothing, prep, ba, x, contexts...) + @test_throws PME $op(nothing, prep, ba, x, contexts...) + return nothing + end + + @eval function test_prep(ba::AbstractADType, scen::$S1in;) + (; f, x, res1, contexts, prep_args) = new_scen = deepcopy(scen) + prep = $prep_op(f, ba, prep_args.x, prep_args.contexts...) + @test prep isa $P + @test_throws PME $val_and_op!( + nothing, mysimilar(res1), prep, ba, x, contexts... + ) + @test_throws PME $op!(nothing, mysimilar(res1), prep, ba, x, contexts...) + return nothing + end + + op == :gradient && continue + + @eval function test_prep(ba::AbstractADType, scen::$S2out;) + (; f, x, y, res1, contexts, prep_args) = new_scen = deepcopy(scen) + prep = $prep_op(f, prep_args.y, ba, prep_args.x, prep_args.contexts...) + @test prep isa $P + @test_throws PME $val_and_op(nothing, mysimilar(y), prep, ba, x, contexts...) + @test_throws PME $op(nothing, mysimilar(y), prep, ba, x, contexts...) + return nothing + end + + @eval function test_prep(ba::AbstractADType, scen::$S2in;) + (; f, x, y, res1, contexts, prep_args) = new_scen = deepcopy(scen) + prep = $prep_op(f, prep_args.y, ba, prep_args.x, prep_args.contexts...) + @test prep isa $P + @test_throws PME $val_and_op!( + nothing, mysimilar(y), mysimilar(res1), prep, ba, x, contexts... + ) + @test_throws PME $op!( + nothing, mysimilar(y), mysimilar(res1), prep, ba, x, contexts... + ) + return nothing + end + + elseif op in [:second_derivative, :hessian] + @eval function test_prep(ba::AbstractADType, scen::$S1out;) + (; f, x, y, res1, res2, contexts, prep_args) = new_scen = deepcopy(scen) + prep = $prep_op(f, ba, prep_args.x, prep_args.contexts...) + @test prep isa $P + @test_throws PME $val_and_op(nothing, prep, ba, x, contexts...) + @test_throws PME $op(nothing, prep, ba, x, contexts...) + return nothing + end + + @eval function test_prep(ba::AbstractADType, scen::$S1in;) + (; f, x, y, res1, res2, contexts, prep_args) = new_scen = deepcopy(scen) + prep = $prep_op(f, ba, prep_args.x, prep_args.contexts...) + @test prep isa $P + @test_throws PME $val_and_op!( + nothing, mysimilar(res1), mysimilar(res2), prep, ba, x, contexts... + ) + @test_throws PME $op!(nothing, mysimilar(res2), prep, ba, x, contexts...) + return nothing + end + + elseif op in [:pushforward, :pullback] + @eval function test_prep(ba::AbstractADType, scen::$S1out;) + (; f, x, y, t, res1, contexts, prep_args) = new_scen = deepcopy(scen) + prep = $prep_op(f, ba, prep_args.x, prep_args.t, prep_args.contexts...) + @test prep isa $P + @test_throws PME $val_and_op(nothing, prep, ba, x, t, contexts...) + @test_throws PME $op(nothing, prep, ba, x, t, contexts...) + return nothing + end + + @eval function test_prep(ba::AbstractADType, scen::$S1in;) + (; f, x, y, t, res1, contexts, prep_args) = new_scen = deepcopy(scen) + prep = $prep_op(f, ba, prep_args.x, prep_args.t, prep_args.contexts...) + @test prep isa $P + @test_throws PME $val_and_op!( + nothing, mysimilar(res1), prep, ba, x, t, contexts... + ) + @test_throws PME $op!(nothing, mysimilar(res1), prep, ba, x, t, contexts...) + return nothing + end + + @eval function test_prep(ba::AbstractADType, scen::$S2out;) + (; f, x, y, t, res1, contexts, prep_args) = new_scen = deepcopy(scen) + prep = $prep_op( + f, prep_args.y, ba, prep_args.x, prep_args.t, prep_args.contexts... + ) + @test prep isa $P + @test_throws PME $val_and_op(nothing, mysimilar(y), prep, ba, x, t, contexts...) + @test_throws PME $op(nothing, mysimilar(y), prep, ba, x, t, contexts...) + return nothing + end + + @eval function test_prep(ba::AbstractADType, scen::$S2in;) + (; f, x, y, t, res1, contexts, prep_args) = new_scen = deepcopy(scen) + prep = $prep_op( + f, prep_args.y, ba, prep_args.x, prep_args.t, prep_args.contexts... + ) + @test prep isa $P + @test_throws PME $val_and_op!( + nothing, mysimilar(y), mysimilar(res1), prep, ba, x, t, contexts... + ) + @test_throws PME $op!( + nothing, mysimilar(y), mysimilar(res1), prep, ba, x, t, contexts... + ) + return nothing + end + + elseif op in [:hvp] + @eval function test_prep(ba::AbstractADType, scen::$S1out;) + (; f, x, y, t, res1, res2, contexts, prep_args) = new_scen = deepcopy(scen) + prep = $prep_op(f, ba, prep_args.x, prep_args.t, prep_args.contexts...) + @test prep isa $P + @test_throws PME $val_and_op(nothing, prep, ba, x, t, contexts...) + @test_throws PME $op(nothing, prep, ba, x, t, contexts...) + return nothing + end + + @eval function test_prep(ba::AbstractADType, scen::$S1in;) + (; f, x, y, t, res1, res2, contexts, prep_args) = new_scen = deepcopy(scen) + prep = $prep_op(f, ba, prep_args.x, prep_args.t, prep_args.contexts...) + @test prep isa $P + @test_throws PME $op!(nothing, mysimilar(res2), prep, ba, x, t, contexts...) + @test_throws PME $val_and_op!( + nothing, mysimilar(res1), mysimilar(res2), prep, ba, x, t, contexts... + ) + return nothing + end + end +end From dff4579d9298ddb9a971299e569a041ce9df8bb6 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Fri, 29 Aug 2025 13:20:10 +0200 Subject: [PATCH 3/6] Fixes --- .../src/tests/correctness_eval.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/DifferentiationInterfaceTest/src/tests/correctness_eval.jl b/DifferentiationInterfaceTest/src/tests/correctness_eval.jl index 6b06266ea..a19c256df 100644 --- a/DifferentiationInterfaceTest/src/tests/correctness_eval.jl +++ b/DifferentiationInterfaceTest/src/tests/correctness_eval.jl @@ -767,10 +767,10 @@ for op in ALL_OPS end foreach(preptup_cands_val) do preptup_val res1_out1_val, res2_out1_val = $val_and_op( - f, preptup_noval..., ba, x, t, contexts... + f, preptup_val..., ba, x, t, contexts... ) res1_out2_val, res2_out2_val = $val_and_op( - f, preptup_noval..., ba, x, t, contexts... + f, preptup_val..., ba, x, t, contexts... ) @test res1_out1_val ≈ scen.res1 @test res1_out2_val ≈ scen.res1 @@ -827,10 +827,10 @@ for op in ALL_OPS res1_in1_val, res2_in1_val = mysimilar(res1), mysimilar(res2) res1_in2_val, res2_in2_val = mysimilar(res1), mysimilar(res2) res1_out1_val, res2_out1_val = $val_and_op!( - f, res1_in1_val, res2_in1_val, preptup_noval..., ba, x, t, contexts... + f, res1_in1_val, res2_in1_val, preptup_val..., ba, x, t, contexts... ) res1_out2_val, res2_out2_val = $val_and_op!( - f, res1_in2_val, res2_in2_val, preptup_noval..., ba, x, t, contexts... + f, res1_in2_val, res2_in2_val, preptup_val..., ba, x, t, contexts... ) @test res1_in1_val === res1_out1_val @test res1_in2_val === res1_out2_val From f8543827361a2c586ffaa36b6d09a386e3d9cb05 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Fri, 29 Aug 2025 14:04:53 +0200 Subject: [PATCH 4/6] Missing conj --- DifferentiationInterfaceTest/src/scenarios/default.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DifferentiationInterfaceTest/src/scenarios/default.jl b/DifferentiationInterfaceTest/src/scenarios/default.jl index 39b8f4ff4..a5889a379 100644 --- a/DifferentiationInterfaceTest/src/scenarios/default.jl +++ b/DifferentiationInterfaceTest/src/scenarios/default.jl @@ -306,7 +306,7 @@ function arr_to_num_hessian(x0) return convert(typeof(similar(x0, length(x0), length(x0))), H) end -arr_to_num_pushforward(x, dx) = sum(arr_to_num_gradient(x) .* dx) +arr_to_num_pushforward(x, dx) = sum(conj.(arr_to_num_gradient(x)) .* dx) arr_to_num_pullback(x, dy) = arr_to_num_gradient(x) .* dy arr_to_num_hvp(x, dx) = reshape(arr_to_num_hessian(x) * vec(dx), size(x)) From c8e91ba561b60cafa6943fa3d960e4164c8bcea5 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Fri, 29 Aug 2025 14:29:33 +0200 Subject: [PATCH 5/6] Reprepare cov --- DifferentiationInterfaceTest/test/zero_backends.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/DifferentiationInterfaceTest/test/zero_backends.jl b/DifferentiationInterfaceTest/test/zero_backends.jl index aeec03bc2..b958c6a79 100644 --- a/DifferentiationInterfaceTest/test/zero_backends.jl +++ b/DifferentiationInterfaceTest/test/zero_backends.jl @@ -14,6 +14,7 @@ test_differentiation( map(zero, default_scenarios(; include_batchified=false)); type_stability=:full, logging=LOGGING, + reprepare=false, ) test_differentiation( From d3600a319f526c36cad347aea3c88a67d4679883 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Tue, 2 Sep 2025 14:19:34 +0100 Subject: [PATCH 6/6] Add changelog --- DifferentiationInterfaceTest/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/DifferentiationInterfaceTest/CHANGELOG.md b/DifferentiationInterfaceTest/CHANGELOG.md index e25180179..e516089fe 100644 --- a/DifferentiationInterfaceTest/CHANGELOG.md +++ b/DifferentiationInterfaceTest/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://github.com/JuliaDiff/DifferentiationInterface.jl/compare/DifferentiationInterfaceTest-v0.10.1...main) +### Fixed + + - Refactor test loops ([#848](https://github.com/JuliaDiff/DifferentiationInterface.jl/pull/848)) + ## [0.10.1](https://github.com/JuliaDiff/DifferentiationInterface.jl/compare/DifferentiationInterfaceTest-v0.10.0...DifferentiationInterfaceTest-v0.10.1) ### Added