Skip to content

Commit 015293d

Browse files
Merge pull request #256 from SciML/fm/ls
Added low sparsity init
2 parents 887a441 + c5d47fa commit 015293d

File tree

4 files changed

+121
-12
lines changed

4 files changed

+121
-12
lines changed

docs/src/api/inits.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@
2222
simple_cycle
2323
pseudo_svd
2424
chaotic_init
25+
low_connectivity
2526
```

src/ReservoirComputing.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export StandardRidge
3939
export scaled_rand, weighted_init, informed_init, minimal_init, chebyshev_mapping,
4040
logistic_mapping, modified_lm
4141
export rand_sparse, delay_line, delay_line_backward, cycle_jumps,
42-
simple_cycle, pseudo_svd, chaotic_init
42+
simple_cycle, pseudo_svd, chaotic_init, low_connectivity
4343
export RNN, MRNN, GRU, GRUParams, FullyGated, Minimal
4444
export train
4545
export ESN, HybridESN, KnowledgeModel, DeepESN

src/esn/esn_inits.jl

Lines changed: 117 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,19 @@ function throw_sparse_error(return_sparse)
1616
end
1717
end
1818

19+
## scale spectral radius
20+
21+
function scale_radius!(reservoir_matrix, radius)
22+
rho_w = maximum(abs.(eigvals(reservoir_matrix)))
23+
reservoir_matrix .*= radius / rho_w
24+
if Inf in unique(reservoir_matrix) || -Inf in unique(reservoir_matrix)
25+
error("""\n
26+
Sparsity too low for size of the matrix.
27+
Increase res_size or increase sparsity.\n
28+
""")
29+
end
30+
end
31+
1932
### input layers
2033
"""
2134
scaled_rand([rng], [T], dims...;
@@ -619,15 +632,7 @@ function rand_sparse(rng::AbstractRNG, ::Type{T}, dims::Integer...;
619632
throw_sparse_error(return_sparse)
620633
lcl_sparsity = T(1) - sparsity #consistency with current implementations
621634
reservoir_matrix = sparse_init(rng, T, dims...; sparsity=lcl_sparsity, std=std)
622-
rho_w = maximum(abs.(eigvals(reservoir_matrix)))
623-
reservoir_matrix .*= radius / rho_w
624-
if Inf in unique(reservoir_matrix) || -Inf in unique(reservoir_matrix)
625-
error("""\n
626-
Sparsity too low for size of the matrix.
627-
Increase res_size or increase sparsity.\n
628-
""")
629-
end
630-
635+
scale_radius!(reservoir_matrix, radius)
631636
return return_init_as(Val(return_sparse), reservoir_matrix)
632637
end
633638

@@ -1115,12 +1120,114 @@ function digital_chaotic_adjacency(rng::AbstractRNG, bit_precision::Integer;
11151120
return adjacency_matrix
11161121
end
11171122

1123+
"""
1124+
low_connectivity([rng], [T], dims...;
1125+
return_sparse = false, connected=false,
1126+
in_degree = 1, radius = 1.0, cut_cycle = false)
1127+
1128+
Construct an internal reservoir connectivity matrix with low connectivity.
1129+
1130+
This function creates a square reservoir matrix with the specified in-degree
1131+
for each node [^griffith2019]. When `in_degree` is 1, the function can enforce
1132+
a fully connected cycle if `connected` is `true`;
1133+
otherwise, it generates a random connectivity pattern.
1134+
1135+
# Arguments
1136+
1137+
- `rng`: Random number generator. Default is `Utils.default_rng()`
1138+
from WeightInitializers.
1139+
- `T`: Type of the elements in the reservoir matrix.
1140+
Default is `Float32`.
1141+
- `dims`: Dimensions of the reservoir matrix.
1142+
1143+
# Keyword Arguments
1144+
1145+
- `return_sparse`: If `true`, the function returns the
1146+
reservoir matrix as a sparse matrix. Default is `false`.
1147+
- `connected`: For `in_degree == 1`, if `true` a connected cycle is enforced.
1148+
Default is `false`.
1149+
- `in_degree`: The number of incoming connections per node.
1150+
Must not exceed the number of nodes. Default is 1.
1151+
- `radius`: The desired spectral radius of the reservoir.
1152+
Defaults to 1.0.
1153+
- `cut_cycle`: If `true`, removes one edge from the cycle to cut it.
1154+
Default is `false`.
1155+
1156+
[^griffith2019]: Griffith, Aaron, Andrew Pomerance, and Daniel J. Gauthier.
1157+
"Forecasting chaotic systems with very low connectivity reservoir computers."
1158+
Chaos: An Interdisciplinary Journal of Nonlinear Science 29.12 (2019).
1159+
"""
1160+
function low_connectivity(rng::AbstractRNG, ::Type{T}, dims::Integer...;
1161+
return_sparse::Bool=false, connected::Bool=false,
1162+
in_degree::Int=1, kwargs...) where {T <: Number}
1163+
res_size = dims[1]
1164+
if length(dims) != 2 || dims[1] != dims[2]
1165+
error("""
1166+
Internal reservoir matrix must be square. Got dims = $(dims)
1167+
""")
1168+
end
1169+
if in_degree > res_size
1170+
error("""
1171+
In-degree k (got k=$(in_degree)) cannot exceed number of nodes N=$(res_size)
1172+
""")
1173+
end
1174+
if in_degree == 1
1175+
reservoir_matrix = build_cycle(
1176+
Val(connected), rng, T, res_size; in_degree=in_degree, kwargs...)
1177+
else
1178+
reservoir_matrix = build_cycle(
1179+
Val(false), rng, T, res_size; in_degree=in_degree, kwargs...)
1180+
end
1181+
return return_init_as(Val(return_sparse), reservoir_matrix)
1182+
end
1183+
1184+
function build_cycle(::Val{false}, rng::AbstractRNG, ::Type{T}, res_size::Int;
1185+
in_degree::Integer=1, radius::T=T(1.0), cut_cycle::Bool=false) where {T <: Number}
1186+
reservoir_matrix = DeviceAgnostic.zeros(rng, T, res_size, res_size)
1187+
for i in 1:res_size
1188+
selected = randperm(rng, res_size)[1:in_degree]
1189+
for j in selected
1190+
reservoir_matrix[i, j] = T(randn(rng))
1191+
end
1192+
end
1193+
scale_radius!(reservoir_matrix, radius)
1194+
return reservoir_matrix
1195+
end
1196+
1197+
function build_cycle(::Val{true}, rng::AbstractRNG, ::Type{T}, res_size::Int;
1198+
in_degree::Integer=1, radius::T=T(1.0), cut_cycle::Bool=false) where {T <: Number}
1199+
reservoir_matrix = DeviceAgnostic.zeros(rng, T, res_size, res_size)
1200+
perm = randperm(rng, res_size)
1201+
for i in 1:(res_size - 1)
1202+
reservoir_matrix[perm[i], perm[i + 1]] = T(randn(rng))
1203+
end
1204+
reservoir_matrix[perm[res_size], perm[1]] = T(randn(rng))
1205+
scale_radius!(reservoir_matrix, radius)
1206+
if cut_cycle
1207+
cut_cycle_edge!(reservoir_matrix, rng)
1208+
end
1209+
return reservoir_matrix
1210+
end
1211+
1212+
function cut_cycle_edge!(
1213+
reservoir_matrix::AbstractMatrix{T}, rng::AbstractRNG) where {T <: Number}
1214+
res_size = size(reservoir_matrix, 1)
1215+
row = rand(rng, 1:res_size)
1216+
for j in 1:res_size
1217+
if reservoir_matrix[row, j] != zero(T)
1218+
reservoir_matrix[row, j] = zero(T)
1219+
break
1220+
end
1221+
end
1222+
return reservoir_matrix
1223+
end
1224+
11181225
### fallbacks
11191226
#fallbacks for initializers #eventually to remove once migrated to WeightInitializers.jl
11201227
for initializer in (:rand_sparse, :delay_line, :delay_line_backward, :cycle_jumps,
11211228
:simple_cycle, :pseudo_svd, :chaotic_init,
11221229
:scaled_rand, :weighted_init, :informed_init, :minimal_init, :chebyshev_mapping,
1123-
:logistic_mapping, :modified_lm)
1230+
:logistic_mapping, :modified_lm, :low_connectivity)
11241231
@eval begin
11251232
function ($initializer)(dims::Integer...; kwargs...)
11261233
return $initializer(Utils.default_rng(), Float32, dims...; kwargs...)

test/esn/test_inits.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ reservoir_inits = [
2525
cycle_jumps,
2626
simple_cycle,
2727
pseudo_svd,
28-
chaotic_init
28+
chaotic_init,
29+
low_connectivity
2930
]
3031
input_inits = [
3132
scaled_rand,

0 commit comments

Comments
 (0)