From 486b798ee99bf6d6c8a140b7b7ddf0cb32bfa1d2 Mon Sep 17 00:00:00 2001 From: MartinuzziFrancesco Date: Fri, 28 Mar 2025 17:37:54 +0100 Subject: [PATCH 1/3] fix: change error condition for sparse inits --- src/esn/inits_components.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/esn/inits_components.jl b/src/esn/inits_components.jl index 35a29cbb..56371a90 100644 --- a/src/esn/inits_components.jl +++ b/src/esn/inits_components.jl @@ -5,7 +5,7 @@ end # error for sparse inits with no SparseArrays.jl call function throw_sparse_error(return_sparse::Bool) - if return_sparse && !haskey(Base.loaded_modules, :SparseArrays) + if return_sparse && !isdefined(Main, :SparseArrays) error("""\n Sparse output requested but SparseArrays.jl is not loaded. Please load it with: From 0f7d40e28cc34f96bd9fdee78c5b46d2c5b400b0 Mon Sep 17 00:00:00 2001 From: MartinuzziFrancesco Date: Sat, 29 Mar 2025 12:34:21 +0100 Subject: [PATCH 2/3] refactor: group minimal initializers --- src/esn/esn_inits.jl | 929 ++++++++++++++++++++++--------------------- 1 file changed, 469 insertions(+), 460 deletions(-) diff --git a/src/esn/esn_inits.jl b/src/esn/esn_inits.jl index 7fe2fdfe..50efff6a 100644 --- a/src/esn/esn_inits.jl +++ b/src/esn/esn_inits.jl @@ -700,11 +700,12 @@ function rand_sparse(rng::AbstractRNG, ::Type{T}, dims::Integer...; end """ - delay_line([rng], [T], dims...; - weight=0.1, return_sparse=false, - kwargs...) + pseudo_svd([rng], [T], dims...; + max_value=1.0, sparsity=0.1, sorted=true, reverse_sort=false, + return_sparse=false) -Create and return a delay line reservoir matrix [^rodan2010]. +Returns an initializer to build a sparse reservoir matrix with the given +`sparsity` by using a pseudo-SVD approach as described in [^yang2018]. # Arguments @@ -716,70 +717,120 @@ Create and return a delay line reservoir matrix [^rodan2010]. # Keyword arguments - - `weight`: Determines the value of all connections in the reservoir. - This can be provided as a single value or an array. In case it is provided as an - array please make sure that the lenght of the array matches the lenght of the sub-diagonal - you want to populate. - Default is 0.1. - - `shift`: delay line shift. Default is 1. + - `max_value`: The maximum absolute value of elements in the matrix. + Default is 1.0 + - `sparsity`: The desired sparsity level of the reservoir matrix. + Default is 0.1 + - `sorted`: A boolean indicating whether to sort the singular values before + creating the diagonal matrix. Default is `true`. + - `reverse_sort`: A boolean indicating whether to reverse the sorted + singular values. Default is `false`. - `return_sparse`: flag for returning a `sparse` matrix. Default is `false`. - - `sampling_type`: Sampling that decides the distribution of `weight` negative numbers. - If set to `:no_sample` the sign is unchanged. If set to `:bernoulli_sample!` then each - `weight` can be positive with a probability set by `positive_prob`. If set to - `:irrational_sample!` the `weight` is negative if the decimal number of the - irrational number chosen is odd. Default is `:no_sample`. - - `positive_prob`: probability of the `weight` being positive with `sampling_type` - set to `:bernoulli_sample!`. Default is 0.5 - - `irrational`: Irrational number whose decimals decide the sign of `weight`. - Default is `pi`. - - `start`: Which place after the decimal point the counting starts for the `irrational` - sign counting. Default is 1. + - `return_diag`: flag for returning a `Diagonal` matrix. If both `return_diag` + and `return_sparse` are set to `true` priority is given to `return_diag`. + Default is `false`. # Examples ```jldoctest -julia> res_matrix = delay_line(5, 5) -5×5 Matrix{Float32}: - 0.0 0.0 0.0 0.0 0.0 - 0.1 0.0 0.0 0.0 0.0 - 0.0 0.1 0.0 0.0 0.0 - 0.0 0.0 0.1 0.0 0.0 - 0.0 0.0 0.0 0.1 0.0 - -julia> res_matrix = delay_line(5, 5; weight=1) +julia> res_matrix = pseudo_svd(5, 5) 5×5 Matrix{Float32}: - 0.0 0.0 0.0 0.0 0.0 - 1.0 0.0 0.0 0.0 0.0 - 0.0 1.0 0.0 0.0 0.0 - 0.0 0.0 1.0 0.0 0.0 - 0.0 0.0 0.0 1.0 0.0 + 0.306998 0.0 0.0 0.0 0.0 + 0.0 0.325977 0.0 0.0 0.0 + 0.0 0.0 0.549051 0.0 0.0 + 0.0 0.0 0.0 0.726199 0.0 + 0.0 0.0 0.0 0.0 1.0 ``` -[^rodan2010]: Rodan, Ali, and Peter Tino. - "Minimum complexity echo state network." - IEEE transactions on neural networks 22.1 (2010): 131-144. +[^yang2018]: Yang, Cuili, et al. + "_Design of polynomial echo state networks for time series prediction._" + Neurocomputing 290 (2018): 148-160. """ -function delay_line(rng::AbstractRNG, ::Type{T}, dims::Integer...; - weight::Union{Number, AbstractVector}=T(0.1), shift::Integer=1, - return_sparse::Bool=false, kwargs...) where {T <: Number} +function pseudo_svd(rng::AbstractRNG, ::Type{T}, dims::Integer...; + max_value::Number=T(1.0), sparsity::Number=0.1, sorted::Bool=true, + reverse_sort::Bool=false, return_sparse::Bool=false, + return_diag::Bool=false) where {T <: Number} throw_sparse_error(return_sparse) - @assert length(dims) == 2&&dims[1] == dims[2] """\n - The dimensions must define a square matrix - (e.g., (100, 100)) - """ - reservoir_matrix = DeviceAgnostic.zeros(rng, T, dims...) - delay_line!(rng, reservoir_matrix, T.(weight), shift; kwargs...) - return return_init_as(Val(return_sparse), reservoir_matrix) + reservoir_matrix = create_diag(rng, T, dims[1], + T(max_value); + sorted=sorted, + reverse_sort=reverse_sort) + tmp_sparsity = get_sparsity(reservoir_matrix, dims[1]) + + while tmp_sparsity <= sparsity + reservoir_matrix *= create_qmatrix(rng, T, dims[1], + rand_range(rng, T, dims[1]), + rand_range(rng, T, dims[1]), + DeviceAgnostic.rand(rng, T) * T(2) - T(1)) + tmp_sparsity = get_sparsity(reservoir_matrix, dims[1]) + end + + if return_diag + return Diagonal(reservoir_matrix) + else + return return_init_as(Val(return_sparse), reservoir_matrix) + end +end + +#hacky workaround for the moment +function rand_range(rng, T, n::Int) + return Int(1 + floor(DeviceAgnostic.rand(rng, T) * n)) +end + +function create_diag(rng::AbstractRNG, ::Type{T}, dim::Number, max_value::Number; + sorted::Bool=true, reverse_sort::Bool=false) where {T <: Number} + diagonal_matrix = DeviceAgnostic.zeros(rng, T, dim, dim) + if sorted == true + if reverse_sort == true + diagonal_values = sort( + DeviceAgnostic.rand(rng, T, dim) .* max_value; rev=true) + diagonal_values[1] = max_value + else + diagonal_values = sort(DeviceAgnostic.rand(rng, T, dim) .* max_value) + diagonal_values[end] = max_value + end + else + diagonal_values = DeviceAgnostic.rand(rng, T, dim) .* max_value + end + + for idx in 1:dim + diagonal_matrix[idx, idx] = diagonal_values[idx] + end + + return diagonal_matrix +end + +function create_qmatrix(rng::AbstractRNG, ::Type{T}, dim::Number, + coord_i::Number, coord_j::Number, theta::Number) where {T <: Number} + qmatrix = DeviceAgnostic.zeros(rng, T, dim, dim) + + for idx in 1:dim + qmatrix[idx, idx] = 1.0 + end + + qmatrix[coord_i, coord_i] = cos(T(theta)) + qmatrix[coord_j, coord_j] = cos(T(theta)) + qmatrix[coord_i, coord_j] = -sin(T(theta)) + qmatrix[coord_j, coord_i] = sin(T(theta)) + return qmatrix +end + +function get_sparsity(M, dim) + return size(M[M .!= 0], 1) / (dim * dim - size(M[M .!= 0], 1)) #nonzero/zero elements end """ - delay_line_backward([rng], [T], dims...; - weight=0.1, fb_weight=0.2, return_sparse=false, - delay_kwargs=(), fb_kwargs=()) + chaotic_init([rng], [T], dims...; + extra_edge_probability=T(0.1), spectral_radius=one(T), + return_sparse=false) -Create a delay line backward reservoir with the specified by `dims` and weights. -Creates a matrix with backward connections as described in [^rodan2010]. +Construct a chaotic reservoir matrix using a digital chaotic system [^xie2024]. + +The matrix topology is derived from a strongly connected adjacency +matrix based on a digital chaotic system operating at finite precision. +If the requested matrix order does not exactly match a valid order the +closest valid order is used. # Arguments @@ -791,79 +842,103 @@ Creates a matrix with backward connections as described in [^rodan2010]. # Keyword arguments - - `weight`: The weight determines the absolute value of - forward connections in the reservoir. - This can be provided as a single value or an array. In case it is provided as an - array please make sure that the lenght of the array matches the lenght of the sub-diagonal - you want to populate. - Default is 0.1 - - - `fb_weight`: Determines the absolute value of backward connections - in the reservoir. - This can be provided as a single value or an array. In case it is provided as an - array please make sure that the lenght of the array matches the lenght of the sub-diagonal - you want to populate. - Default is 0.2 - - `return_sparse`: flag for returning a `sparse` matrix. - Default is `false`. - - `delay_kwargs` and `fb_kwargs`: named tuples that control the kwargs for the - delay line weight and feedback weights respectively. The kwargs are as follows: - - + `sampling_type`: Sampling that decides the distribution of `weight` negative numbers. - If set to `:no_sample` the sign is unchanged. If set to `:bernoulli_sample!` then each - `weight` can be positive with a probability set by `positive_prob`. If set to - `:irrational_sample!` the `weight` is negative if the decimal number of the - irrational number chosen is odd. Default is `:no_sample`. - + `positive_prob`: probability of the `weight` being positive when `sampling_type` is - set to `:bernoulli_sample!`. Default is 0.5 - + `irrational`: Irrational number whose decimals decide the sign of `weight`. - Default is `pi`. - + `start`: Which place after the decimal point the counting starts for the `irrational` - sign counting. Default is 1. + - `extra_edge_probability`: Probability of adding extra random edges in + the adjacency matrix to enhance connectivity. Default is 0.1. + - `desired_spectral_radius`: The target spectral radius for the + reservoir matrix. Default is one. + - `return_sparse`: If `true`, the function returns the + reservoir matrix as a sparse matrix. Default is `false`. # Examples ```jldoctest -julia> res_matrix = delay_line_backward(5, 5) -5×5 Matrix{Float32}: - 0.0 0.2 0.0 0.0 0.0 - 0.1 0.0 0.2 0.0 0.0 - 0.0 0.1 0.0 0.2 0.0 - 0.0 0.0 0.1 0.0 0.2 - 0.0 0.0 0.0 0.1 0.0 - -julia> res_matrix = delay_line_backward(Float16, 5, 5) -5×5 Matrix{Float16}: - 0.0 0.2 0.0 0.0 0.0 - 0.1 0.0 0.2 0.0 0.0 - 0.0 0.1 0.0 0.2 0.0 - 0.0 0.0 0.1 0.0 0.2 - 0.0 0.0 0.0 0.1 0.0 +julia> res_matrix = chaotic_init(8, 8) +┌ Warning: +│ +│ Adjusting reservoir matrix order: +│ from 8 (requested) to 4 +│ based on computed bit precision = 1. +│ +└ @ ReservoirComputing ~/.julia/dev/ReservoirComputing/src/esn/esn_inits.jl:805 +4×4 SparseArrays.SparseMatrixCSC{Float32, Int64} with 6 stored entries: + ⋅ -0.600945 ⋅ ⋅ + ⋅ ⋅ 0.132667 2.21354 + ⋅ -2.60383 ⋅ -2.90391 + -0.578156 ⋅ ⋅ ⋅ ``` -[^rodan2010]: Rodan, Ali, and Peter Tino. - "Minimum complexity echo state network." - IEEE transactions on neural networks 22.1 (2010): 131-144. +[^xie2024]: Xie, Minzhi, Qianxue Wang, and Simin Yu. + "Time Series Prediction of ESN Based on Chebyshev Mapping and Strongly + Connected Topology." + Neural Processing Letters 56.1 (2024): 30. """ -function delay_line_backward(rng::AbstractRNG, ::Type{T}, dims::Integer...; - weight::Union{Number, AbstractVector}=T(0.1), - fb_weight::Union{Number, AbstractVector}=T(0.2), shift::Integer=1, - fb_shift::Integer=1, return_sparse::Bool=false, - delay_kwargs::NamedTuple=NamedTuple(), - fb_kwargs::NamedTuple=NamedTuple()) where {T <: Number} +function chaotic_init(rng::AbstractRNG, ::Type{T}, dims::Integer...; + extra_edge_probability::AbstractFloat=T(0.1), spectral_radius::AbstractFloat=one(T), + return_sparse::Bool=false) where {T <: Number} throw_sparse_error(return_sparse) - reservoir_matrix = DeviceAgnostic.zeros(rng, T, dims...) - delay_line!(rng, reservoir_matrix, T.(weight), shift; delay_kwargs...) - backward_connection!(rng, reservoir_matrix, T.(fb_weight), fb_shift; fb_kwargs...) + requested_order = first(dims) + if length(dims) > 1 && dims[2] != requested_order + @warn """\n + Using dims[1] = $requested_order for the chaotic reservoir matrix order.\n + """ + end + d_estimate = log2(requested_order) / 2 + d_floor = max(floor(Int, d_estimate), 1) + d_ceil = ceil(Int, d_estimate) + candidate_order_floor = 2^(2 * d_floor) + candidate_order_ceil = 2^(2 * d_ceil) + chosen_bit_precision = abs(candidate_order_floor - requested_order) <= + abs(candidate_order_ceil - requested_order) ? d_floor : d_ceil + actual_matrix_order = 2^(2 * chosen_bit_precision) + if actual_matrix_order != requested_order + @warn """\n + Adjusting reservoir matrix order: + from $requested_order (requested) to $actual_matrix_order + based on computed bit precision = $chosen_bit_precision. \n + """ + end + + random_weight_matrix = T(2) * rand(rng, T, actual_matrix_order, actual_matrix_order) .- + T(1) + adjacency_matrix = digital_chaotic_adjacency( + rng, chosen_bit_precision; extra_edge_probability=extra_edge_probability) + reservoir_matrix = random_weight_matrix .* adjacency_matrix + current_spectral_radius = maximum(abs, eigvals(reservoir_matrix)) + if current_spectral_radius != 0 + reservoir_matrix .*= spectral_radius / current_spectral_radius + end + return return_init_as(Val(return_sparse), reservoir_matrix) end +function digital_chaotic_adjacency(rng::AbstractRNG, bit_precision::Integer; + extra_edge_probability::AbstractFloat=0.1) + matrix_order = 2^(2 * bit_precision) + adjacency_matrix = DeviceAgnostic.zeros(rng, Int, matrix_order, matrix_order) + for row_index in 1:(matrix_order - 1) + adjacency_matrix[row_index, row_index + 1] = 1 + end + adjacency_matrix[matrix_order, 1] = 1 + for row_index in 1:matrix_order, column_index in 1:matrix_order + if row_index != column_index && rand(rng) < extra_edge_probability + adjacency_matrix[row_index, column_index] = 1 + end + end + + return adjacency_matrix +end + """ - cycle_jumps([rng], [T], dims...; - cycle_weight=0.1, jump_weight=0.1, jump_size=3, return_sparse=false, - cycle_kwargs=(), jump_kwargs=()) + low_connectivity([rng], [T], dims...; + return_sparse = false, connected=false, + in_degree = 1, radius = 1.0, cut_cycle = false) -Create a cycle jumps reservoir [^Rodan2012]. +Construct an internal reservoir connectivity matrix with low connectivity. + +This function creates a square reservoir matrix with the specified in-degree +for each node [^griffith2019]. When `in_degree` is 1, the function can enforce +a fully connected cycle if `connected` is `true`; +otherwise, it generates a random connectivity pattern. # Arguments @@ -873,97 +948,113 @@ Create a cycle jumps reservoir [^Rodan2012]. Default is `Float32`. - `dims`: Dimensions of the reservoir matrix. -# Keyword arguments - - - `cycle_weight`: The weight of cycle connections. - This can be provided as a single value or an array. In case it is provided as an - array please make sure that the lenght of the array matches the lenght of the cycle - you want to populate. - Default is 0.1. +# Keyword Arguments - - `jump_weight`: The weight of jump connections. - This can be provided as a single value or an array. In case it is provided as an - array please make sure that the lenght of the array matches the lenght of the jumps - you want to populate. - Default is 0.1. - - `jump_size`: The number of steps between jump connections. - Default is 3. - - `return_sparse`: flag for returning a `sparse` matrix. + - `return_sparse`: If `true`, the function returns the + reservoir matrix as a sparse matrix. Default is `false`. + - `connected`: For `in_degree == 1`, if `true` a connected cycle is enforced. + Default is `false`. + - `in_degree`: The number of incoming connections per node. + Must not exceed the number of nodes. Default is 1. + - `radius`: The desired spectral radius of the reservoir. + Defaults to 1.0. + - `cut_cycle`: If `true`, removes one edge from the cycle to cut it. Default is `false`. - - `cycle_kwargs` and `jump_kwargs`: named tuples that control the kwargs for the - cycle and jump weights respectively. The kwargs are as follows: - - + `sampling_type`: Sampling that decides the distribution of `weight` negative numbers. - If set to `:no_sample` the sign is unchanged. If set to `:bernoulli_sample!` then each - `weight` can be positive with a probability set by `positive_prob`. If set to - `:irrational_sample!` the `weight` is negative if the decimal number of the - irrational number chosen is odd. Default is `:no_sample`. - + `positive_prob`: probability of the `weight` being positive when `sampling_type` is - set to `:bernoulli_sample!`. Default is 0.5 - + `irrational`: Irrational number whose decimals decide the sign of `weight`. - Default is `pi`. - + `start`: Which place after the decimal point the counting starts for the `irrational` - sign counting. Default is 1. -# Examples +[^griffith2019]: Griffith, Aaron, Andrew Pomerance, and Daniel J. Gauthier. + "Forecasting chaotic systems with very low connectivity reservoir computers." + Chaos: An Interdisciplinary Journal of Nonlinear Science 29.12 (2019). +""" +function low_connectivity(rng::AbstractRNG, ::Type{T}, dims::Integer...; + return_sparse::Bool=false, connected::Bool=false, + in_degree::Integer=1, kwargs...) where {T <: Number} + res_size = dims[1] + if length(dims) != 2 || dims[1] != dims[2] + error(""" + Internal reservoir matrix must be square. Got dims = $(dims) + """) + end + if in_degree > res_size + error(""" + In-degree k (got k=$(in_degree)) cannot exceed number of nodes N=$(res_size) + """) + end + if in_degree == 1 + reservoir_matrix = build_cycle( + Val(connected), rng, T, res_size; in_degree=in_degree, kwargs...) + else + reservoir_matrix = build_cycle( + Val(false), rng, T, res_size; in_degree=in_degree, kwargs...) + end + return return_init_as(Val(return_sparse), reservoir_matrix) +end -```jldoctest -julia> res_matrix = cycle_jumps(5, 5) -5×5 Matrix{Float32}: - 0.0 0.0 0.0 0.1 0.1 - 0.1 0.0 0.0 0.0 0.0 - 0.0 0.1 0.0 0.0 0.0 - 0.1 0.0 0.1 0.0 0.0 - 0.0 0.0 0.0 0.1 0.0 +function build_cycle(::Val{false}, rng::AbstractRNG, ::Type{T}, res_size::Int; + in_degree::Integer=1, radius::Number=T(1.0), cut_cycle::Bool=false) where {T <: + Number} + reservoir_matrix = DeviceAgnostic.zeros(rng, T, res_size, res_size) + for idx in 1:res_size + selected = randperm(rng, res_size)[1:in_degree] + for jdx in selected + reservoir_matrix[idx, jdx] = T(randn(rng)) + end + end + reservoir_matrix = scale_radius!(reservoir_matrix, T(radius)) + return reservoir_matrix +end -julia> res_matrix = cycle_jumps(5, 5; jump_size=2) -5×5 Matrix{Float32}: - 0.0 0.0 0.1 0.0 0.1 - 0.1 0.0 0.0 0.0 0.0 - 0.1 0.1 0.0 0.0 0.1 - 0.0 0.0 0.1 0.0 0.0 - 0.0 0.0 0.1 0.1 0.0 -``` +function build_cycle(::Val{true}, rng::AbstractRNG, ::Type{T}, res_size::Int; + in_degree::Integer=1, radius::Number=T(1.0), cut_cycle::Bool=false) where {T <: + Number} + reservoir_matrix = DeviceAgnostic.zeros(rng, T, res_size, res_size) + perm = randperm(rng, res_size) + for idx in 1:(res_size - 1) + reservoir_matrix[perm[idx], perm[idx + 1]] = T(randn(rng)) + end + reservoir_matrix[perm[res_size], perm[1]] = T(randn(rng)) + reservoir_matrix = scale_radius!(reservoir_matrix, T(radius)) + if cut_cycle + cut_cycle_edge!(reservoir_matrix, rng) + end + return reservoir_matrix +end -[^rodan2012]: Rodan, Ali, and Peter Tiňo. - "Simple deterministically constructed cycle reservoirs with regular jumps." - Neural computation 24.7 (2012): 1822-1852. -""" -function cycle_jumps(rng::AbstractRNG, ::Type{T}, dims::Integer...; - cycle_weight::Union{Number, AbstractVector}=T(0.1), - jump_weight::Union{Number, AbstractVector}=T(0.1), - jump_size::Integer=3, return_sparse::Bool=false, - cycle_kwargs::NamedTuple=NamedTuple(), - jump_kwargs::NamedTuple=NamedTuple()) where {T <: Number} - throw_sparse_error(return_sparse) - res_size = first(dims) - reservoir_matrix = DeviceAgnostic.zeros(rng, T, dims...) - simple_cycle!(rng, reservoir_matrix, T.(cycle_weight); cycle_kwargs...) - add_jumps!(rng, reservoir_matrix, T.(jump_weight), jump_size; jump_kwargs...) - return return_init_as(Val(return_sparse), reservoir_matrix) +function cut_cycle_edge!( + reservoir_matrix::AbstractMatrix{T}, rng::AbstractRNG) where {T <: Number} + res_size = size(reservoir_matrix, 1) + row = rand(rng, 1:res_size) + for idx in 1:res_size + if reservoir_matrix[row, idx] != zero(T) + reservoir_matrix[row, idx] = zero(T) + break + end + end + return reservoir_matrix end """ - simple_cycle([rng], [T], dims...; + delay_line([rng], [T], dims...; weight=0.1, return_sparse=false, kwargs...) -Create a simple cycle reservoir [^rodan2010]. +Create and return a delay line reservoir matrix [^rodan2010]. # Arguments - `rng`: Random number generator. Default is `Utils.default_rng()` from WeightInitializers. - - `T`: Type of the elements in the reservoir matrix. Default is `Float32`. + - `T`: Type of the elements in the reservoir matrix. + Default is `Float32`. - `dims`: Dimensions of the reservoir matrix. # Keyword arguments - - `weight`: Weight of the connections in the reservoir matrix. + - `weight`: Determines the value of all connections in the reservoir. This can be provided as a single value or an array. In case it is provided as an - array please make sure that the lenght of the array matches the lenght of the cycle + array please make sure that the lenght of the array matches the lenght of the sub-diagonal you want to populate. Default is 0.1. + - `shift`: delay line shift. Default is 1. - `return_sparse`: flag for returning a `sparse` matrix. Default is `false`. - `sampling_type`: Sampling that decides the distribution of `weight` negative numbers. @@ -971,7 +1062,7 @@ Create a simple cycle reservoir [^rodan2010]. `weight` can be positive with a probability set by `positive_prob`. If set to `:irrational_sample!` the `weight` is negative if the decimal number of the irrational number chosen is odd. Default is `:no_sample`. - - `positive_prob`: probability of the `weight` being positive when `sampling_type` is + - `positive_prob`: probability of the `weight` being positive with `sampling_type` set to `:bernoulli_sample!`. Default is 0.5 - `irrational`: Irrational number whose decimals decide the sign of `weight`. Default is `pi`. @@ -981,43 +1072,47 @@ Create a simple cycle reservoir [^rodan2010]. # Examples ```jldoctest -julia> res_matrix = simple_cycle(5, 5) +julia> res_matrix = delay_line(5, 5) 5×5 Matrix{Float32}: - 0.0 0.0 0.0 0.0 0.1 + 0.0 0.0 0.0 0.0 0.0 0.1 0.0 0.0 0.0 0.0 0.0 0.1 0.0 0.0 0.0 0.0 0.0 0.1 0.0 0.0 0.0 0.0 0.0 0.1 0.0 -julia> res_matrix = simple_cycle(5, 5; weight=11) +julia> res_matrix = delay_line(5, 5; weight=1) 5×5 Matrix{Float32}: - 0.0 0.0 0.0 0.0 11.0 - 11.0 0.0 0.0 0.0 0.0 - 0.0 11.0 0.0 0.0 0.0 - 0.0 0.0 11.0 0.0 0.0 - 0.0 0.0 0.0 11.0 0.0 + 0.0 0.0 0.0 0.0 0.0 + 1.0 0.0 0.0 0.0 0.0 + 0.0 1.0 0.0 0.0 0.0 + 0.0 0.0 1.0 0.0 0.0 + 0.0 0.0 0.0 1.0 0.0 ``` [^rodan2010]: Rodan, Ali, and Peter Tino. "Minimum complexity echo state network." IEEE transactions on neural networks 22.1 (2010): 131-144. """ -function simple_cycle(rng::AbstractRNG, ::Type{T}, dims::Integer...; - weight::Union{Number, AbstractVector}=T(0.1), +function delay_line(rng::AbstractRNG, ::Type{T}, dims::Integer...; + weight::Union{Number, AbstractVector}=T(0.1), shift::Integer=1, return_sparse::Bool=false, kwargs...) where {T <: Number} throw_sparse_error(return_sparse) + @assert length(dims) == 2&&dims[1] == dims[2] """\n + The dimensions must define a square matrix + (e.g., (100, 100)) + """ reservoir_matrix = DeviceAgnostic.zeros(rng, T, dims...) - simple_cycle!(rng, reservoir_matrix, T.(weight); kwargs...) + delay_line!(rng, reservoir_matrix, T.(weight), shift; kwargs...) return return_init_as(Val(return_sparse), reservoir_matrix) end """ - pseudo_svd([rng], [T], dims...; - max_value=1.0, sparsity=0.1, sorted=true, reverse_sort=false, - return_sparse=false) + delay_line_backward([rng], [T], dims...; + weight=0.1, fb_weight=0.2, return_sparse=false, + delay_kwargs=(), fb_kwargs=()) -Returns an initializer to build a sparse reservoir matrix with the given -`sparsity` by using a pseudo-SVD approach as described in [^yang2018]. +Create a delay line backward reservoir with the specified by `dims` and weights. +Creates a matrix with backward connections as described in [^rodan2010]. # Arguments @@ -1029,120 +1124,81 @@ Returns an initializer to build a sparse reservoir matrix with the given # Keyword arguments - - `max_value`: The maximum absolute value of elements in the matrix. - Default is 1.0 - - `sparsity`: The desired sparsity level of the reservoir matrix. + - `weight`: The weight determines the absolute value of + forward connections in the reservoir. + This can be provided as a single value or an array. In case it is provided as an + array please make sure that the lenght of the array matches the lenght of the sub-diagonal + you want to populate. Default is 0.1 - - `sorted`: A boolean indicating whether to sort the singular values before - creating the diagonal matrix. Default is `true`. - - `reverse_sort`: A boolean indicating whether to reverse the sorted - singular values. Default is `false`. + + - `fb_weight`: Determines the absolute value of backward connections + in the reservoir. + This can be provided as a single value or an array. In case it is provided as an + array please make sure that the lenght of the array matches the lenght of the sub-diagonal + you want to populate. + Default is 0.2. + - `fb_shift`: How far the backward connection will be from the diagonal. + Default is 2. - `return_sparse`: flag for returning a `sparse` matrix. Default is `false`. - - `return_diag`: flag for returning a `Diagonal` matrix. If both `return_diag` - and `return_sparse` are set to `true` priority is given to `return_diag`. - Default is `false`. + - `delay_kwargs` and `fb_kwargs`: named tuples that control the kwargs for the + delay line weight and feedback weights respectively. The kwargs are as follows: + + + `sampling_type`: Sampling that decides the distribution of `weight` negative numbers. + If set to `:no_sample` the sign is unchanged. If set to `:bernoulli_sample!` then each + `weight` can be positive with a probability set by `positive_prob`. If set to + `:irrational_sample!` the `weight` is negative if the decimal number of the + irrational number chosen is odd. Default is `:no_sample`. + + `positive_prob`: probability of the `weight` being positive when `sampling_type` is + set to `:bernoulli_sample!`. Default is 0.5 + + `irrational`: Irrational number whose decimals decide the sign of `weight`. + Default is `pi`. + + `start`: Which place after the decimal point the counting starts for the `irrational` + sign counting. Default is 1. # Examples ```jldoctest -julia> res_matrix = pseudo_svd(5, 5) +julia> res_matrix = delay_line_backward(5, 5) 5×5 Matrix{Float32}: - 0.306998 0.0 0.0 0.0 0.0 - 0.0 0.325977 0.0 0.0 0.0 - 0.0 0.0 0.549051 0.0 0.0 - 0.0 0.0 0.0 0.726199 0.0 - 0.0 0.0 0.0 0.0 1.0 + 0.0 0.2 0.0 0.0 0.0 + 0.1 0.0 0.2 0.0 0.0 + 0.0 0.1 0.0 0.2 0.0 + 0.0 0.0 0.1 0.0 0.2 + 0.0 0.0 0.0 0.1 0.0 + +julia> res_matrix = delay_line_backward(Float16, 5, 5) +5×5 Matrix{Float16}: + 0.0 0.2 0.0 0.0 0.0 + 0.1 0.0 0.2 0.0 0.0 + 0.0 0.1 0.0 0.2 0.0 + 0.0 0.0 0.1 0.0 0.2 + 0.0 0.0 0.0 0.1 0.0 ``` -[^yang2018]: Yang, Cuili, et al. - "_Design of polynomial echo state networks for time series prediction._" - Neurocomputing 290 (2018): 148-160. +[^rodan2010]: Rodan, Ali, and Peter Tino. + "Minimum complexity echo state network." + IEEE transactions on neural networks 22.1 (2010): 131-144. """ -function pseudo_svd(rng::AbstractRNG, ::Type{T}, dims::Integer...; - max_value::Number=T(1.0), sparsity::Number=0.1, sorted::Bool=true, - reverse_sort::Bool=false, return_sparse::Bool=false, - return_diag::Bool=false) where {T <: Number} +function delay_line_backward(rng::AbstractRNG, ::Type{T}, dims::Integer...; + weight::Union{Number, AbstractVector}=T(0.1), + fb_weight::Union{Number, AbstractVector}=T(0.2), shift::Integer=1, + fb_shift::Integer=1, return_sparse::Bool=false, + delay_kwargs::NamedTuple=NamedTuple(), + fb_kwargs::NamedTuple=NamedTuple()) where {T <: Number} throw_sparse_error(return_sparse) - reservoir_matrix = create_diag(rng, T, dims[1], - T(max_value); - sorted=sorted, - reverse_sort=reverse_sort) - tmp_sparsity = get_sparsity(reservoir_matrix, dims[1]) - - while tmp_sparsity <= sparsity - reservoir_matrix *= create_qmatrix(rng, T, dims[1], - rand_range(rng, T, dims[1]), - rand_range(rng, T, dims[1]), - DeviceAgnostic.rand(rng, T) * T(2) - T(1)) - tmp_sparsity = get_sparsity(reservoir_matrix, dims[1]) - end - - if return_diag - return Diagonal(reservoir_matrix) - else - return return_init_as(Val(return_sparse), reservoir_matrix) - end -end - -#hacky workaround for the moment -function rand_range(rng, T, n::Int) - return Int(1 + floor(DeviceAgnostic.rand(rng, T) * n)) -end - -function create_diag(rng::AbstractRNG, ::Type{T}, dim::Number, max_value::Number; - sorted::Bool=true, reverse_sort::Bool=false) where {T <: Number} - diagonal_matrix = DeviceAgnostic.zeros(rng, T, dim, dim) - if sorted == true - if reverse_sort == true - diagonal_values = sort( - DeviceAgnostic.rand(rng, T, dim) .* max_value; rev=true) - diagonal_values[1] = max_value - else - diagonal_values = sort(DeviceAgnostic.rand(rng, T, dim) .* max_value) - diagonal_values[end] = max_value - end - else - diagonal_values = DeviceAgnostic.rand(rng, T, dim) .* max_value - end - - for idx in 1:dim - diagonal_matrix[idx, idx] = diagonal_values[idx] - end - - return diagonal_matrix -end - -function create_qmatrix(rng::AbstractRNG, ::Type{T}, dim::Number, - coord_i::Number, coord_j::Number, theta::Number) where {T <: Number} - qmatrix = DeviceAgnostic.zeros(rng, T, dim, dim) - - for idx in 1:dim - qmatrix[idx, idx] = 1.0 - end - - qmatrix[coord_i, coord_i] = cos(T(theta)) - qmatrix[coord_j, coord_j] = cos(T(theta)) - qmatrix[coord_i, coord_j] = -sin(T(theta)) - qmatrix[coord_j, coord_i] = sin(T(theta)) - return qmatrix -end - -function get_sparsity(M, dim) - return size(M[M .!= 0], 1) / (dim * dim - size(M[M .!= 0], 1)) #nonzero/zero elements + reservoir_matrix = DeviceAgnostic.zeros(rng, T, dims...) + delay_line!(rng, reservoir_matrix, T.(weight), shift; delay_kwargs...) + backward_connection!(rng, reservoir_matrix, T.(fb_weight), fb_shift; fb_kwargs...) + return return_init_as(Val(return_sparse), reservoir_matrix) end """ - chaotic_init([rng], [T], dims...; - extra_edge_probability=T(0.1), spectral_radius=one(T), - return_sparse=false) - -Construct a chaotic reservoir matrix using a digital chaotic system [^xie2024]. + cycle_jumps([rng], [T], dims...; + cycle_weight=0.1, jump_weight=0.1, jump_size=3, return_sparse=false, + cycle_kwargs=(), jump_kwargs=()) -The matrix topology is derived from a strongly connected adjacency -matrix based on a digital chaotic system operating at finite precision. -If the requested matrix order does not exactly match a valid order the -closest valid order is used. +Create a cycle jumps reservoir [^Rodan2012]. # Arguments @@ -1154,194 +1210,140 @@ closest valid order is used. # Keyword arguments - - `extra_edge_probability`: Probability of adding extra random edges in - the adjacency matrix to enhance connectivity. Default is 0.1. - - `desired_spectral_radius`: The target spectral radius for the - reservoir matrix. Default is one. - - `return_sparse`: If `true`, the function returns the - reservoir matrix as a sparse matrix. Default is `false`. + - `cycle_weight`: The weight of cycle connections. + This can be provided as a single value or an array. In case it is provided as an + array please make sure that the lenght of the array matches the lenght of the cycle + you want to populate. + Default is 0.1. + + - `jump_weight`: The weight of jump connections. + This can be provided as a single value or an array. In case it is provided as an + array please make sure that the lenght of the array matches the lenght of the jumps + you want to populate. + Default is 0.1. + - `jump_size`: The number of steps between jump connections. + Default is 3. + - `return_sparse`: flag for returning a `sparse` matrix. + Default is `false`. + - `cycle_kwargs` and `jump_kwargs`: named tuples that control the kwargs for the + cycle and jump weights respectively. The kwargs are as follows: + + + `sampling_type`: Sampling that decides the distribution of `weight` negative numbers. + If set to `:no_sample` the sign is unchanged. If set to `:bernoulli_sample!` then each + `weight` can be positive with a probability set by `positive_prob`. If set to + `:irrational_sample!` the `weight` is negative if the decimal number of the + irrational number chosen is odd. Default is `:no_sample`. + + `positive_prob`: probability of the `weight` being positive when `sampling_type` is + set to `:bernoulli_sample!`. Default is 0.5 + + `irrational`: Irrational number whose decimals decide the sign of `weight`. + Default is `pi`. + + `start`: Which place after the decimal point the counting starts for the `irrational` + sign counting. Default is 1. # Examples ```jldoctest -julia> res_matrix = chaotic_init(8, 8) -┌ Warning: -│ -│ Adjusting reservoir matrix order: -│ from 8 (requested) to 4 -│ based on computed bit precision = 1. -│ -└ @ ReservoirComputing ~/.julia/dev/ReservoirComputing/src/esn/esn_inits.jl:805 -4×4 SparseArrays.SparseMatrixCSC{Float32, Int64} with 6 stored entries: - ⋅ -0.600945 ⋅ ⋅ - ⋅ ⋅ 0.132667 2.21354 - ⋅ -2.60383 ⋅ -2.90391 - -0.578156 ⋅ ⋅ ⋅ +julia> res_matrix = cycle_jumps(5, 5) +5×5 Matrix{Float32}: + 0.0 0.0 0.0 0.1 0.1 + 0.1 0.0 0.0 0.0 0.0 + 0.0 0.1 0.0 0.0 0.0 + 0.1 0.0 0.1 0.0 0.0 + 0.0 0.0 0.0 0.1 0.0 + +julia> res_matrix = cycle_jumps(5, 5; jump_size=2) +5×5 Matrix{Float32}: + 0.0 0.0 0.1 0.0 0.1 + 0.1 0.0 0.0 0.0 0.0 + 0.1 0.1 0.0 0.0 0.1 + 0.0 0.0 0.1 0.0 0.0 + 0.0 0.0 0.1 0.1 0.0 ``` -[^xie2024]: Xie, Minzhi, Qianxue Wang, and Simin Yu. - "Time Series Prediction of ESN Based on Chebyshev Mapping and Strongly - Connected Topology." - Neural Processing Letters 56.1 (2024): 30. +[^rodan2012]: Rodan, Ali, and Peter Tiňo. + "Simple deterministically constructed cycle reservoirs with regular jumps." + Neural computation 24.7 (2012): 1822-1852. """ -function chaotic_init(rng::AbstractRNG, ::Type{T}, dims::Integer...; - extra_edge_probability::AbstractFloat=T(0.1), spectral_radius::AbstractFloat=one(T), - return_sparse::Bool=false) where {T <: Number} +function cycle_jumps(rng::AbstractRNG, ::Type{T}, dims::Integer...; + cycle_weight::Union{Number, AbstractVector}=T(0.1), + jump_weight::Union{Number, AbstractVector}=T(0.1), + jump_size::Integer=3, return_sparse::Bool=false, + cycle_kwargs::NamedTuple=NamedTuple(), + jump_kwargs::NamedTuple=NamedTuple()) where {T <: Number} throw_sparse_error(return_sparse) - requested_order = first(dims) - if length(dims) > 1 && dims[2] != requested_order - @warn """\n - Using dims[1] = $requested_order for the chaotic reservoir matrix order.\n - """ - end - d_estimate = log2(requested_order) / 2 - d_floor = max(floor(Int, d_estimate), 1) - d_ceil = ceil(Int, d_estimate) - candidate_order_floor = 2^(2 * d_floor) - candidate_order_ceil = 2^(2 * d_ceil) - chosen_bit_precision = abs(candidate_order_floor - requested_order) <= - abs(candidate_order_ceil - requested_order) ? d_floor : d_ceil - actual_matrix_order = 2^(2 * chosen_bit_precision) - if actual_matrix_order != requested_order - @warn """\n - Adjusting reservoir matrix order: - from $requested_order (requested) to $actual_matrix_order - based on computed bit precision = $chosen_bit_precision. \n - """ - end - - random_weight_matrix = T(2) * rand(rng, T, actual_matrix_order, actual_matrix_order) .- - T(1) - adjacency_matrix = digital_chaotic_adjacency( - rng, chosen_bit_precision; extra_edge_probability=extra_edge_probability) - reservoir_matrix = random_weight_matrix .* adjacency_matrix - current_spectral_radius = maximum(abs, eigvals(reservoir_matrix)) - if current_spectral_radius != 0 - reservoir_matrix .*= spectral_radius / current_spectral_radius - end - + res_size = first(dims) + reservoir_matrix = DeviceAgnostic.zeros(rng, T, dims...) + simple_cycle!(rng, reservoir_matrix, T.(cycle_weight); cycle_kwargs...) + add_jumps!(rng, reservoir_matrix, T.(jump_weight), jump_size; jump_kwargs...) return return_init_as(Val(return_sparse), reservoir_matrix) end -function digital_chaotic_adjacency(rng::AbstractRNG, bit_precision::Integer; - extra_edge_probability::AbstractFloat=0.1) - matrix_order = 2^(2 * bit_precision) - adjacency_matrix = DeviceAgnostic.zeros(rng, Int, matrix_order, matrix_order) - for row_index in 1:(matrix_order - 1) - adjacency_matrix[row_index, row_index + 1] = 1 - end - adjacency_matrix[matrix_order, 1] = 1 - for row_index in 1:matrix_order, column_index in 1:matrix_order - if row_index != column_index && rand(rng) < extra_edge_probability - adjacency_matrix[row_index, column_index] = 1 - end - end - - return adjacency_matrix -end - """ - low_connectivity([rng], [T], dims...; - return_sparse = false, connected=false, - in_degree = 1, radius = 1.0, cut_cycle = false) - -Construct an internal reservoir connectivity matrix with low connectivity. + simple_cycle([rng], [T], dims...; + weight=0.1, return_sparse=false, + kwargs...) -This function creates a square reservoir matrix with the specified in-degree -for each node [^griffith2019]. When `in_degree` is 1, the function can enforce -a fully connected cycle if `connected` is `true`; -otherwise, it generates a random connectivity pattern. +Create a simple cycle reservoir [^rodan2010]. # Arguments - `rng`: Random number generator. Default is `Utils.default_rng()` from WeightInitializers. - - `T`: Type of the elements in the reservoir matrix. - Default is `Float32`. + - `T`: Type of the elements in the reservoir matrix. Default is `Float32`. - `dims`: Dimensions of the reservoir matrix. -# Keyword Arguments +# Keyword arguments - - `return_sparse`: If `true`, the function returns the - reservoir matrix as a sparse matrix. Default is `false`. - - `connected`: For `in_degree == 1`, if `true` a connected cycle is enforced. - Default is `false`. - - `in_degree`: The number of incoming connections per node. - Must not exceed the number of nodes. Default is 1. - - `radius`: The desired spectral radius of the reservoir. - Defaults to 1.0. - - `cut_cycle`: If `true`, removes one edge from the cycle to cut it. + - `weight`: Weight of the connections in the reservoir matrix. + This can be provided as a single value or an array. In case it is provided as an + array please make sure that the lenght of the array matches the lenght of the cycle + you want to populate. + Default is 0.1. + - `return_sparse`: flag for returning a `sparse` matrix. Default is `false`. + - `sampling_type`: Sampling that decides the distribution of `weight` negative numbers. + If set to `:no_sample` the sign is unchanged. If set to `:bernoulli_sample!` then each + `weight` can be positive with a probability set by `positive_prob`. If set to + `:irrational_sample!` the `weight` is negative if the decimal number of the + irrational number chosen is odd. Default is `:no_sample`. + - `positive_prob`: probability of the `weight` being positive when `sampling_type` is + set to `:bernoulli_sample!`. Default is 0.5 + - `irrational`: Irrational number whose decimals decide the sign of `weight`. + Default is `pi`. + - `start`: Which place after the decimal point the counting starts for the `irrational` + sign counting. Default is 1. -[^griffith2019]: Griffith, Aaron, Andrew Pomerance, and Daniel J. Gauthier. - "Forecasting chaotic systems with very low connectivity reservoir computers." - Chaos: An Interdisciplinary Journal of Nonlinear Science 29.12 (2019). -""" -function low_connectivity(rng::AbstractRNG, ::Type{T}, dims::Integer...; - return_sparse::Bool=false, connected::Bool=false, - in_degree::Integer=1, kwargs...) where {T <: Number} - res_size = dims[1] - if length(dims) != 2 || dims[1] != dims[2] - error(""" - Internal reservoir matrix must be square. Got dims = $(dims) - """) - end - if in_degree > res_size - error(""" - In-degree k (got k=$(in_degree)) cannot exceed number of nodes N=$(res_size) - """) - end - if in_degree == 1 - reservoir_matrix = build_cycle( - Val(connected), rng, T, res_size; in_degree=in_degree, kwargs...) - else - reservoir_matrix = build_cycle( - Val(false), rng, T, res_size; in_degree=in_degree, kwargs...) - end - return return_init_as(Val(return_sparse), reservoir_matrix) -end +# Examples -function build_cycle(::Val{false}, rng::AbstractRNG, ::Type{T}, res_size::Int; - in_degree::Integer=1, radius::Number=T(1.0), cut_cycle::Bool=false) where {T <: - Number} - reservoir_matrix = DeviceAgnostic.zeros(rng, T, res_size, res_size) - for idx in 1:res_size - selected = randperm(rng, res_size)[1:in_degree] - for jdx in selected - reservoir_matrix[idx, jdx] = T(randn(rng)) - end - end - reservoir_matrix = scale_radius!(reservoir_matrix, T(radius)) - return reservoir_matrix -end +```jldoctest +julia> res_matrix = simple_cycle(5, 5) +5×5 Matrix{Float32}: + 0.0 0.0 0.0 0.0 0.1 + 0.1 0.0 0.0 0.0 0.0 + 0.0 0.1 0.0 0.0 0.0 + 0.0 0.0 0.1 0.0 0.0 + 0.0 0.0 0.0 0.1 0.0 -function build_cycle(::Val{true}, rng::AbstractRNG, ::Type{T}, res_size::Int; - in_degree::Integer=1, radius::Number=T(1.0), cut_cycle::Bool=false) where {T <: - Number} - reservoir_matrix = DeviceAgnostic.zeros(rng, T, res_size, res_size) - perm = randperm(rng, res_size) - for idx in 1:(res_size - 1) - reservoir_matrix[perm[idx], perm[idx + 1]] = T(randn(rng)) - end - reservoir_matrix[perm[res_size], perm[1]] = T(randn(rng)) - reservoir_matrix = scale_radius!(reservoir_matrix, T(radius)) - if cut_cycle - cut_cycle_edge!(reservoir_matrix, rng) - end - return reservoir_matrix -end +julia> res_matrix = simple_cycle(5, 5; weight=11) +5×5 Matrix{Float32}: + 0.0 0.0 0.0 0.0 11.0 + 11.0 0.0 0.0 0.0 0.0 + 0.0 11.0 0.0 0.0 0.0 + 0.0 0.0 11.0 0.0 0.0 + 0.0 0.0 0.0 11.0 0.0 +``` -function cut_cycle_edge!( - reservoir_matrix::AbstractMatrix{T}, rng::AbstractRNG) where {T <: Number} - res_size = size(reservoir_matrix, 1) - row = rand(rng, 1:res_size) - for idx in 1:res_size - if reservoir_matrix[row, idx] != zero(T) - reservoir_matrix[row, idx] = zero(T) - break - end - end - return reservoir_matrix +[^rodan2010]: Rodan, Ali, and Peter Tino. + "Minimum complexity echo state network." + IEEE transactions on neural networks 22.1 (2010): 131-144. +""" +function simple_cycle(rng::AbstractRNG, ::Type{T}, dims::Integer...; + weight::Union{Number, AbstractVector}=T(0.1), + return_sparse::Bool=false, kwargs...) where {T <: Number} + throw_sparse_error(return_sparse) + reservoir_matrix = DeviceAgnostic.zeros(rng, T, dims...) + simple_cycle!(rng, reservoir_matrix, T.(weight); kwargs...) + return return_init_as(Val(return_sparse), reservoir_matrix) end """ @@ -1579,9 +1581,9 @@ end @doc raw""" selfloop_delayline_backward([rng], [T], dims...; - weight=0.1, selfloop_weight=0.1, - return_sparse=false, fb_kwargs=(), selfloop_kwargs=(), - delay_kwargs=()) + weight=0.1, selfloop_weight=0.1, fb_weight=0.1, + fb_shift=2, return_sparse=false, fb_kwargs=(), + selfloop_kwargs=(), delay_kwargs=()) Creates a reservoir based on a delay line with the addition of self loops and backward connections shifted by one [^elsarraj2019]. @@ -1619,6 +1621,13 @@ W_{i,j} = array please make sure that the lenght of the array matches the lenght of the diagonal you want to populate. Default is 0.1. + - `fb_weight`: Weight of the feedback in the reservoir matrix. + This can be provided as a single value or an array. In case it is provided as an + array please make sure that the lenght of the array matches the lenght of the diagonal + you want to populate. + Default is 0.1. + - `fb_shift`: How far the backward connection will be from the diagonal. + Default is 2. - `return_sparse`: flag for returning a `sparse` matrix. Default is `false`. - `delay_kwargs`, `selfloop_kwargs`, and `fb_kwargs`: named tuples that control the kwargs From 0ac181f558951a0f0ba3336d69a4d985a78b1a8e Mon Sep 17 00:00:00 2001 From: MartinuzziFrancesco Date: Sat, 29 Mar 2025 12:36:00 +0100 Subject: [PATCH 3/3] fix: adjust docstrings in minimal_init --- src/esn/esn_inits.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/esn/esn_inits.jl b/src/esn/esn_inits.jl index 50efff6a..a339d21b 100644 --- a/src/esn/esn_inits.jl +++ b/src/esn/esn_inits.jl @@ -310,7 +310,7 @@ The sign difference is randomly determined by the `sampling` chosen. - `irrational`: Irrational number chosen for sampling if `sampling_type=:irrational`. Default is `pi`. - `start`: Starting value for the irrational sample. Default is 1 - - `p`: Probability for the Bernoulli sampling. Lower probability increases negative + - `positive_prob`: Probability for the Bernoulli sampling. Lower probability increases negative value. Higher probability increases positive values. Default is 0.5 # Examples