|
719 | 719 | @test getfield(cache, :dual_linear_cache) === inner |
720 | 720 | @test ≈(sol3, Matrix(A2) \ b2, rtol = 1.0e-9) |
721 | 721 | end |
| 722 | + |
| 723 | +struct ReinterpretTestTag end |
| 724 | + |
| 725 | +@testset "Default algorithm selection uses primal values (issue with reinterpret-wrapped duals)" begin |
| 726 | + # `solve(prob)` with no algorithm must select the default from the *primal* |
| 727 | + # A/b (what the inner LinearCache actually solves with), not the dual types. |
| 728 | + # A reinterpret/reshape-wrapped Dual A (as produced by PreallocationTools' |
| 729 | + # get_tmp) is not a DenseMatrix, so dual-type-based selection picks |
| 730 | + # KrylovJL_GMRES — but the primal cache is a dense Matrix whose default-solver |
| 731 | + # Krylov cacheval slots are initialized as Nothing, giving a TypeError in |
| 732 | + # __setfield! at solve time. |
| 733 | + DualT = ForwardDiff.Dual{ForwardDiff.Tag{ReinterpretTestTag, Float64}, Float64, 1} |
| 734 | + |
| 735 | + A_buf = zeros(2) |
| 736 | + b_buf = zeros(2) |
| 737 | + A = reshape(reinterpret(DualT, A_buf), 1, 1) |
| 738 | + b = reinterpret(DualT, b_buf) |
| 739 | + A[1, 1] = DualT(2.0, ForwardDiff.Partials((1.0,))) |
| 740 | + b[1] = DualT(3.0, ForwardDiff.Partials((0.5,))) |
| 741 | + |
| 742 | + @test !(A isa DenseMatrix) # the wrapper that triggered dual-based GMRES selection |
| 743 | + |
| 744 | + sol = solve(LinearProblem(A, b)) |
| 745 | + # u = b/A = 1.5, du = (db - u * dA)/A = (0.5 - 1.5)/2 = -0.5 |
| 746 | + @test ForwardDiff.value(sol.u[1]) ≈ 1.5 |
| 747 | + @test ForwardDiff.partials(sol.u[1])[1] ≈ -0.5 |
| 748 | + |
| 749 | + # The init entry point selects from primal values too and must agree. |
| 750 | + cache = init(LinearProblem(A, b), nothing) |
| 751 | + @test solve!(cache).u ≈ sol.u |
| 752 | +end |
0 commit comments