diff --git a/Project.toml b/Project.toml index b003aecd..fb57c37a 100644 --- a/Project.toml +++ b/Project.toml @@ -14,6 +14,7 @@ MLJLinearModels = "6ee0df7b-362f-4a72-a706-9e79364fb692" NNlib = "872c559c-99b0-510c-b3b7-b6c96a88d5cd" Optim = "429524aa-4258-5aef-a3af-852621145aeb" PartialFunctions = "570af359-4316-4cb7-8c74-252c00c2016b" +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" @@ -29,6 +30,7 @@ LinearAlgebra = "1.10" MLJLinearModels = "0.9.2" NNlib = "0.8.4, 0.9" Optim = "1" +PartialFunctions = "1.2" Random = "1" SafeTestsets = "0.1" SparseArrays = "1.10" diff --git a/src/ReservoirComputing.jl b/src/ReservoirComputing.jl index 84b0d145..c826b3f3 100644 --- a/src/ReservoirComputing.jl +++ b/src/ReservoirComputing.jl @@ -9,6 +9,8 @@ using LinearAlgebra using MLJLinearModels using NNlib using Optim +using PartialFunctions +using Random using SparseArrays using Statistics diff --git a/src/esn/esn_reservoirs.jl b/src/esn/esn_reservoirs.jl index 047fb0bf..fa57109b 100644 --- a/src/esn/esn_reservoirs.jl +++ b/src/esn/esn_reservoirs.jl @@ -23,11 +23,11 @@ This type of reservoir initialization is commonly used in ESNs for capturing tem function rand_sparse(rng::AbstractRNG, ::Type{T}, dims::Integer...; - radius = 1.0, - sparsity = 0.1) where {T <: Number} + radius = T(1.0), + sparsity = T(0.1)) where {T <: Number} reservoir_matrix = Matrix{T}(sprand(rng, dims..., sparsity)) - reservoir_matrix = 2.0 .* (reservoir_matrix .- 0.5) - replace!(reservoir_matrix, -1.0 => 0.0) + reservoir_matrix = T(2.0) .* (reservoir_matrix .- T(0.5)) + replace!(reservoir_matrix, T(-1.0) => T(0.0)) rho_w = maximum(abs.(eigvals(reservoir_matrix))) reservoir_matrix .*= radius / rho_w if Inf in unique(reservoir_matrix) || -Inf in unique(reservoir_matrix) @@ -62,7 +62,7 @@ Rodan, Ali, and Peter Tino. "Minimum complexity echo state network." IEEE Transa function delay_line(rng::AbstractRNG, ::Type{T}, dims::Integer...; - weight = 0.1) where {T <: Number} + weight = T(0.1)) where {T <: Number} reservoir_matrix = zeros(T, dims...) @assert length(dims) == 2 && dims[1] == dims[2], "The dimensions must define a square matrix (e.g., (100, 100))" diff --git a/test/esn/test_reservoirs.jl b/test/esn/test_reservoirs.jl index ac751712..df58bd3c 100644 --- a/test/esn/test_reservoirs.jl +++ b/test/esn/test_reservoirs.jl @@ -1,79 +1,36 @@ using ReservoirComputing +using LinearAlgebra +using Random +include("../utils.jl") const res_size = 20 const radius = 1.0 const sparsity = 0.1 const weight = 0.2 const jump_size = 3 +const rng = Random.default_rng() + +dtypes = [Float16, Float32, Float64] +reservoir_inits = [rand_sparse] + +@testset "Sizes and types" begin + for init in reservoir_inits + for dt in dtypes + #sizes + @test size(init(res_size, res_size)) == (res_size, res_size) + @test size(init(rng, res_size, res_size)) == (res_size, res_size) + #types + @test eltype(init(dt, res_size, res_size)) == dt + @test eltype(init(rng, dt, res_size, res_size)) == dt + #closure + cl = init(rng) + @test cl(dt, res_size, res_size) isa AbstractArray{dt} + end + end +end + +@testset "rand_sparse" begin + sp = rand_sparse(res_size, res_size) + @test check_radius(sp, radius) +end -#testing RandSparseReservoir implicit and esplicit constructors -reservoir_constructor = RandSparseReservoir(res_size, radius, sparsity) -reservoir_matrix = create_reservoir(reservoir_constructor, res_size) -@test size(reservoir_matrix) == (res_size, res_size) - -reservoir_constructor = RandSparseReservoir(res_size, radius = radius, sparsity = sparsity) -reservoir_matrix = create_reservoir(reservoir_constructor, res_size) -@test size(reservoir_matrix) == (res_size, res_size) - -#testing PseudoSVDReservoir implicit and esplicit constructors -reservoir_constructor = PseudoSVDReservoir(res_size, radius, sparsity) -reservoir_matrix = create_reservoir(reservoir_constructor, res_size) -@test size(reservoir_matrix) == (res_size, res_size) -@test maximum(reservoir_matrix) <= radius - -reservoir_constructor = PseudoSVDReservoir(res_size, max_value = radius, - sparsity = sparsity) -reservoir_matrix = create_reservoir(reservoir_constructor, res_size) -@test size(reservoir_matrix) == (res_size, res_size) -@test maximum(reservoir_matrix) <= radius - -#testing DelayLineReservoir implicit and esplicit constructors -reservoir_constructor = DelayLineReservoir(res_size, weight) -reservoir_matrix = create_reservoir(reservoir_constructor, res_size) -@test size(reservoir_matrix) == (res_size, res_size) -@test maximum(reservoir_matrix) == weight - -reservoir_constructor = DelayLineReservoir(res_size, weight = weight) -reservoir_matrix = create_reservoir(reservoir_constructor, res_size) -@test size(reservoir_matrix) == (res_size, res_size) -@test maximum(reservoir_matrix) == weight - -#testing DelayLineReservoir implicit and esplicit constructors -reservoir_constructor = DelayLineBackwardReservoir(res_size, weight, weight) -reservoir_matrix = create_reservoir(reservoir_constructor, res_size) -@test size(reservoir_matrix) == (res_size, res_size) -@test maximum(reservoir_matrix) == weight - -reservoir_constructor = DelayLineBackwardReservoir(res_size, weight = weight, - fb_weight = weight) -reservoir_matrix = create_reservoir(reservoir_constructor, res_size) -@test size(reservoir_matrix) == (res_size, res_size) -@test maximum(reservoir_matrix) == weight - -#testing SimpleCycleReservoir implicit and esplicit constructors -reservoir_constructor = SimpleCycleReservoir(res_size, weight) -reservoir_matrix = create_reservoir(reservoir_constructor, res_size) -@test size(reservoir_matrix) == (res_size, res_size) -@test maximum(reservoir_matrix) == weight - -reservoir_constructor = SimpleCycleReservoir(res_size, weight = weight) -reservoir_matrix = create_reservoir(reservoir_constructor, res_size) -@test size(reservoir_matrix) == (res_size, res_size) -@test maximum(reservoir_matrix) == weight - -#testing CycleJumpsReservoir implicit and esplicit constructors -reservoir_constructor = CycleJumpsReservoir(res_size, weight, weight, jump_size) -reservoir_matrix = create_reservoir(reservoir_constructor, res_size) -@test size(reservoir_matrix) == (res_size, res_size) -@test maximum(reservoir_matrix) == weight - -reservoir_constructor = CycleJumpsReservoir(res_size, cycle_weight = weight, - jump_weight = weight, jump_size = jump_size) -reservoir_matrix = create_reservoir(reservoir_constructor, res_size) -@test size(reservoir_matrix) == (res_size, res_size) -@test maximum(reservoir_matrix) == weight - -#testing NullReservoir constructors -reservoir_constructor = NullReservoir() -reservoir_matrix = create_reservoir(reservoir_constructor, res_size) -@test size(reservoir_matrix) == (res_size, res_size) diff --git a/test/utils.jl b/test/utils.jl new file mode 100644 index 00000000..9ef6f360 --- /dev/null +++ b/test/utils.jl @@ -0,0 +1,5 @@ +function check_radius(matrix, target_radius; tolerance=1e-5) + eigenvalues = eigvals(matrix) + spectral_radius = maximum(abs.(eigenvalues)) + return isapprox(spectral_radius, target_radius, atol=tolerance) +end \ No newline at end of file