Skip to content

Commit

Permalink
general refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
jarbus committed Feb 21, 2024
1 parent 67d3c42 commit 2ab77e9
Show file tree
Hide file tree
Showing 28 changed files with 263 additions and 148 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,11 @@

- Minimize architectural complexity, maximize code reuse
- All Counters use the highest level appropriate type (AbstractIndividual, AbstractGene, etc)

# Design of population operators

- population retriever gets all subpopulations for each pop id provided
- selector filters individuals
- reproducer creates individuals them
- mutator replaces the children with new individuals
- need a retriever to get all children and an update to update all children
21 changes: 4 additions & 17 deletions src/Jevo.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,13 @@ import Base: show

include("./abstracts.jl")
include("./utils.jl")
include("./datatypes/datatypes.jl")
include("./creators/creator.jl")
include("./individuals/individual.jl")
include("./populations/populations.jl")
include("./state.jl")
include("./organisms/organisms.jl")
include("./environments/environments.jl")
include("./operators/operators.jl")

include("./datatypes/counters.jl")

include("./genotypes/numbersgame.jl")
include("./genotypes/nn.jl")

include("./phenotypes/phenotype.jl")
include("./phenotypes/numbersgame.jl")

include("./datatypes/match.jl")
include("./environments/numbersgame.jl")

include("./operators/initializers.jl")
include("./operators/retrievers/retrievers.jl")
include("./operators/updaters/updaters.jl")
include("./operators/matchmaker/all_vs_all.jl")
include("./operators/operator.jl")
include("./operators/mutators/mutators.jl")
end
6 changes: 3 additions & 3 deletions src/abstracts.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# for use in Counters
import Base: show
export AbstractGene, AbstractIndividual, AbstractGeneration
export AbstractGene, AbstractIndividual, AbstractGeneration, AbstractEnvironment
abstract type AbstractJevo end
abstract type AbstractState <: AbstractJevo end
abstract type AbstractData <: AbstractJevo end
Expand All @@ -25,11 +25,11 @@ abstract type AbstractMutator <: AbstractOperator end
# populates the state.matches with matches
abstract type AbstractMatchMaker <: AbstractOperator end
abstract type AbstractScorer <: AbstractOperator end
abstract type AbstractPerformer <: AbstractOperator end
# Evaluator should enable distributed computing
abstract type AbstractEvaluator <: AbstractOperator end
abstract type AbstractCrossover <: AbstractOperator end
abstract type AbstractReproducer <: AbstractOperator end
abstract type AbstractSelector <: AbstractOperator end
abstract type AbstractReplacer <: AbstractOperator end
abstract type AbstractCheckpointer <: AbstractOperator end
abstract type AbstractReporter <: AbstractOperator end
abstract type AbstractAssertor <: AbstractOperator end # Can apply assertions to objects in state
Expand Down
4 changes: 4 additions & 0 deletions src/datatypes/datatypes.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
include("./counters.jl")
include("./match.jl")
include("./interaction.jl")
include("./record.jl")
6 changes: 6 additions & 0 deletions src/datatypes/interaction.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
struct Interaction <: AbstractInteraction
match_id::Int
individual_id::Int
other_ids::Vector{Int}
score::Float32
end
5 changes: 2 additions & 3 deletions src/datatypes/match.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
struct Match <: AbstractMatch
ids::Vector{Int}
id::Int
individuals::Vector{<:AbstractIndividual}
environment_creator::AbstractCreator
genotypes::Vector{<:AbstractGenotype}
developers::Vector{<:AbstractCreator}
end
5 changes: 5 additions & 0 deletions src/datatypes/record.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Generic record, typically used for fitness-proportional selection
struct Record <: AbstractRecord
id::Int
fitness::Float64
end
11 changes: 7 additions & 4 deletions src/environments/environment.jl
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
export step!, done, play
# Creation should be done as an environment constructor
function step!(::AbstractEnvironment, args...; kwargs...)
function step!(env::AbstractEnvironment, args...; kwargs...)
@error "step! not implemented for $(typeof(env))"
end
function done(::AbstractEnvironment)::Bool
true
end
function play(environment_creator::AbstractCreator, genotypes_and_creators::Vector{Tuple{<:AbstractGenotype, <:AbstractCreator}})
play(environment_creator(), [develop(c, g) for (g, c) in genotypes_and_creators])
play(match::Match) = play(match.environment_creator, match.individuals)
function play(environment_creator::AbstractCreator, individuals::Vector{<:AbstractIndividual})
play(environment_creator(), develop.(individuals))

end

function play(env::AbstractEnvironment, phenotypes::Vector{<:AbstractPhenotype})
is_done = false
scores = zeros(length(phenotypes))
while !is_done
scores += step!(env)
scores += step!(env, phenotypes)
is_done = done(env)
end
scores
Expand Down
2 changes: 2 additions & 0 deletions src/environments/environments.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
include("./environment.jl")
include("./numbersgame.jl")
14 changes: 7 additions & 7 deletions src/environments/numbersgame.jl
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
export CompareOnOne, AbstractNumbersGame
abstract type AbstractNumbersGame end
abstract type AbstractNumbersGame <: AbstractEnvironment end
struct CompareOnOne <: AbstractNumbersGame end

function step!(environment::CompareOnOne, a::VectorPhenotype, b::VectorPhenotype)

max_a_dim = argmax(a)
max_b_dim = argmax(b)
a_score = a[max_b_dim] >= b[max_b_dim] ? 1.0 : 0.0
b_score = b[max_a_dim] >= a[max_a_dim] ? 1.0 : 0.0
step!(::CompareOnOne, phenotypes::Vector{VectorPhenotype}) = step!(CompareOnOne(), phenotypes...)
function step!(::CompareOnOne, a::VectorPhenotype, b::VectorPhenotype)
max_a_dim = argmax(a.numbers)
max_b_dim = argmax(b.numbers)
a_score = a.numbers[max_b_dim] >= b.numbers[max_b_dim] ? 1.0 : 0.0
b_score = b.numbers[max_a_dim] >= a.numbers[max_a_dim] ? 1.0 : 0.0
return [a_score, b_score]
end

Expand Down
19 changes: 12 additions & 7 deletions src/individuals/individual.jl
Original file line number Diff line number Diff line change
@@ -1,38 +1,43 @@
export Individual
export Individual, develop

mutable struct Individual <: AbstractIndividual
id::Int
generation::Int
parents::Vector{Int}
genotype::AbstractGenotype
developer::AbstractCreator
records::Vector{AbstractRecord}
interactions::Dict{Any, <:AbstractInteraction}
interactions::Vector{<:AbstractInteraction}
data::Vector{AbstractData}
end

function Individual(
id::Int,
generation::Int,
parents::Vector{Int},
genotype::AbstractGenotype;
genotype::AbstractGenotype,
developer::AbstractCreator;
records::Vector{AbstractRecord} = AbstractRecord[],
interactions::Dict{Any, <:AbstractInteraction} = Dict{Any, AbstractInteraction}(),
interactions::Vector{<:AbstractInteraction} = AbstractInteraction[],
data::Vector{AbstractData} = AbstractData[]
)
Individual(id, generation, parents, genotype, records, interactions, data)
Individual(id, generation, parents, genotype, developer, records, interactions, data)
end
new_id_and_gen(state::AbstractState) = new_id_and_gen(state.counters)
function new_id_and_gen(counters::Vector{<:AbstractCounter})
id = find(:type, AbstractIndividual, counters) |> inc!
gen = find(:type, AbstractGeneration, counters) |> value
id, gen
end
develop(ind::AbstractIndividual) = develop(ind.developer, ind.genotype)

"Create new individual with no parents"
function Individual(counters::Vector{<:AbstractCounter},
genotype_creator::Creator)
genotype_creator::Creator,
developer::AbstractCreator
)
id, generation = new_id_and_gen(counters)
Individual(id, generation, Int[], genotype_creator())
Individual(id, generation, Int[], genotype_creator(), developer)
end

function Base.show(io::IO, ind::Individual)
Expand Down
35 changes: 25 additions & 10 deletions src/operators/matchmaker/all_vs_all.jl
Original file line number Diff line number Diff line change
@@ -1,17 +1,32 @@
struct All_vs_All <: AbstractMatchMaker
export AllVsAllMatchMaker, make_matches!
struct AllVsAllMatchMaker <: AbstractMatchMaker
condition::Function
retriever::AbstractRetriever
operator::Function
updater::AbstractUpdater
rng::Union{AbstractRNG, Nothing}
updater::Union{AbstractUpdater, Function}
data::Vector{AbstractData}
end

function All_vs_All()
condition = (::AbstractState) -> true
retriever = (::AbstractState) -> state.populations
operator = (pop::AbstractPopulation) -> pop
updater = add_matches!
rng = StableRNG(1234)
All_vs_All(condition, retriever, operator, updater, rng, AbstractData[])
function AllVsAllMatchMaker(ids::Vector{String}=String[])
condition = always
retriever = PopulationRetriever(ids)
operator = noop
updater = make_matches!
AllVsAllMatchMaker(condition, retriever, operator, updater, AbstractData[])
end


function make_matches!(state::AbstractState, pops::Vector{Vector{Population}})
match_counter = get_counter(AbstractMatch, state)
env_creators = get_creators(AbstractEnvironment, state)
@assert length(env_creators) == 1 "There should be exactly one environment creator for the time being, found $(length(env_creators))."
env_creator = env_creators[1]
for i in 1:length(pops), j in i+1:length(pops) # for each pair of populations
for subpopi in pops[i], subpopj in pops[j] # for each pair of subpopulations
for indi in subpopi.individuals, indj in subpopj.individuals # for each pair of individuals
push!(state.matches, Match(inc!(match_counter), [indi, indj], env_creator))
end
end
end
@assert length(state.matches) > 0
end
1 change: 1 addition & 0 deletions src/operators/matchmaker/matchmaker.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include("./all_vs_all.jl")
7 changes: 3 additions & 4 deletions src/operators/mutators/mutators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ end

function Mutator(pop_ids::Vector{String}=String[])
condition = always
retriever = PopulationRetriever(pop_ids) # returns vec{vec{ind}}
retriever = PopulationRetriever(pop_ids) # returns vec{vec{pop}}
operator = map((s,x)->mutate(s, x))
updater = PopulationUpdater(pop_ids)
Mutator(condition, retriever, operator, updater)
Expand All @@ -22,8 +22,7 @@ mutate(state::AbstractState, pop::AbstractPopulation) =
function mutate(state::AbstractState, ind::AbstractIndividual)
new_id, gen = new_id_and_gen(state)
new_geno = mutate(state, ind.genotype)
Individual(new_id, gen, [ind.id], new_geno)
Individual(new_id, gen, [ind.id], new_geno, ind.developer)
end
function mutate(::AbstractState, genotype::AbstractGenotype)
mutate(::AbstractState, genotype::AbstractGenotype) =
error("mutate function not implemented for $(typeof(genotype))")
end
2 changes: 0 additions & 2 deletions src/operators/operator.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,3 @@ function operate!(state::AbstractState, operator::AbstractOperator)
objects = operator.operator(state, objects)
operator.updater(state, objects)
end

noop(x...) = x
7 changes: 7 additions & 0 deletions src/operators/operators.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
include("./retrievers/retrievers.jl")
include("./updaters/updaters.jl")
include("./operator.jl")
include("./initializers.jl")
include("./matchmaker/matchmaker.jl")
include("./performer.jl")
include("./mutators/mutators.jl")
9 changes: 9 additions & 0 deletions src/operators/performer.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export Performer

Base.@kwdef struct Performer <: AbstractPerformer
condition::Function = always
retriever::Union{AbstractRetriever,Function} = (state::AbstractState) -> state.matches
operator::Function = noop
updater::AbstractUpdater = ComputeInteractions()
data::Vector{<:AbstractData} = AbstractData[]
end
19 changes: 14 additions & 5 deletions src/operators/retrievers/retrievers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,27 @@ Base.@kwdef struct PopulationRetriever <: AbstractRetriever
end

function get_populations(id::String, population::Population; flatten::Bool=false)
(flatten || population.id == id) ? [population] : nothing
(flatten || population.id == id) ? [population] : Population[]
end

function get_populations(id::String, populations::Vector{<:AbstractPopulation}; flatten::Bool=false)
[get_populations(id, p, flatten=flatten) for p in populations] |> filter(!isnothing) |> Iterators.flatten |> collect
[get_populations(id, p, flatten=flatten) for p in populations] |>
Iterators.flatten |> collect
end
function get_populations(id::String, composite::CompositePopulation; flatten::Bool=false)
flatten && composite.id == id && error("Cannot flatten composite population $(composite.id)")
[get_populations(id, p, flatten=flatten || composite.id == id)
for p in composite.populations] |> filter(!isnothing) |> Iterators.flatten |> collect
pops = [get_populations(id, p, flatten=flatten || composite.id == id)
for p in composite.populations]
pops = pops |> Iterators.flatten |> collect
pops
end

"""Traverses the population tree and returns references to all vectors of individuals to an arbitrary depth. If ids is not empty, it only returns individuals from the specified populations"""
function (r::PopulationRetriever)(state::AbstractState)
if !isempty(r.ids)
pops = [get_populations(id, state.populations) for id in r.ids]
@assert !isempty(pops) "Failed to retrieve any populations with ids $(r.ids)"
@assert all(!isnothing, pops) "Failed to retrieve a population with id $(r.ids)"
@assert all(!isempty, pops) "Failed to retrieve a population with id $(r.ids)"
else
pops = get_populations("", state.populations, flatten=true)
@assert !isempty(pops) "Failed to retrieve any populations"
Expand All @@ -53,3 +56,9 @@ end
struct PopulationCreatorRetriever <: AbstractRetriever end
# (::PopulationCreatorRetriever)(state::AbstractState) = filter(c -> c.type isa AbstractPopulation, state.creators) |> collect
(::PopulationCreatorRetriever)(state::AbstractState) = filter(c -> c.type <: AbstractPopulation, state.creators) |> collect

get_individuals(pop::Population) = pop.individuals
get_individuals(pop::CompositePopulation) =
get_individuals.(pop.populations) |> Iterators.flatten |> collect
get_individuals(pops::Vector{<:AbstractPopulation}) =
get_individuals.(pops) |> Iterators.flatten |> collect
13 changes: 13 additions & 0 deletions src/operators/updaters/updaters.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,16 @@ function (updater::PopulationUpdater)(state::AbstractState, inds::Vector{<:Vecto
end
end
end

Base.@kwdef struct ComputeInteractions <: AbstractUpdater end
function (updater::ComputeInteractions)(::AbstractState, matches::Vector{<:AbstractMatch})
@assert !isempty(matches) "No matches to compute interactions for"
for m in matches
scores = play(m)
for (score, ind) in zip(scores, m.individuals)
interaction = Interaction(m.id, ind.id,
[i.id for i in m.individuals if i !== ind], score)
push!(ind.interactions, interaction)
end
end
end
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
export VectorGenotype
export VectorGenotype, VectorPhenotype, develop, mutate
mutable struct VectorGenotype <: AbstractGenotype
numbers::Vector{Float32}
end

mutable struct VectorPhenotype <: AbstractPhenotype
numbers::Vector{Float32}
end

VectorGenotype(n::Int, rng::AbstractRNG; init::Function=rand) = VectorGenotype(init(rng, Float32, n))

function mutate(state::State, genotype::VectorGenotype)
Expand All @@ -16,3 +20,5 @@ function mutate(state::State, genotype::VectorGenotype)
genotype.numbers[j] += randn(state.rng)
genotype
end

develop(c::Creator, genotype::VectorGenotype) = c.type(genotype.numbers)
2 changes: 2 additions & 0 deletions src/organisms/organisms.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
include("./numbersgame.jl")
include("./nn.jl")
6 changes: 0 additions & 6 deletions src/phenotypes/numbersgame.jl

This file was deleted.

1 change: 0 additions & 1 deletion src/phenotypes/phenotype.jl

This file was deleted.

8 changes: 4 additions & 4 deletions src/populations/populations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ CompositePopulation(id::String, populations::Vector{<:AbstractPopulation}) =
CompositePopulation(id, populations, Vector{AbstractData}())
# Create composite population using genotype creator
CompositePopulation(id::String,
pops::Vector{Tuple{String, Int, Creator}},
pops::Vector{<:Tuple{String, Int, <:AbstractCreator, <:AbstractCreator}},
counters::Vector{<:AbstractCounter}) =
CompositePopulation(id, [Population(id, n, gc, counters) for (id, n, gc) in pops])
CompositePopulation(id, [Population(id, n, gc, dev, counters) for (id, n, gc, dev) in pops])

mutable struct Population <: AbstractPopulation
id::String
Expand All @@ -24,8 +24,8 @@ Population(id::String, individuals::Vector{<:AbstractIndividual}) =
Population(id, individuals, AbstractData[])

# Create n inds using genotype creator, updates counters
Population(id::String, n::Int, genotype_creator::Creator, counters::Vector{<:AbstractCounter}) =
Population(id, [Individual(counters, genotype_creator) for i in 1:n])
Population(id::String, n::Int, genotype_creator::AbstractCreator, developer::AbstractCreator, counters::Vector{<:AbstractCounter}) =
Population(id, [Individual(counters, genotype_creator, developer) for i in 1:n])

find_population(id::String, population::Population) =
population.id == id ? population : nothing
Expand Down
Loading

0 comments on commit 2ab77e9

Please sign in to comment.