@@ -17,7 +17,6 @@ verifies the given `op` relative to `state`. The `state` must pass
1717An "operator" is any object that [`evaluate`](@ref) returns when evaluating a
1818time-dependent dynamic generator. The specific requirements for `op` are:
1919
20- * `eltype(op)` must be defined and return a numeric type
2120* `size(op)` must be defined and return a tuple of integers
2221* `size(op, dim)` must be defined for each dimension and be consistent with
2322 `size(op)`
@@ -26,9 +25,11 @@ time-dependent dynamic generator. The specific requirements for `op` are:
2625* `op * state` must be defined
2726* The [`QuantumPropagators.Interfaces.supports_inplace`](@ref) method must be
2827 defined for `op`. If it returns `true`, it must be possible to evaluate a
29- generator in-place into the existing `op`. See [`check_generator`](@ref).
28+ generator in-place into the existing `op`. See
29+ [`QuantumPropagators.Interfaces.check_generator`](@ref).
3030
31- If [`QuantumPropagators.Interfaces.supports_inplace(state)`](@ref):
31+ If [`QuantumPropagators.Interfaces.supports_inplace(state)`](@ref
32+ QuantumPropagators.Interfaces.supports_inplace):
3233
3334* The 3-argument `LinearAlgebra.mul!` must apply `op` to the given `state`
3435* The 5-argument `LinearAlgebra.mul!` must apply `op` to the given `state`
@@ -40,6 +41,30 @@ If `for_expval` (typically required for optimal control):
4041* `LinearAlgebra.dot(state, op, state)` must return return a number
4142* `dot(state, op, state)` must match `dot(state, op * state)`, if applicable
4243
44+ If [`QuantumPropagators.Interfaces.supports_matrix_interface(op)`](@ref
45+ QuantumPropagators.Interfaces.supports_matrix_interface) is `true`, the
46+ operator must implement the
47+ [Abstract Array interface](https://docs.julialang.org/en/v1/manual/interfaces/#man-interface-array)
48+ for two-dimensional arrays:
49+
50+ * `eltype(op)` must be defined and return a numeric type
51+ * `getindex(op, i, j)` must be defined and return elements matching `eltype`
52+ * `length(op)` must equal `prod(size(op))`
53+ * `iterate(op)` must be defined
54+ * `similar(op)` must be defined and return a mutable array with the same shape
55+ and element type
56+ * `similar(op, ::Type{S})` must return a mutable array with the same shape and
57+ element type `S`
58+ * `similar(op, dims::Dims)` must return a mutable array with the same element
59+ type and the given dimensions
60+ * `similar(op, ::Type{S}, dims::Dims)` must return a mutable array with the
61+ given element type and dimensions
62+
63+ If additionally [`QuantumPropagators.Interfaces.supports_inplace(op)`](@ref
64+ QuantumPropagators.Interfaces.supports_inplace) is `true` (read-write matrix):
65+
66+ * `setindex!(op, v, i, j)` must be defined
67+
4368The function returns `true` for a valid operator and `false` for an invalid
4469operator. Unless `quiet=true`, it will log an error to indicate which of the
4570conditions failed.
@@ -74,20 +99,6 @@ function check_operator(
7499 success = false
75100 end
76101
77- try
78- T = eltype (op)
79- if ! (T isa Type && T <: Number )
80- quiet || @error " $(px) `eltype(op)` must return a numeric type, not $T "
81- success = false
82- end
83- catch exc
84- quiet || @error (
85- " $(px) `eltype(op)` must be defined." ,
86- exception = (exc, catch_abbreviated_backtrace ())
87- )
88- success = false
89- end
90-
91102 try
92103 s = size (op)
93104 if ! (s isa Tuple)
@@ -171,7 +182,6 @@ function check_operator(
171182 success = false
172183 end
173184
174-
175185 if supports_inplace (state)
176186
177187 try
@@ -254,6 +264,197 @@ function check_operator(
254264
255265 end
256266
267+ if supports_matrix_interface (op)
268+
269+ try
270+ T = eltype (op)
271+ if ! (T isa Type && T <: Number )
272+ quiet || @error " $(px) `eltype(op)` must return a numeric type, not $T "
273+ success = false
274+ end
275+ catch exc
276+ quiet || @error (
277+ " $(px) `eltype(op)` must be defined." ,
278+ exception = (exc, catch_abbreviated_backtrace ())
279+ )
280+ success = false
281+ end
282+
283+ try
284+ s = size (op)
285+ if length (s) == 2 && all (d -> d > 0 , s)
286+ val = op[1 , 1 ]
287+ T = eltype (op)
288+ if T isa Type && T <: Number && ! (val isa T)
289+ quiet ||
290+ @error " $(px) `op[1, 1]` must return a value of type `eltype(op)=$T `, not $(typeof (val)) "
291+ success = false
292+ end
293+ end
294+ catch exc
295+ quiet || @error (
296+ " $(px) `getindex(op, i, j)` must be defined." ,
297+ exception = (exc, catch_abbreviated_backtrace ())
298+ )
299+ success = false
300+ end
301+
302+ try
303+ l = length (op)
304+ s = size (op)
305+ if l != prod (s)
306+ quiet ||
307+ @error " $(px) `length(op)` must equal `prod(size(op))`: $l ≠ $(prod (s)) "
308+ success = false
309+ end
310+ catch exc
311+ quiet || @error (
312+ " $(px) `length(op)` must be defined." ,
313+ exception = (exc, catch_abbreviated_backtrace ())
314+ )
315+ success = false
316+ end
317+
318+ try
319+ itr = iterate (op)
320+ s = size (op)
321+ if isnothing (itr) && prod (s) > 0
322+ quiet ||
323+ @error " $(px) `iterate(op)` must not return `nothing` for a non-empty operator"
324+ success = false
325+ end
326+ catch exc
327+ quiet || @error (
328+ " $(px) `iterate(op)` must be defined." ,
329+ exception = (exc, catch_abbreviated_backtrace ())
330+ )
331+ success = false
332+ end
333+
334+ try
335+ op2 = similar (op)
336+ if ! supports_inplace (op2)
337+ quiet ||
338+ @error " $(px) `similar(op)` must return a mutable array (`supports_inplace` must be `true`), got $(typeof (op2)) "
339+ success = false
340+ end
341+ if size (op2) != size (op)
342+ quiet ||
343+ @error " $(px) `similar(op)` must return an array with the same shape: size $(size (op2)) ≠ $(size (op)) "
344+ success = false
345+ end
346+ if eltype (op2) != eltype (op)
347+ quiet ||
348+ @error " $(px) `similar(op)` must return an array with the same element type: $(eltype (op2)) ≠ $(eltype (op)) "
349+ success = false
350+ end
351+ catch exc
352+ quiet || @error (
353+ " $(px) `similar(op)` must be defined." ,
354+ exception = (exc, catch_abbreviated_backtrace ())
355+ )
356+ success = false
357+ end
358+
359+ try
360+ S = (eltype (op) == ComplexF64) ? ComplexF32 : ComplexF64
361+ op2 = similar (op, S)
362+ if ! supports_inplace (op2)
363+ quiet ||
364+ @error " $(px) `similar(op, $S )` must return a mutable array (`supports_inplace` must be `true`), got $(typeof (op2)) "
365+ success = false
366+ end
367+ if size (op2) != size (op)
368+ quiet ||
369+ @error " $(px) `similar(op, $S )` must return an array with the same shape: size $(size (op2)) ≠ $(size (op)) "
370+ success = false
371+ end
372+ if eltype (op2) != S
373+ quiet ||
374+ @error " $(px) `similar(op, $S )` must return an array with element type $S , got $(eltype (op2)) "
375+ success = false
376+ end
377+ catch exc
378+ quiet || @error (
379+ " $(px) `similar(op, ::Type{S})` must be defined." ,
380+ exception = (exc, catch_abbreviated_backtrace ())
381+ )
382+ success = false
383+ end
384+
385+ try
386+ dims = size (op)
387+ op2 = similar (op, dims)
388+ if ! supports_inplace (op2)
389+ quiet ||
390+ @error " $(px) `similar(op, dims)` must return a mutable array (`supports_inplace` must be `true`), got $(typeof (op2)) "
391+ success = false
392+ end
393+ if size (op2) != dims
394+ quiet ||
395+ @error " $(px) `similar(op, dims)` must return an array with size $dims , got $(size (op2)) "
396+ success = false
397+ end
398+ if eltype (op2) != eltype (op)
399+ quiet ||
400+ @error " $(px) `similar(op, dims)` must return an array with the same element type: $(eltype (op2)) ≠ $(eltype (op)) "
401+ success = false
402+ end
403+ catch exc
404+ quiet || @error (
405+ " $(px) `similar(op, dims::Dims)` must be defined." ,
406+ exception = (exc, catch_abbreviated_backtrace ())
407+ )
408+ success = false
409+ end
410+
411+ try
412+ S = (eltype (op) == ComplexF64) ? ComplexF32 : ComplexF64
413+ dims = size (op)
414+ op2 = similar (op, S, dims)
415+ if ! supports_inplace (op2)
416+ quiet ||
417+ @error " $(px) `similar(op, $S , dims)` must return a mutable array (`supports_inplace` must be `true`), got $(typeof (op2)) "
418+ success = false
419+ end
420+ if size (op2) != dims
421+ quiet ||
422+ @error " $(px) `similar(op, $S , dims)` must return an array with size $dims , got $(size (op2)) "
423+ success = false
424+ end
425+ if eltype (op2) != S
426+ quiet ||
427+ @error " $(px) `similar(op, $S , dims)` must return an array with element type $S , got $(eltype (op2)) "
428+ success = false
429+ end
430+ catch exc
431+ quiet || @error (
432+ " $(px) `similar(op, ::Type{S}, dims::Dims)` must be defined." ,
433+ exception = (exc, catch_abbreviated_backtrace ())
434+ )
435+ success = false
436+ end
437+
438+ if supports_inplace (op)
439+
440+ try
441+ s = size (op)
442+ if length (s) == 2 && all (d -> d > 0 , s)
443+ v = op[1 , 1 ]
444+ op[1 , 1 ] = v # write back the same value
445+ end
446+ catch exc
447+ quiet || @error (
448+ " $(px) `setindex!(op, v, i, j)` must be defined for an in-place operator." ,
449+ exception = (exc, catch_abbreviated_backtrace ())
450+ )
451+ success = false
452+ end
453+
454+ end
455+
456+ end
457+
257458 return success
258459
259460end
0 commit comments