@@ -36,6 +36,14 @@ Subtypes of this abstract type should define the following methods:
3636"""
3737abstract type AbstractCrossoverTransformation end
3838
39+ """
40+ RotationMatrixBasedCrossoverTransformation
41+
42+ An abstract type representing a transformation applied to a candidate prior to applying
43+ the crossover operator which uses an orthogonal rotation matrix.
44+ """
45+ abstract type RotationMatrixBasedCrossoverTransformation <: AbstractCrossoverTransformation end
46+
3947"""
4048 NoTransformation
4149
@@ -62,7 +70,7 @@ DOI: [10.1016/j.asoc.2014.01.038](https://doi.org/10.1016/j.asoc.2014.01.038).
6270- `ct::Vector{Float64}`: The transformed candidate.
6371- `mt::Vector{Float64}`: The transformed mutant.
6472"""
65- struct CovarianceTransformation <: AbstractCrossoverTransformation
73+ struct CovarianceTransformation <: RotationMatrixBasedCrossoverTransformation
6674 ps:: Float64
6775 pb:: Float64
6876 B:: Matrix{Float64}
@@ -113,15 +121,110 @@ struct CovarianceTransformation <: AbstractCrossoverTransformation
113121 end
114122end
115123
124+ """
125+ UncorrelatedCovarianceTransformation{T<:AbstractCrossoverTransformation}
126+
127+ A transformation for performing crossover in the eigen-space of the covariance matrix of the
128+ best candidates in the population which are also not too closely correlated.
129+
130+ This is an implementation of the method proposed by Wang and Li in "Differential Evolution
131+ Based on Covariance Matrix Learning and Bimodal Distribution Parameter Setting, " 2014,
132+ DOI: [10.1016/j.asoc.2014.01.038](https://doi.org/10.1016/j.asoc.2014.01.038).
133+
134+ Correlation alteration based on "Covariance Matrix Learning Differential Evolution Algorithm Based on Correlation"
135+ DOI: https://doi.org/10.4236/ijis.2021.111002
136+
137+ # Fields
138+ - `ps::Float64`: The proportion of candidates to consider in the covariance matrix. That is,
139+ for a population size of `N` with `M` candidates remaining after the correlation check, the covariance matrix is calculated using the
140+ `clamp(ceil(ps * M), 2, M)` best candidates.
141+ - `pb::Float64`: The probability of applying the transformation.
142+ - `a::Float64`: The correlation threshold for which two candidates are considered 'too close' to both be used in the covariance matrix construction.
143+ - `B::Matrix{Float64}`: The real part of the eigenvectors of the covariance matrix.
144+ - `ct::Vector{Float64}`: The transformed candidate.
145+ - `mt::Vector{Float64}`: The transformed mutant.
146+ - `idxs::Vector{UInt16}`: A vector of indexes for the population
147+ - `cidxs::Vector{UInt16}`: A vector of unique `correlated` indexes for the population set for removal
148+ """
149+ struct UncorrelatedCovarianceTransformation <: RotationMatrixBasedCrossoverTransformation
150+ ps:: Float64
151+ pb:: Float64
152+ a:: Float64
153+ B:: Matrix{Float64}
154+
155+ # Preallocate storage for transformed candidate and mutant
156+ ct:: Vector{Float64}
157+ mt:: Vector{Float64}
158+
159+ # Preallocate storage for calculating transformation
160+ idxs:: Vector{UInt16}
161+
162+ cidxs:: Vector{UInt16}
163+
164+ @doc """
165+ UncorrelatedCovarianceTransformation{T<:AbstractCrossoverTransformation}
166+
167+ A transformation for performing crossover in the eigen-space of the covariance matrix of the
168+ best candidates in the population which are also not too closely correlated.
169+
170+ This is an implementation of the method proposed by Yuan and Feng in "Covariance Matrix
171+ Learning Differential Evolution Algorithm Based on Correlation"
172+ DOI: https://doi.org/10.4236/ijis.2021.111002
173+
174+ # Arguments
175+ - `pb::Float64`: The probability of applying the transformation.
176+ - `a::Float64`: The correlation threshold for two candidates being 'too close'.
177+ - `num_dims::Int`: The number of dimensions in the search space.
178+
179+ # Keyword Arguments:
180+ - `ps::Float64`: The proportion of candidates to consider in the covariance matrix.
181+ Defaults to 1.0 (i.e., all uncorrelated candidates are considered)
182+
183+ # Returns
184+ - `UncorrelatedCovarianceTransformation`: A `UncorrelatedCovarianceTransformation` object with the specified
185+ parameters.
186+
187+ # Examples
188+ ```julia-repl
189+ julia> using GlobalOptimization
190+ julia> transformation = UncorrelatedCovarianceTransformation(0.5, .95, 10; ps = 1.0)
191+ UncorrelatedCovarianceTransformation(1.0, 0.5, 0.95, [1.0630691323565e-311 1.0630691151907e-311 … 1.063069230316e-311 1.063069119645e-311; 1.0630691158705e-311 1.063069115333e-311 … 1.063069172704e-311 1.0630692904614e-311; … ; 1.063069115428e-311 1.063069114246e-311 … 1.063069124886e-311 1.0630694190924e-311; 1.0630691153804e-311 1.0630691141986e-311 … 1.0630691348624e-311 1.063069428614e-311], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], UInt16[], UInt16[])
192+ ```
193+ """
194+ function UncorrelatedCovarianceTransformation (pb, a, num_dims; ps = 1.0 )
195+ if ps <= 0.0 || ps > 1.0
196+ throw (ArgumentError (" ps must be in the range (0, 1]." ))
197+ end
198+ if pb <= 0.0 || pb > 1.0
199+ throw (ArgumentError (" pb must be in the range (0, 1]." ))
200+ end
201+ if a <= 0.0 || a > 1.0
202+ throw (ArgumentError (" a must be in the range (0, 1]." ))
203+ end
204+ B = Matrix {Float64} (undef, num_dims, num_dims)
205+ return new (ps, pb, a, B, zeros (num_dims), zeros (num_dims), Vector {UInt16} (undef, 0 ), Vector {UInt16} (undef, 0 ))
206+ end
207+
208+ end
209+
116210initialize! (transformation:: NoTransformation , population_size) = nothing
117- function initialize! (transformation:: CovarianceTransformation , population_size)
211+ function initialize! (transformation:: RotationMatrixBasedCrossoverTransformation , population_size)
212+ resize! (transformation. idxs, population_size)
213+ transformation. idxs .= 1 : population_size
214+ return nothing
215+ end
216+
217+ function initialize! (transformation:: UncorrelatedCovarianceTransformation , population_size)
118218 resize! (transformation. idxs, population_size)
219+ resize! (transformation. cidxs, population_size)
119220 transformation. idxs .= 1 : population_size
221+ empty! (transformation. cidxs)
120222 return nothing
121223end
122224
123225update_transformation! (transformation:: NoTransformation , population) = nothing
124226function update_transformation! (transformation:: CovarianceTransformation , population)
227+
125228 # Get number of candidates to consider in the covariance matrix
126229 n = clamp (ceil (Int, transformation. ps * length (population)), 2 , length (population))
127230
@@ -138,9 +241,76 @@ function update_transformation!(transformation::CovarianceTransformation, popula
138241
139242 return nothing
140243end
244+ function update_transformation! (transformation:: UncorrelatedCovarianceTransformation , population)
245+ # get correlation for each pair of vectors in population
246+ cor_mat = cor (stack (population. current_generation. candidates))
247+
248+ # store population_size
249+ pop_size = length (population)
250+
251+ if all_correlated (cor_mat, transformation. a)
252+ # If all correlated, set transform to identity and return
253+ # let's just set the transformation to identity as it doesn't
254+ # really make sense to compute the covariance matrix transformation given this
255+ # transformation is for specifically avoiding using correlated candidates when
256+ # computing the transformation.
257+ fill_identity! (transformation. B)
258+ return nothing
259+ else
260+ # lower triangular so that we only have unique pairs (excluding diagonal, since all elements are 1.0)
261+ tril! (cor_mat, - 1 )
262+
263+ # find points where two candidates are strongly correlated
264+
265+ @inbounds for j in axes (cor_mat,2 )
266+ for i in j+ 1 : size (cor_mat,1 )
267+ abs_x = abs (cor_mat[i, j])
268+ if abs_x >= transformation. a
269+ # Base.push!(transformation.pairs, SVector(i, j))
270+ if ! in (i, transformation. cidxs)
271+ Base. push! (transformation. cidxs, i)
272+ end
273+ end
274+ end
275+ end
276+
277+
278+ # if we're removing all but one idx, set transformation to identity and return
279+ if length (transformation. cidxs) >= length (transformation. idxs) - 1
280+ fill_identity! (transformation. B)
281+ return nothing
282+ end
283+
284+ # sort transformation.idxs in order of best candidate to worst candidate
285+ sortperm! (transformation. idxs, population. current_generation. candidates_fitness)
286+
287+ # remove candidates (note setdiff preserves order)
288+ setdiff! (transformation. idxs, transformation. cidxs)
289+
290+ # get number of candidates to use for covariance based on remaining candidates
291+ n = clamp (ceil (Int, transformation. ps * length (transformation. idxs)), 2 , length (transformation. idxs))
292+
293+ # Get indices of n best remaining candidates
294+ idxs = view (transformation. idxs, 1 : n)
295+
296+ # Calculate the covariance matrix for n best candidates
297+ C = cov (view (population. current_generation. candidates, idxs))
298+
299+ # Compute eigen decomposition
300+ E = eigen! (C)
301+ transformation. B .= real .(E. vectors)
302+
303+ # Reset preallocated storage
304+ empty! (transformation. cidxs)
305+ resize! (transformation. idxs, pop_size)
306+ transformation. idxs .= 1 : pop_size
307+ end
308+
309+ return nothing
310+ end
141311
142312to_transformed (transformation:: NoTransformation , c, m) = c, m, false
143- function to_transformed (transformation:: CovarianceTransformation , c, m)
313+ function to_transformed (transformation:: RotationMatrixBasedCrossoverTransformation , c, m)
144314 if rand () < transformation. pb
145315 mul! (transformation. ct, transpose (transformation. B), c)
146316 mul! (transformation. mt, transpose (transformation. B), m)
@@ -151,7 +321,7 @@ function to_transformed(transformation::CovarianceTransformation, c, m)
151321end
152322
153323from_transformed! (transformation:: NoTransformation , mt, m) = nothing
154- function from_transformed! (transformation:: CovarianceTransformation , mt, m)
324+ function from_transformed! (transformation:: RotationMatrixBasedCrossoverTransformation , mt, m)
155325 mul! (m, transformation. B, mt)
156326 return nothing
157327end
0 commit comments