Skip to content

Commit 9d1331a

Browse files
committed
Merge #100 from branch interface-traits
2 parents 8609179 + 6cd2b0f commit 9d1331a

8 files changed

Lines changed: 1310 additions & 19 deletions

CLAUDE.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,26 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
55
## Development Commands
66

77
### Testing
8+
89
- `make test` - Run the full test suite
910
- `julia --project=test -e 'include("test/runtests.jl")'` - Run tests directly
1011

1112
### Development Environment
13+
1214
- `make devrepl` - Start interactive REPL with development environment (recommended for development)
1315
- `julia -i --banner=no devrepl.jl` - Alternative way to start development REPL
1416

1517
### Documentation
18+
1619
- `make docs` - Build documentation
1720

1821
### Code Quality
22+
1923
- `make codestyle` - Apply JuliaFormatter to entire project
2024
- `julia --project=test -e 'using JuliaFormatter; format(".", verbose=true)'` - Format code directly
2125

2226
### Cleanup
27+
2328
- `make clean` - Clean build/doc/testing artifacts
2429
- `make distclean` - Restore to clean checkout state
2530

@@ -28,6 +33,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
2833
This is a Julia package for quantum system time propagation within the JuliaQuantumControl ecosystem.
2934

3035
### Core Structure
36+
3137
- **src/QuantumPropagators.jl** - Main module definition with submodule includes
3238
- **src/interfaces/** - Interface definitions for operators, states, generators, propagators, etc.
3339
- **Submodules** organized by functionality:
@@ -43,27 +49,36 @@ This is a Julia package for quantum system time propagation within the JuliaQuan
4349
- `Generators` (generators.jl) - Hamiltonian/Liouvillian generators
4450

4551
### Key Propagation Methods
52+
4653
- **Chebyshev propagation** (cheby_propagator.jl) - Polynomial expansion method
4754
- **Newton propagation** (newton_propagator.jl) - Newton interpolation method
4855
- **Matrix exponential** (exp_propagator.jl) - Direct exponentiation
4956
- **ODE integration** (ode_function.jl) - Interface to OrdinaryDiffEq.jl via extensions
5057

5158
### High-Level Interface
59+
5260
- `propagate()` function for single time evolution
5361
- `propagate_sequence()` for sequential propagations with different parameters
5462

5563
### Extensions
64+
5665
The package uses Julia's extension system for optional dependencies:
5766
- `QuantumPropagatorsODEExt` - OrdinaryDiffEq.jl integration
5867
- `QuantumPropagatorsRecursiveArrayToolsExt` - RecursiveArrayTools.jl support
5968
- `QuantumPropagatorsStaticArraysExt` - StaticArrays.jl optimization
6069

6170
### Testing Framework
71+
6272
Uses SafeTestsets for isolated test execution. Tests are comprehensive and include:
6373
- Interface validation tests
6474
- Individual propagator method tests
6575
- Integration tests for complete propagation workflows
6676

77+
Running `make test` prints out coverage data in a table.
78+
79+
If necessary, detailed line-by-line coverage information can be obtained by running julia --project=test -e 'include("devrepl.jl"); generate_coverage_html()' after `make test`.
80+
This will produce html files inside the `coverage` subfolder, with `coverage/src` mirroring the structure of the `src` folder of `.jl` files. Lines with `<span class="tlaUNC">` are not covered. Ignore the raw tracefiles in the `.coverage` subfolder.
81+
6782
### Development Environment
6883
The project uses a sophisticated development setup:
6984
- Development REPL (devrepl.jl) with Revise.jl for hot reloading
@@ -72,6 +87,7 @@ The project uses a sophisticated development setup:
7287
- Code formatting with JuliaFormatter
7388

7489
### Documentation
90+
7591
Uses Documenter.jl with comprehensive API documentation and examples. Documentation includes detailed method explanations.
7692

7793
## Docstrings

src/QuantumPropagators.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,13 @@ export init_prop, reinit_prop!, prop_step!
3838

3939
#! format: off
4040
module Interfaces
41-
export supports_inplace
41+
export supports_inplace, supports_vector_interface, supports_matrix_interface
4242
export check_operator, check_state, check_tlist, check_amplitude
4343
export check_control, check_generator, check_propagator
4444
export check_parameterized_function, check_parameterized
4545
include("interfaces/supports_inplace.jl")
46+
include("interfaces/supports_vector_interface.jl")
47+
include("interfaces/supports_matrix_interface.jl")
4648
include("interfaces/utils.jl")
4749
include("interfaces/state.jl")
4850
include("interfaces/tlist.jl")

src/interfaces/operator.jl

Lines changed: 196 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ verifies the given `op` relative to `state`. The `state` must pass
1717
An "operator" is any object that [`evaluate`](@ref) returns when evaluating a
1818
time-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,25 @@ 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+
4363
The function returns `true` for a valid operator and `false` for an invalid
4464
operator. Unless `quiet=true`, it will log an error to indicate which of the
4565
conditions failed.
@@ -74,20 +94,6 @@ function check_operator(
7494
success = false
7595
end
7696

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-
9197
try
9298
s = size(op)
9399
if !(s isa Tuple)
@@ -171,7 +177,6 @@ function check_operator(
171177
success = false
172178
end
173179

174-
175180
if supports_inplace(state)
176181

177182
try
@@ -254,6 +259,179 @@ function check_operator(
254259

255260
end
256261

262+
if supports_matrix_interface(op)
263+
264+
try
265+
T = eltype(op)
266+
if !(T isa Type && T <: Number)
267+
quiet || @error "$(px)`eltype(op)` must return a numeric type, not $T"
268+
success = false
269+
end
270+
catch exc
271+
quiet || @error(
272+
"$(px)`eltype(op)` must be defined.",
273+
exception = (exc, catch_abbreviated_backtrace())
274+
)
275+
success = false
276+
end
277+
278+
try
279+
s = size(op)
280+
if length(s) == 2 && all(d -> d > 0, s)
281+
val = op[1, 1]
282+
T = eltype(op)
283+
if T isa Type && T <: Number && !(val isa T)
284+
quiet ||
285+
@error "$(px)`op[1, 1]` must return a value of type `eltype(op)=$T`, not $(typeof(val))"
286+
success = false
287+
end
288+
end
289+
catch exc
290+
quiet || @error(
291+
"$(px)`getindex(op, i, j)` must be defined.",
292+
exception = (exc, catch_abbreviated_backtrace())
293+
)
294+
success = false
295+
end
296+
297+
try
298+
l = length(op)
299+
s = size(op)
300+
if l != prod(s)
301+
quiet ||
302+
@error "$(px)`length(op)` must equal `prod(size(op))`: $l$(prod(s))"
303+
success = false
304+
end
305+
catch exc
306+
quiet || @error(
307+
"$(px)`length(op)` must be defined.",
308+
exception = (exc, catch_abbreviated_backtrace())
309+
)
310+
success = false
311+
end
312+
313+
try
314+
itr = iterate(op)
315+
s = size(op)
316+
if isnothing(itr) && prod(s) > 0
317+
quiet ||
318+
@error "$(px)`iterate(op)` must not return `nothing` for a non-empty operator"
319+
success = false
320+
end
321+
catch exc
322+
quiet || @error(
323+
"$(px)`iterate(op)` must be defined.",
324+
exception = (exc, catch_abbreviated_backtrace())
325+
)
326+
success = false
327+
end
328+
329+
try
330+
op2 = similar(op)
331+
if !supports_inplace(op2)
332+
quiet ||
333+
@error "$(px)`similar(op)` must return a mutable array (`supports_inplace` must be `true`), got $(typeof(op2))"
334+
success = false
335+
end
336+
if size(op2) != size(op)
337+
quiet ||
338+
@error "$(px)`similar(op)` must return an array with the same shape: size $(size(op2))$(size(op))"
339+
success = false
340+
end
341+
if eltype(op2) != eltype(op)
342+
quiet ||
343+
@error "$(px)`similar(op)` must return an array with the same element type: $(eltype(op2))$(eltype(op))"
344+
success = false
345+
end
346+
catch exc
347+
quiet || @error(
348+
"$(px)`similar(op)` must be defined.",
349+
exception = (exc, catch_abbreviated_backtrace())
350+
)
351+
success = false
352+
end
353+
354+
try
355+
S = (eltype(op) == ComplexF64) ? ComplexF32 : ComplexF64
356+
op2 = similar(op, S)
357+
if !supports_inplace(op2)
358+
quiet ||
359+
@error "$(px)`similar(op, $S)` must return a mutable array (`supports_inplace` must be `true`), got $(typeof(op2))"
360+
success = false
361+
end
362+
if size(op2) != size(op)
363+
quiet ||
364+
@error "$(px)`similar(op, $S)` must return an array with the same shape: size $(size(op2))$(size(op))"
365+
success = false
366+
end
367+
if eltype(op2) != S
368+
quiet ||
369+
@error "$(px)`similar(op, $S)` must return an array with element type $S, got $(eltype(op2))"
370+
success = false
371+
end
372+
catch exc
373+
quiet || @error(
374+
"$(px)`similar(op, ::Type{S})` must be defined.",
375+
exception = (exc, catch_abbreviated_backtrace())
376+
)
377+
success = false
378+
end
379+
380+
try
381+
dims = size(op)
382+
op2 = similar(op, dims)
383+
if !supports_inplace(op2)
384+
quiet ||
385+
@error "$(px)`similar(op, dims)` must return a mutable array (`supports_inplace` must be `true`), got $(typeof(op2))"
386+
success = false
387+
end
388+
if size(op2) != dims
389+
quiet ||
390+
@error "$(px)`similar(op, dims)` must return an array with size $dims, got $(size(op2))"
391+
success = false
392+
end
393+
if eltype(op2) != eltype(op)
394+
quiet ||
395+
@error "$(px)`similar(op, dims)` must return an array with the same element type: $(eltype(op2))$(eltype(op))"
396+
success = false
397+
end
398+
catch exc
399+
quiet || @error(
400+
"$(px)`similar(op, dims::Dims)` must be defined.",
401+
exception = (exc, catch_abbreviated_backtrace())
402+
)
403+
success = false
404+
end
405+
406+
try
407+
S = (eltype(op) == ComplexF64) ? ComplexF32 : ComplexF64
408+
dims = size(op)
409+
op2 = similar(op, S, dims)
410+
if !supports_inplace(op2)
411+
quiet ||
412+
@error "$(px)`similar(op, $S, dims)` must return a mutable array (`supports_inplace` must be `true`), got $(typeof(op2))"
413+
success = false
414+
end
415+
if size(op2) != dims
416+
quiet ||
417+
@error "$(px)`similar(op, $S, dims)` must return an array with size $dims, got $(size(op2))"
418+
success = false
419+
end
420+
if eltype(op2) != S
421+
quiet ||
422+
@error "$(px)`similar(op, $S, dims)` must return an array with element type $S, got $(eltype(op2))"
423+
success = false
424+
end
425+
catch exc
426+
quiet || @error(
427+
"$(px)`similar(op, ::Type{S}, dims::Dims)` must be defined.",
428+
exception = (exc, catch_abbreviated_backtrace())
429+
)
430+
success = false
431+
end
432+
433+
end
434+
257435
return success
258436

259437
end

0 commit comments

Comments
 (0)