Skip to content

Commit b135548

Browse files
committed
added new combinatorial stuff:
- crossover: subset selection (SSX) - mutation: `replace` - problem: 120 bits subset selection
1 parent 33c3d4f commit b135548

File tree

9 files changed

+154
-16
lines changed

9 files changed

+154
-16
lines changed

docs/src/crossover.md

+8-5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ All recombination operations have following call interface: `recombination(i1, i
1010

1111
## Operations
1212

13+
### ES Crossovers
14+
1315
List of the ES strategy recombination operations:
1416

1517
```@docs
@@ -23,7 +25,7 @@ average(population::Vector{T}) where {T <: AbstractVector}
2325
marriage
2426
```
2527

26-
Binary crossovers:
28+
### Binary Crossovers
2729

2830
```@docs
2931
SPX
@@ -35,7 +37,7 @@ EXPX
3537
BSX
3638
```
3739

38-
Real-valued crossovers:
40+
### Real-valued Crossovers
3941

4042
```@docs
4143
identity
@@ -50,17 +52,18 @@ MILX
5052
SBX
5153
```
5254

53-
Combinatorial crossovers:
55+
### Combinatorial Crossovers
5456

5557
```@docs
5658
PMX
5759
OX1
5860
CX
5961
OX2
6062
POS
63+
SSX
6164
```
6265

63-
Tree (expression) crossovers:
66+
### Tree (expression) Crossovers
6467

6568
```@docs
6669
Evolutionary.crosstree
@@ -80,4 +83,4 @@ Evolutionary.crosstree
8083

8184
[^6]: K. Deb, R. B. Agrawal, "Simulated Binary Crossover for Continuous Search Space", Complex Syst., 9., 1995
8285

83-
[^7]: M. A. Wolters, “A Genetic Algorithm for Selection of Fixed-Size Subsets with Application to Design Problems”, J. Stat. Soft., vol. 68, no. 1, pp. 1–18, Nov. 2015.
86+
[^7]: M. A. Wolters, “A Genetic Algorithm for Selection of Fixed-Size Subsets with Application to Design Problems”, J. Stat. Soft., vol. 68, no. 1, pp. 1–18, Nov. 2015.

docs/src/mutation.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ insertion
5757
swap2
5858
scramble
5959
shifting
60+
Evolutionary.replace
6061
```
6162

6263
## Differential Evolution
@@ -92,4 +93,4 @@ shrink
9293

9394
[^8]: P. J. Angeline, "An investigation into the sensitivity of genetic programming to the frequency of leaf selection during subtree crossover", Genetic Programming 1996: Proceedings of the First Annual Conference, 21–29, 1996.
9495

95-
[^9]: K. Deb, R. B. Agrawal, "Simulated Binary Crossover for Continuous Search Space", Complex Syst., 9., 1995
96+
[^9]: K. Deb, R. B. Agrawal, "Simulated Binary Crossover for Continuous Search Space", Complex Syst., 9., 1995

examples/120bits.jl

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
"""
2+
Problem: 120-bit sparse subset
3+
4+
Let a 120-bit string be b = [b_1|b_2|···|b_20], where b_i, i = 1, 2, ..., 20 are
5+
six-bit substrings referred to as blocks. Define the sequence `000001` to be
6+
the target configuration for a block, and denote this configuration by b_t.
7+
The objective function is defined such that the optimal solution is
8+
b* = [b_t|b_t|···|b_t], that is, the string with all 20 blocks having the target
9+
configuration.
10+
11+
This solution can be expressed as the index vector v* = [6, 12, 18,...,120].
12+
"""
13+
14+
using Evolutionary, Test, Random
15+
16+
17+
function fitness(oset, bsize = 6)
18+
scores = Dict(i => (i != bsize ? 3i : 20) for i in 0:bsize)
19+
# count 1s in blocks
20+
ht = Dict{Int,Int}()
21+
sl = sr = 0
22+
for i in oset
23+
if i > 60
24+
sr += 1
25+
else
26+
sl += 1
27+
end
28+
blk = div(i, bsize)
29+
blk -= i % bsize == 0 ? 1 : 0
30+
if haskey(ht, blk)
31+
ht[blk] += 1
32+
else
33+
ht[blk] = 1
34+
end
35+
end
36+
# calculate total score
37+
ss = 0
38+
for blk in sort(collect(keys(ht)))
39+
cnt = ht[blk]
40+
if cnt == 7
41+
println(oset)
42+
println(ht)
43+
error("Problem")
44+
end
45+
ss += scores[cnt]
46+
if (cnt == 1) && ((blk+1)*bsize in oset)
47+
ss += 9
48+
end
49+
#println("$blk, $cnt, $(scores[cnt]), $ss")
50+
end
51+
ss -= 2(sl > 13 ? sl : 0)
52+
ss -= 2(sr > 13 ? sr : 0)
53+
ss
54+
end
55+
56+
# Bit string examples
57+
bstr = "000100 001010 000010 111111 000100 101000 000010 000000 000001 000000 000010 000001 001000 000000 000000 010000 000000 000010 000000 000000"
58+
idxs = [i for (i,j) in enumerate(filter(c->c != ' ', bstr)) if j == '1']
59+
@test fitness(idxs) == 50
60+
61+
# optimal solution
62+
idxs_opt = [6*i for i in 1:20]
63+
@test fitness(idxs_opt) == 240
64+
65+
# GA solution
66+
mthd = GA(populationSize = 100, selection = tournament(5),
67+
crossover = SSX, crossoverRate = 0.99,
68+
mutation = replace(collect(1:120)), mutationRate = 0.1)
69+
70+
Random.seed!(42)
71+
opts = Evolutionary.Options(show_trace=false, successive_f_tol=30)
72+
init_pop = ()->randperm(120)[1:20]
73+
res = Evolutionary.optimize(idxs->-fitness(idxs), init_pop, mthd, opts)
74+
println(res)
75+
Evolutionary.minimizer(res) |> sort
76+

src/Evolutionary.jl

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ module Evolutionary
66
using NLSolversBase: NLSolversBase, AbstractObjective, ConstraintBounds,
77
AbstractConstraints, nconstraints_x, nconstraints
88
import NLSolversBase: f_calls, value, value!, value!!
9-
import Base: show, copy, minimum, summary, identity, getproperty, rand, getindex, length, copyto!, setindex!
9+
import Base: show, copy, minimum, summary, identity, getproperty, rand, getindex, length, copyto!, setindex!, replace
1010

1111
export AbstractStrategy, strategy, mutationwrapper,
1212
IsotropicStrategy, AnisotropicStrategy, NoStrategy,
@@ -17,14 +17,14 @@ module Evolutionary
1717
gaussian, cauchy,
1818
# GA mutations
1919
flip, bitinversion, uniform, BGA, inversion, insertion, swap2, scramble,
20-
shifting, PM, MIPM, PLM,
20+
shifting, PM, MIPM, PLM, replace,
2121
# ES recombinations
2222
average, marriage,
2323
# GA recombinations
2424
SPX, TPX, UX, SHFX,
2525
DC, AX, WAX, IC, LC, HX, LX, MILX, SBX,
2626
PMX, OX1, CX, OX2, POS,
27-
BSX,
27+
BSX, SSX,
2828
# GA selections
2929
ranklinear, uniformranking, roulette, rouletteinv, sus, susinv,
3030
tournament, truncation,

src/mutations.jl

+31
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,36 @@ function shifting(recombinant::T; rng::AbstractRNG=Random.GLOBAL_RNG) where {T <
356356
return recombinant
357357
end
358358

359+
"""
360+
replace(pool,[minchange=1])(recombinant)
361+
362+
Replacement mutation operator changes an arbitrary number, no smaller then `minchange`,
363+
of elements in the individual by replacing them with elements from the predefined `pool` that are not in the individual.
364+
"""
365+
function replace(pool::Vector{P}; minchange=1) where {P}
366+
function rplc(recombinant::T; rng::AbstractRNG=Random.GLOBAL_RNG) where {T <: AbstractVector}
367+
l = length(recombinant)
368+
p = length(pool)
369+
# how many values to change
370+
nchg = max(minchange, rand(rng, 1:min(l, p-l)))
371+
# select new elements
372+
idxs = randperm(rng, p)
373+
new_vals = P[]
374+
for i in idxs
375+
if pool[i] recombinant
376+
push!(new_vals, pool[i])
377+
end
378+
length(new_vals) == nchg && break
379+
end
380+
# update arbitrary positions with new values
381+
new_idxs = randperm(rng, l)[1:nchg]
382+
recombinant[new_idxs] .= new_vals
383+
return recombinant
384+
end
385+
return rplc
386+
end
387+
388+
359389
# Differential Evolution
360390
# ======================
361391

@@ -376,6 +406,7 @@ function differentiation(recombinant::T, mutators::AbstractVector{T}; F::Real =
376406
return recombinant
377407
end
378408

409+
379410
# Genetic Programming
380411
# ======================
381412

src/recombinations.jl

+25-6
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ end
9595
"""
9696
SHFX(v1, v2)
9797
98-
Shuffle crossover between the parents `v1` and `v2` that performs recombination similar to (SPX)[@ref] preliminary shuffling these parents.
98+
Shuffle crossover between the parents `v1` and `v2` that performs recombination similar to [`SPX`](@ref) preliminary shuffling these parents.
9999
"""
100100
function SHFX(v1::T, v2::T; rng::AbstractRNG=Random.GLOBAL_RNG) where {T <: AbstractVector}
101101
l = length(v1)
@@ -132,7 +132,7 @@ end
132132
"""
133133
BINX(Cr::Real=0.5)
134134
135-
Returns a uniform (binomial) crossover function, see [Recombination Interface](@ref), function with the propbabilty `Cr` [^2].
135+
Returns a uniform (binomial) crossover function, see [Recombination Interface](@ref), function with the probability `Cr` [^2].
136136
137137
The crossover probability value must be in unit interval, ``Cr \\in [0,1]``.
138138
"""
@@ -155,7 +155,7 @@ end
155155
"""
156156
EXPX(Cr::Real=0.5)
157157
158-
Returns an exponential crossover function, see [Recombination Interface](@ref), function with the propbabilty `Cr` [^2].
158+
Returns an exponential crossover function, see [Recombination Interface](@ref), function with the probability `Cr` [^2].
159159
160160
The crossover probability value must be in unit interval, ``Cr \\in [0,1]``.
161161
"""
@@ -183,7 +183,7 @@ end
183183
"""
184184
BSX(k::Int)
185185
186-
Binary Subset Crossover[^7]. Produces two offsprings by first pooling the unique
186+
Binary Subset Crossover[^7]. Produces an offspring by first pooling the unique
187187
items of the two parents, and then creating each offspring by sampling without
188188
replacement at most `k` elements from the pool of items.
189189
"""
@@ -201,6 +201,7 @@ function BSX(k::Int)
201201
return BSX
202202
end
203203

204+
204205
# Real valued crossovers
205206
# ----------------------
206207

@@ -533,7 +534,7 @@ end
533534
"""
534535
POS(v1, v2)
535536
536-
Position-based crossover is a modification of the [OX1](@ref) operator.
537+
Position-based crossover is a modification of the [`OX1`](@ref) operator.
537538
It selects a random set of positions in the parents `v1` and `v2`, then imposes
538539
the position of the selected elements of one parent on the corresponding elements
539540
of the other parent.
@@ -549,7 +550,7 @@ function POS(v1::T, v2::T; rng::AbstractRNG=Random.GLOBAL_RNG) where {T <: Abstr
549550
c1[i] = v2[i]
550551
c2[i] = v1[i]
551552
end
552-
end
553+
end
553554

554555
for i in 1:s
555556
if !in(v1[i],c1)
@@ -564,6 +565,24 @@ function POS(v1::T, v2::T; rng::AbstractRNG=Random.GLOBAL_RNG) where {T <: Abstr
564565
return c1,c2
565566
end
566567

568+
"""
569+
SSX(v1, v2)
570+
571+
Subset crossover operator creates new offspring by pooling the unique indices of
572+
the two parent vectors `v1` and `v2`, and then sampling a set of unique indices
573+
from this pool, uniformly at random.
574+
"""
575+
function SSX(v1::T, v2::T; rng::AbstractRNG=Random.GLOBAL_RNG) where {T <: AbstractVector}
576+
l = length(v1)
577+
pool = unique([v1;v2])
578+
p = length(pool)
579+
c1, c2 = if l <= p
580+
pool[randperm(p)[1:l]], pool[randperm(p)[1:l]]
581+
else
582+
rand(pool, l), rand(pool, l)
583+
end
584+
return c1, c2
585+
end
567586

568587

569588
# ===================

test/gp.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
optimizer = GA(
8181
selection = tournament(5),
8282
ɛ = 0.1,
83-
mutationRate = 0.8,
83+
mutationRate = 0.85,
8484
crossoverRate = 0.2,
8585
),
8686
)

test/mutations.jl

+2
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@
6262
@test scramble([0:5;], rng=rng) == [0,4,1,5,3,2]
6363
Random.seed!(rng, 2)
6464
@test shifting([0:5;], rng=rng) == [2,3,4,5,0,1]
65+
Random.seed!(rng, 2)
66+
@test replace([0:9;])([0:5;], rng=rng) == [7,1,2,3,4,9]
6567

6668
end
6769

test/recombinations.jl

+6
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@
5959
@test off1[end] == 0 && off2[end] == 0
6060
@test sum(off1) == 3 && sum(off2) == 3
6161
end
62+
63+
Random.seed!(rng, 1)
64+
off1, off2 = SSX([0:4;], [5:9;], rng=rng)
65+
@test all(i -> i in 0:9, off1)
66+
@test all(i -> i in 0:9, off2)
67+
6268
end
6369

6470
@testset "DE" begin

0 commit comments

Comments
 (0)