11using NonlinearSolveBase: NonlinearSolveBase, Utils
2- using LinearSolve # linsolve_identity!! routes matrices through the LinearSolve default
2+ using LinearSolve # used by sparse and structured linsolve_identity!! workspaces
33using LinearAlgebra
44using Test
55
@@ -20,20 +20,15 @@ using Test
2020 @test Utils. linsolve_identity!! (workspace, A) ≈ inv (A_orig)
2121 # nothing workspace (preinverted-init path in QuasiNewton) still works
2222 @test Utils. linsolve_identity!! (nothing , A) ≈ inv (A_orig)
23- # re-inverting the returned inverse (which aliases the workspace's solution
24- # buffer) recovers A
23+ # re-inverting the returned inverse recovers A
2524 Ai_copy = copy (Utils. linsolve_identity!! (workspace, A))
2625 @test Utils. linsolve_identity!! (workspace, Ai_copy) ≈ A_orig
2726 @test Utils. linsolve_identity!! (workspace, Utils. linsolve_identity!! (workspace, A)) ≈
2827 A_orig
2928 end
3029end
3130
32- @testset " singular input takes the pivoted-QR rescue" begin
33- # The result is the LinearSolve default algorithm's least-squares generalized
34- # inverse from its singular-LU → pivoted-QR rescue, NOT the SVD `pinv` (an
35- # intentional semantics change: same fitness for quasi-Newton initialization,
36- # different finite matrix).
31+ @testset " dense singular input matches pinv" begin
3732 n = 20
3833 A_singular = let B = rand (n, n)
3934 B[:, 1 ] .= 0
4540 for A in (A_singular, A_singular_triu)
4641 A_orig = copy (A)
4742 workspace, _ = Utils. linsolve_workspace (A)
43+ @test workspace === nothing
4844 X = Utils. linsolve_identity!! (workspace, A)
4945 @test size (X) == size (A)
5046 @test all (isfinite, X)
51- # X is a least-squares generalized inverse: A * X projects onto range(A )
47+ @test X ≈ pinv (A_orig )
5248 @test A * X * A ≈ A atol = 1.0e-8 * norm (A)
5349 @test A == A_orig
54- # a subsequent nonsingular solve with the same workspace is unaffected
5550 B = rand (n, n) + n * I
5651 @test Utils. linsolve_identity!! (workspace, B) ≈ inv (B)
5752 end
5853end
5954
60- @testset " workspace reuse does not allocate a matrix copy " begin
55+ @testset " strided matrices use the native inverse path " begin
6156 n = 51
6257 A = rand (n, n) + n * I
63- workspace, _ = Utils. linsolve_workspace (A)
64- Utils. linsolve_identity!! (workspace, A) # compile
65- # Steady state is the LinearSolve refactorization floor (`lu!` ipiv + wrapper); the
66- # old code copied A (`lu`) and allocated a LAPACK getri work array, both O(n^2).
67- allocs = @allocated Utils. linsolve_identity!! (workspace, A)
68- @test allocs < sizeof (A)
58+ workspace, A_ret = Utils. linsolve_workspace (A)
59+ @test workspace === nothing && A_ret === A
60+ @test Utils. linsolve_identity!! (workspace, A) ≈ inv (A)
6961
7062 A_triu = triu (A)
71- Utils . linsolve_identity!! ( workspace, A_triu) # compile
72- allocs_tri = @allocated Utils . linsolve_identity!! (workspace, A_triu)
73- @test allocs_tri < sizeof (A )
63+ workspace, A_triu_ret = Utils . linsolve_workspace (A_triu)
64+ @test workspace === nothing && A_triu_ret === A_triu
65+ @test Utils . linsolve_identity!! (workspace, A_triu) ≈ inv (A_triu )
7466end
7567
7668@testset " sparse matrices route through the lincache (no sparse pinv)" begin
0 commit comments