@@ -3,10 +3,11 @@ using QuantumPropagators.Generators: hamiltonian
33using QuantumPropagators. Controls: get_controls
44using QuantumControlTestUtils. RandomObjects: random_matrix, random_state_vector
55using QuantumControl. Interfaces: check_generator
6- using QuantumPropagators. Interfaces: check_state
7- using QuantumGradientGenerators: GradGenerator, GradVector
6+ using QuantumPropagators. Interfaces:
7+ check_state, check_operator, supports_matrix_interface, supports_vector_interface
8+ using QuantumGradientGenerators: GradGenerator, GradVector, GradgenOperator
89using StaticArrays: SVector, SMatrix
9- using LinearAlgebra: norm
10+ using LinearAlgebra: norm, dot, mul!, I
1011
1112
1213@testset " GradVector Interface" begin
7576 @test check_generator (G̃_of_t; state = Ψ̃, tlist, for_gradient_optimization = false )
7677
7778end
79+
80+
81+ @testset " GradgenOperator Matrix Interface" begin
82+
83+ N = 5
84+ L = 2
85+ G = Matrix {ComplexF64} (I, N, N)
86+ mu = [rand (ComplexF64, N, N) for _ = 1 : L]
87+ op = GradgenOperator {L,Matrix{ComplexF64},Matrix{ComplexF64}} (G, mu)
88+ state = GradVector (rand (ComplexF64, N), L)
89+
90+ # supports_matrix_interface reports true for matrix-backed GradgenOperator
91+ @test supports_matrix_interface (typeof (op))
92+
93+ # check_operator passes the full matrix interface check including for_expval
94+ @test check_operator (op; state, for_expval = true )
95+
96+ # getindex is consistent with the dense Array representation
97+ dense = Array (op)
98+ @test all (op[i, j] ≈ dense[i, j] for i = 1 : size (op, 1 ), j = 1 : size (op, 2 ))
99+
100+ # length
101+ @test length (op) == prod (size (op))
102+
103+ # iterate visits elements in column-major order, consistent with vec(Array(op))
104+ @test all (collect (op) .≈ vec (dense))
105+
106+ # 3-arg mul! agrees with 5-arg mul!(Phi, G, Psi, 1, 0)
107+ Psi = GradVector (rand (ComplexF64, N), L)
108+ Phi1 = GradVector (zeros (ComplexF64, N), L)
109+ Phi2 = GradVector (zeros (ComplexF64, N), L)
110+ mul! (Phi1, op, Psi)
111+ mul! (Phi2, op, Psi, true , false )
112+ @test norm (Phi1 - Phi2) < 1e-14
113+
114+ # 3-arg dot(Psi, op, Phi) matches dot(Psi, op * Phi)
115+ Psi2 = GradVector (rand (ComplexF64, N), L)
116+ @test dot (state, op, Psi2) ≈ dot (state, op * Psi2)
117+
118+ # similar(op) returns a dense Array of the same eltype and size (matching Operator pattern)
119+ op_sim = similar (op)
120+ @test op_sim isa Array{eltype (op)}
121+ @test size (op_sim) == size (op)
122+
123+ # similar(op, S) returns a dense Array of type S with matching size
124+ @test similar (op, Float64) isa Array{Float64}
125+ @test size (similar (op, Float64)) == size (op)
126+
127+ # similar(op, dims) returns a dense Array with given dims
128+ @test similar (op, (3 , 4 )) isa Array{eltype (op)}
129+ @test size (similar (op, (3 , 4 ))) == (3 , 4 )
130+
131+ # similar(op, S, dims) returns a dense Array of type S with given dims
132+ @test similar (op, Float64, (3 , 4 )) isa Array{Float64}
133+ @test size (similar (op, Float64, (3 , 4 ))) == (3 , 4 )
134+
135+ end
136+
137+
138+ @testset " GradVector Vector Interface" begin
139+
140+ N = 5
141+ L = 2
142+ Psi = rand (ComplexF64, N)
143+ gradvec = GradVector (Psi, L)
144+
145+ # supports_vector_interface is true for Vector-backed GradVector
146+ @test supports_vector_interface (typeof (gradvec))
147+
148+ # check_state passes full vector interface check
149+ @test check_state (gradvec)
150+
151+ # size is 1D with total length
152+ @test size (gradvec) == (N * (L + 1 ),)
153+ @test size (gradvec) == (length (gradvec),)
154+
155+ # getindex is consistent with convert_gradvec_to_dense layout:
156+ # [grad_states[1]; grad_states[2]; ...; grad_states[L]; state]
157+ dense = convert (Vector{ComplexF64}, gradvec)
158+ @test all (gradvec[k] == dense[k] for k = 1 : length (gradvec))
159+
160+ # iterate visits elements consistent with getindex
161+ @test all (collect (gradvec) .== dense)
162+
163+ # setindex! round-trips through getindex
164+ gradvec2 = GradVector (copy (Psi), L)
165+ for k = 1 : length (gradvec2)
166+ gradvec2[k] = gradvec[k]
167+ end
168+ @test all (gradvec2[k] == gradvec[k] for k = 1 : length (gradvec))
169+
170+ # similar(gradvec, S) returns a mutable Vector{S} with same length
171+ @test similar (gradvec, ComplexF32) isa Vector{ComplexF32}
172+ @test length (similar (gradvec, ComplexF32)) == length (gradvec)
173+
174+ # similar(gradvec, dims) returns a plain Array with same eltype and given dims
175+ @test similar (gradvec, (3 , 4 )) isa Array{eltype (gradvec)}
176+ @test size (similar (gradvec, (3 , 4 ))) == (3 , 4 )
177+
178+ end
179+
180+
181+ @testset " GradVector Vector Interface (Static)" begin
182+
183+ N = 5
184+ L = 2
185+ Psi = SVector {N,ComplexF64} (rand (ComplexF64, N))
186+ gradvec = GradVector (Psi, L)
187+
188+ # SVector-backed GradVector: supports_vector_interface follows the component type
189+ @test supports_vector_interface (typeof (gradvec))
190+
191+ # check_state passes (SVector is inplace=false, so setindex! is not checked)
192+ @test check_state (gradvec)
193+
194+ # getindex is consistent with the dense layout
195+ dense = convert (Vector{ComplexF64}, gradvec)
196+ @test all (gradvec[k] == dense[k] for k = 1 : length (gradvec))
197+
198+ end
199+
200+
201+ @testset " GradVector without Vector Interface" begin
202+
203+ N = 5
204+ L = 2
205+ # Matrix is not an AbstractVector, so supports_vector_interface returns false
206+ Psi = rand (ComplexF64, N, N)
207+ gradvec = GradVector (Psi, L)
208+
209+ @test ! supports_vector_interface (typeof (gradvec))
210+
211+ # check_state still passes via the basic (non-vector) state interface
212+ @test check_state (gradvec)
213+
214+ end
0 commit comments