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