convert animal parameters to model parameters to make them adjustable in GLMakie

main
Michael Brehm 2024-06-20 15:04:24 +02:00
parent 78552bb044
commit fbf2453ccd
2 changed files with 65 additions and 27 deletions

View File

@ -15,6 +15,10 @@ mutable struct AnimalDefinition
dangers::Vector{String}
food::Vector{String}
end
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")]
energy_threshold(a) = abmproperties(model)[Symbol(a.def.type*"_"*"energy_threshold")]
struct StartDefinition
n::Int32
def::AnimalDefinition
@ -30,8 +34,8 @@ end
function perceive!(a::Animal,model)
if a.def.perception > 0
nearby = collect(nearby_agents(a, model, a.def.perception))
if perception(a) > 0
nearby = collect(nearby_agents(a, model, perception(a)))
a.nearby_dangers = map(x -> x.pos, filter(x -> isa(x, Animal) && x.def.type a.def.dangers && isnothing(x.death_cause), nearby))
a.nearby_food = map(x -> x.pos, filter(x -> isa(x, Animal) && x.def.type a.def.food && isnothing(x.death_cause), nearby))
if "Grass" a.def.food
@ -65,7 +69,7 @@ function calculate_best_pos(a::Animal,model)
push!(danger_scores,danger_score)
end
if !isempty(a.nearby_food)
food_score = sum(map(food -> findmax(abs.(pos.-danger))[1], a.nearby_food))
food_score = sum(map(food -> findmax(abs.(pos.-food))[1], a.nearby_food))
push!(food_scores,food_score)
end
end
@ -86,7 +90,7 @@ function eat!(a::Animal, model)
if !isnothing(prey)
#remove_agent!(dinner, model)
prey.death_cause = Predation
a.energy += prey.def.Δenergy
a.energy += Δenergy(prey)
end
if "Grass" a.def.food && model.fully_grown[a.pos...]
model.fully_grown[a.pos...] = false
@ -95,7 +99,7 @@ function eat!(a::Animal, model)
return
end
function reproduce!(a::Animal, model)
if a.energy > a.def.energy_threshold && rand(abmrng(model)) a.def.reproduction_prob
if a.energy > energy_threshold(a) && rand(abmrng(model)) reproduction_prop(a)#a.def.reproduction_prob
a.energy /= 2
replicate!(a, model)
end
@ -122,7 +126,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, a.def.perception)
nearby_pos = nearby_positions(a.pos, model, perception(a))
fully_grown_positions = filter(x -> model.fully_grown[x...], collect(nearby_pos))
return fully_grown_positions
end
@ -165,19 +169,19 @@ function initialize_model(;
## Notice how the properties are a `NamedTuple` to ensure type stability.
## define as dictionary(mutable) instead of tuples(immutable) as per https://github.com/JuliaDynamics/Agents.jl/issues/727
## maybe instead of AnimalDefinition we build the properties dict dynamically and use model properties during the simulation
animal_defs = Vector{AnimalDefinition}()
for start_def in start_defs
push!(animal_defs,start_def.def)
end
animal_properties = generate_animal_parameters(animal_defs)
properties = Dict(
:events => events,
:fully_grown => falses(dims),
:countdown => zeros(Int, dims),
:regrowth_time => regrowth_time,
:Δenergy_sheep => Δenergy_sheep,
:Δenergy_wolf => Δenergy_wolf,
:Δenergy_grass => Δenergy_grass,
:sheep_reproduce => sheep_reproduce,
:wolf_reproduce => wolf_reproduce,
:sheep_perception => sheep_perception,
:wolf_perception => wolf_perception
)
properties = merge(properties,animal_properties)
model = StandardABM(Animal, space;
agent_step! = animal_step!, model_step! = model_step!,
properties, rng, scheduler = Schedulers.Randomly(), warn = false, agents_first = false
@ -317,6 +321,28 @@ function event_handler!(model)
end
end
function generate_animal_parameters(defs::Vector{AnimalDefinition})
parameter_dict = Dict()
for def in defs
parameter_dict[Symbol(def.type*"_"*"Δenergy")]=def.Δenergy
parameter_dict[Symbol(def.type*"_"*"reproduction_prob")]=def.reproduction_prob
parameter_dict[Symbol(def.type*"_"*"perception")]=def.perception
parameter_dict[Symbol(def.type*"_"*"energy_threshold")]=def.energy_threshold
end
return parameter_dict
end
function generate_animal_parameter_ranges(defs::Vector{AnimalDefinition})
parameter_range_dict = Dict()
for def in defs
parameter_range_dict[Symbol(def.type*"_"*"Δenergy")]=0:1:100
parameter_range_dict[Symbol(def.type*"_"*"reproduction_prob")]=0:0.01:1
parameter_range_dict[Symbol(def.type*"_"*"perception")]=0:1:10
parameter_range_dict[Symbol(def.type*"_"*"energy_threshold")]=0:1:100
end
return parameter_range_dict
end
mutable struct RecurringEvent
name::String

View File

@ -9,7 +9,28 @@
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[32m\u001b[1m Activating\u001b[22m\u001b[39m project at `~/SCJ/Projekt/SCJ-PredatorPrey/env`\n"
"\u001b[32m\u001b[1m Activating\u001b[22m\u001b[39m project at `~/Studium/SCJ-PredatorPrey/env`\n",
"\u001b[32m\u001b[1mPrecompiling\u001b[22m\u001b[39m project...\n",
"\u001b[32m ✓ \u001b[39m\u001b[90mSpecialFunctions\u001b[39m\n",
"\u001b[32m ✓ \u001b[39m\u001b[90mColorVectorSpace → SpecialFunctionsExt\u001b[39m\n",
"\u001b[32m ✓ \u001b[39m\u001b[90mSpecialFunctions → SpecialFunctionsChainRulesCoreExt\u001b[39m\n",
"\u001b[32m ✓ \u001b[39m\u001b[90mDualNumbers\u001b[39m\n",
"\u001b[32m ✓ \u001b[39m\u001b[90mHypergeometricFunctions\u001b[39m\n",
"\u001b[32m ✓ \u001b[39m\u001b[90mStatsFuns\u001b[39m\n",
"\u001b[32m ✓ \u001b[39m\u001b[90mStatsFuns → StatsFunsInverseFunctionsExt\u001b[39m\n",
"\u001b[32m ✓ \u001b[39m\u001b[90mStatsFuns → StatsFunsChainRulesCoreExt\u001b[39m\n",
"\u001b[32m ✓ \u001b[39m\u001b[90mDistributions\u001b[39m\n",
"\u001b[32m ✓ \u001b[39m\u001b[90mDistributions → DistributionsTestExt\u001b[39m\n",
"\u001b[32m ✓ \u001b[39m\u001b[90mDistributions → DistributionsChainRulesCoreExt\u001b[39m\n",
"\u001b[32m ✓ \u001b[39m\u001b[90mKernelDensity\u001b[39m\n",
"\u001b[32m ✓ \u001b[39m\u001b[90mStreamSampling\u001b[39m\n",
"\u001b[32m ✓ \u001b[39mAgents\n",
"\u001b[32m ✓ \u001b[39m\u001b[90mMakie\u001b[39m\n",
"\u001b[32m ✓ \u001b[39m\u001b[90mAgents → AgentsVisualizations\u001b[39m\n",
"\u001b[32m ✓ \u001b[39mAgentsExampleZoo\n",
"\u001b[32m ✓ \u001b[39mCairoMakie\n",
"\u001b[32m ✓ \u001b[39mGLMakie\n",
" 19 dependencies successfully precompiled in 272 seconds. 270 already precompiled.\n"
]
}
],
@ -60,22 +81,12 @@
},
{
"cell_type": "code",
"execution_count": 10,
"execution_count": 23,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[32m\u001b[1mStatus\u001b[22m\u001b[39m `~/SCJ/Projekt/SCJ-PredatorPrey/env/Manifest.toml`\n",
" \u001b[90m[46ada45e] \u001b[39mAgents v6.0.13\n",
" \u001b[90m[e9467ef8] \u001b[39mGLMakie v0.10.2\n"
]
}
],
"outputs": [],
"source": [
"include(\"./predator_prey_generic.jl\")\n",
"Pkg.status([\"Agents\",\"GLMakie\"]; mode = Pkg.Types.PKGMODE_MANIFEST, io=stdout)\n",
"#Pkg.status([\"Agents\",\"GLMakie\"]; mode = Pkg.Types.PKGMODE_MANIFEST, io=stdout)\n",
"using GLMakie\n",
"GLMakie.activate!()\n",
"events = RecurringEvent[]\n",
@ -86,6 +97,7 @@
"sheep_def = AnimalDefinition('●',RGBAf(1.0, 1.0, 1.0, 0.8),20, 0.3, 20, 3, \"Sheep\", [\"Wolf\",\"Bear\"], [\"Grass\"])\n",
"wolf_def = AnimalDefinition('▲',RGBAf(0.2, 0.2, 0.3, 0.8),20, 0.07, 20, 1, \"Wolf\", [], [\"Sheep\"])\n",
"bear_def = AnimalDefinition('■',RGBAf(1.0, 0.8, 0.5, 0.8),20, 0.07, 20, 1, \"Bear\", [], [\"Sheep\"])\n",
"parameter_ranges = generate_animal_parameter_ranges([sheep_def,wolf_def,bear_def])\n",
"\n",
"stable_params = (;\n",
" events = events,\n",
@ -100,7 +112,7 @@
" :regrowth_time => 0:1:100,\n",
" :Δenergy_grass => 0:1:50,\n",
")\n",
"\n",
"params = merge(params,parameter_ranges)\n",
"sheep(a) = a.def.type == \"Sheep\"\n",
"wolf(a) = a.def.type == \"Wolf\"\n",
"eaten(a) = a.def.type == \"Sheep\" && a.death_cause == Predation\n",