diff --git a/optimize.jl b/optimize.jl new file mode 100644 index 0000000..af71c96 --- /dev/null +++ b/optimize.jl @@ -0,0 +1,107 @@ +import Pkg +Pkg.activate("./env") +using Distributed +@everywhere include("./predator_prey_generic.jl") +using BlackBoxOptim, Random +using Statistics: mean, median +using Serialization +function load(file) + fh = open(file, "r") + optctrlb, res = deserialize(fh); + close(fh) + return (optctrlb, res) +end +function generator(x,n) + models = [] + rng = MersenneTwister(71758) + for i in 1:n + animal_defs = [ + #AnimalDefinition(trunc(Int,x[8]),'●',RGBAf(1.0, 1.0, 1.0, 1),x[3], x[3], 1, x[1], x[3], trunc(Int,x[6]), "Sheep", ["Wolf","Bear"], ["Grass"]) + #AnimalDefinition(trunc(Int,x[9]),'▲',RGBAf(0.2, 0.2, 0.3, 1),x[4], x[4], 1, x[2], x[4], trunc(Int,x[7]), "Wolf", [], ["Sheep"]) + AnimalDefinition(trunc(Int,x[6]),'●',RGBAf(1.0, 1.0, 1.0, 1),0, 0, 1, x[1], x[3], 0, "Sheep", ["Wolf","Bear"], ["Grass"]) + AnimalDefinition(trunc(Int,x[7]),'▲',RGBAf(0.2, 0.2, 0.3, 1),0, 0, 1, x[2], x[4], 0, "Wolf", [], ["Sheep"]) + ] + stable_params = (; + events = [], + animal_defs = animal_defs, + dims = (30, 30), + regrowth_time = 30, + Δenergy_grass = x[5], + #seed = 71758, + ) + seed = rand(rng,1000:100000) + push!(models,initialize_model(;seed,stable_params...)) + end + return models +end +function cost(x) + steps = 2000 + iterations = 10 + models = generator(x,iterations) + sheep(a) = a.def.type == "Sheep" + wolf(a) = a.def.type == "Wolf" + eaten(a) = a.def.type == "Sheep" && a.death_cause == Predation + starved(a) = a.def.type == "Sheep" && a.death_cause == Starvation + count_grass(model) = count(model.fully_grown) + adata = [(sheep, count), (wolf, count), (eaten, count), (starved, count)] + mdata = [count_grass] + df1,df2 = ensemblerun!(models, steps; adata, mdata, parallel=true, showprogress=true) + println(x) + fitness_scores = [] + for i in 1:iterations + df = df1[df1.ensemble .== i,:] + println(string(count(!iszero,df.count_sheep))*" "*string(count(!iszero,df.count_wolf))) + score = count(iszero,df.count_sheep) + 2*count(iszero,df.count_wolf) + push!(fitness_scores,score) + end + fitness = float(sum(fitness_scores)) + println(fitness) + return fitness +end + +#result = bboptimize(cost,SearchRange = [(0.0, 1.0),(0.0, 1.0),(0.0, 30.0),(0.0, 30.0),],NumDimensions = 4,MaxTime = 20,) +SearchRange = [ + (0.01, 0.4), + (0.01, 0.4), + (5.0, 30.0), + (5.0, 30.0), + (5.0, 30.0), + #(1, 3), + #(1, 3), + (3, 30), + (3, 30), + ] +optctrl = bbsetup(cost;SearchRange, MaxTime = 300, Method = :generating_set_search)#, TraceInterval=1.0, TraceMode=:verbose); +#optctrl, res = load("SimpleModellOptimization900.tmp"); +res = bboptimize(optctrl) +tempfilename = "./temp" * string(rand(1:Int(1e8))) * ".tmp" +fh = open(tempfilename, "w") +serialize(fh, (optctrl, res)) +close(fh) + +#cost([0.806586, 0.0481975, 18.5285, 22.329]) +#[0.165438, 0.0462449, 15.4501, 12.0382] +#[0.571934, 0.74005, 4.22395, 24.9997, 15.4605, 1.09129, 2.18749, 24.3948, 11.3926] + +#Repro_Schaf, Repro_Wolf, Delta_Energie_Schaf, Delta_Energy_Wolf, Delta_Energy_Gras, n_Schaf, n_Wölfe +#[0.26817737483789245, 0.027182763696826588, 14.440470034137558, 27.81279288508929, 15.785601397364756, 28.644469239080397, 13.471462703569484] +#[0.11524114234251756, 0.07378121226251827, 29.31006871020899, 20.47494251025892, 5.915473514486612, 9.568612576389182, 22.299369669891565] + +# Schaf stirbt aus obwohl es mehr energy bekommt??? +#[0.11524114234251756, 0.07378121226251827, 29.31006871020899, 20.47494251025892, 12.155473514486612, 9.568612576389182, 22.299369669891565] + +#Wolf stirbt aus weil Schaf sich zu wenig reproduziert +#[0.016950722103029812, 0.07378121226251827, 29.31006871020899, 20.47494251025892, 5.915473514486612, 9.568612576389182, 22.299369669891565] + + +#Wolf stirbt aus, da er sich viel zu stark reproduziert +#[0.11524114234251756, 0.25901432860274654, 29.31006871020899, 20.47494251025892, 5.915473514486612, 9.568612576389182, 22.299369669891565] + +#Aber hier plötzlich wieder einigermaßen stabil +#[0.11524114234251756, 0.2477989358947258, 29.31006871020899, 20.47494251025892, 5.915473514486612, 9.568612576389182, 22.299369669891565] + +#Wolf stirbt weil zu wenig Schafe am Anfang +#[0.11524114234251756, 0.07378121226251827, 29.31006871020899, 20.47494251025892, 5.915473514486612, 3.3286125763891814, 22.299369669891565] + +#Einigermaßen stabil, aber Schaf stirbt oft aus, weil zu hohe reproduktion +#[0.39570778081524405, 0.07378121226251827, 29.31006871020899, 20.47494251025892, 5.915473514486612, 9.568612576389182, 22.299369669891565] \ No newline at end of file diff --git a/predator_prey_generic.jl b/predator_prey_generic.jl index 8131c73..c496794 100644 --- a/predator_prey_generic.jl +++ b/predator_prey_generic.jl @@ -23,17 +23,19 @@ mutable struct AnimalDefinition end # some helper functions to get generated model parameters for animals -reproduction_prop(a) = abmproperties(model)[Symbol(a.def.type*"_"*"reproduction_prob")] -Δenergy(a) = abmproperties(model)[Symbol(a.def.type*"_"*"Δenergy")] -perception(a) = abmproperties(model)[Symbol(a.def.type*"_"*"perception")] -reproduction_energy_threshold(a) = abmproperties(model)[Symbol(a.def.type*"_"*"reproduction_energy_threshold")] -forage_energy_threshold(a) = abmproperties(model)[Symbol(a.def.type*"_"*"forage_energy_threshold")] -energy_usage(a) = abmproperties(model)[Symbol(a.def.type*"_"*"energy_usage")] +reproduction_prop(a, model) = abmproperties(model)[Symbol(a.def.type*"_"*"reproduction_prob")] +Δenergy(a, model) = abmproperties(model)[Symbol(a.def.type*"_"*"Δenergy")] +perception(a, model) = abmproperties(model)[Symbol(a.def.type*"_"*"perception")] +reproduction_energy_threshold(a, model) = abmproperties(model)[Symbol(a.def.type*"_"*"reproduction_energy_threshold")] +forage_energy_threshold(a, model) = abmproperties(model)[Symbol(a.def.type*"_"*"forage_energy_threshold")] +energy_usage(a, model) = abmproperties(model)[Symbol(a.def.type*"_"*"energy_usage")] # Animal with AnimalDefinition and fields that change during simulation # might be better to use @multiagent and @subagent with predator prey as subtypes. Allows to dispatch different functions per kind and change execution order with schedulers.bykind @agent struct Animal(GridAgent{2}) energy::Float64 + color::GLMakie.ColorTypes.RGBA{Float32} + symbol::Char def::AnimalDefinition death_cause::Union{DeathCause,Nothing} nearby_dangers @@ -43,8 +45,8 @@ end # get nearby food and danger for later when choosing the next position function perceive!(a::Animal,model) - if perception(a) > 0 - nearby = collect(nearby_agents(a, model, perception(a))) + if perception(a, model) > 0 + nearby = collect(nearby_agents(a, model, perception(a, model))) a.nearby_dangers = map(x -> x.pos, filter(x -> isa(x, Animal) && x.def.type ∈ a.def.dangers, nearby)) a.nearby_food = map(x -> x.pos, filter(x -> isa(x, Animal) && x.def.type ∈ a.def.food, nearby)) if "Grass" ∈ a.def.food @@ -67,7 +69,7 @@ function move!(a::Animal,model) else randomwalk!(a, model) end - a.energy -= energy_usage(a) + a.energy -= energy_usage(a, model) end # choose best position based on scoring @@ -86,7 +88,7 @@ function choose_position(a::Animal,model) end end for food in a.nearby_food - if a.energy < forage_energy_threshold(a) + if a.energy < forage_energy_threshold(a, model) distance = findmax(abs.(pos.-food))[1] if distance != 0 score += 1/distance @@ -107,7 +109,8 @@ function eat!(a::Animal, model) if !isnothing(prey) #remove_agent!(dinner, model) prey.death_cause = Predation - a.energy += Δenergy(prey) + prey.symbol = 'x' + a.energy += Δenergy(prey, model) end if "Grass" ∈ a.def.food && model.fully_grown[a.pos...] model.fully_grown[a.pos...] = false @@ -119,7 +122,7 @@ end # dublicate the animal, based on chance and if it has enough energy function reproduce!(a::Animal, model) - if a.energy > reproduction_energy_threshold(a) && rand(abmrng(model)) ≤ reproduction_prop(a) + if a.energy > reproduction_energy_threshold(a, model) && rand(abmrng(model)) ≤ reproduction_prop(a, model) a.energy /= 2 replicate!(a, model) end @@ -156,7 +159,7 @@ function move_towards!(agent, pos, model; ifempty=true) walk!(agent,direction,model; ifempty=ifempty) end function nearby_fully_grown(a::Animal, model) - nearby_pos = nearby_positions(a.pos, model, perception(a)) + nearby_pos = nearby_positions(a.pos, model, perception(a, model)) fully_grown_positions = filter(x -> model.fully_grown[x...], collect(nearby_pos)) return fully_grown_positions end @@ -207,7 +210,7 @@ function initialize_model(; for def in animal_defs for _ in 1:def.n energy = rand(abmrng(model), 1:(def.Δenergy*2)) - 1 - add_agent!(Animal, model, energy, def, nothing, [], [], []) + add_agent!(Animal, model, energy, def.color, def.symbol, def, nothing, [], [], []) end end ## Add grass with random initial growth @@ -231,10 +234,16 @@ function animal_step!(a::Animal, model) move!(a, model) if a.energy < 0 a.death_cause = Starvation + a.symbol = 'x' return end eat!(a, model) reproduce!(a, model) + #if a.energy < 10 && a.def.type != "Wolf" + # a.color = RGBAf(a.energy/10,a.energy/10,a.energy/10,1) + #elseif a.def.type != "Wolf" + # a.color = RGBAf(1,1,1,1) + #end end function model_step!(model)