@@ -16,6 +16,19 @@ function throw_sparse_error(return_sparse)
1616 end
1717end
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)
632637end
633638
@@ -1115,12 +1120,114 @@ function digital_chaotic_adjacency(rng::AbstractRNG, bit_precision::Integer;
11151120 return adjacency_matrix
11161121end
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
11201227for 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... )
0 commit comments