Created
November 6, 2022 01:13
-
-
Save Elmuti/8184c7eea69ac5d34afe3c70a95ec44d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local PreBakedDeathFunction = {} | |
local random = math.random | |
local ceil = math.ceil | |
local floor = math.floor | |
local randomseed = math.randomseed | |
local prevActual = 0 | |
local prevWeeks = 0 | |
randomseed(1557007091) --os.time() | |
random(); random(); random() | |
--16/4/19 | |
--r-strategy simulation | |
--23/4/19 | |
--r-strategy simulation expanded with population strata | |
--24/4/19 | |
--first run for 1000 years (52000 weeks), results: 54.6 s with 1465 agents (i5 2500k) | |
--basedeathfactor was 0.0018 and basefertilityfactor was 0.008 | |
--r-strategy simulation expanded to multiple repeated simulations with efficient logging | |
--28/4/19 | |
--multiple simulation implemented (with inferior rolling average method) (will be replaced with superior full-averaging method) | |
--todo notes | |
--implement average agent pregnancies tracking (28/4/19) | |
--implement age strata size tracking (28/4/19) | |
--29/4/19 | |
--multiple simulation (superior full average method) implemented | |
--mortality calculators | |
--(0.6(x+2)^2)(0.01(x-2)^2)(0.9(x-2)^2)+0.01 | |
--big local list | |
local ResultsFileName = "C:/Users/Elmeri/Desktop/GregorSimData/GregorData" --output file name (in general) | |
local pop_num = 1000 --number of agents in the start | |
local desired_length = 1000 --length in years to simulate | |
local desired_simulations = 5 --number of simulations | |
local verboseoutput = true --should the output include text (true) or just be numbers (false) | |
local runSingle = false --set to true if a single simulation is desired | |
local prerepro = 0.35 --percent of population in the pre-reproductive stage | |
local repro = 0.6 --percent of population in the reproductive stage of life | |
local postrepro = 0.05 --percent of population in the post-reproductive stage | |
--small local list | |
--internal locals | |
local pop = {} --table to contain the agents | |
local week = 0 --counts weeks, for ingest into Excel | |
local basedeathfactor = 0.0011 | |
local basefertilityfactor = 0.007 | |
local sim_length = (desired_length*52) --simulation length in weeks | |
--local pop_actual = pop_num | |
local max_pop_size = 10000 --maximum population size to terminate the simulation | |
local simSettingsFile = (os.time().."RStratSimSettings.txt") --simulation settings log file | |
local sim_interval = 10 --prints a progress message every n simulations to make sure the program is working as intended | |
local sim_number = 0 --keeps track of the current number of simulations | |
local sim_time_length = 0.83 --length in seconds of a single simulation per 1000 weeks in that simulation, to calculate estimated times | |
local simSingleOutput = "C:/Users/Elmeri/Desktop/GregorSimData/GregorData" | |
local preResults = {} | |
--sim length estimator | |
print("Expected run time (in seconds) "..( ((sim_length/1000) * sim_time_length) * desired_simulations)) | |
os.execute("title GregorPopSim - Running "..desired_simulations.." simulations, each "..sim_length.." weeks") | |
--various verbose text | |
if desired_simulations ~= 0 then | |
print("Warning, multiple simulations ("..desired_simulations..") will be run!") | |
end | |
if simSingleOutput == nil then | |
print("Critical! Special simulation output folder not available!") | |
end | |
print("The program will now present various mission critical variables, and write them into a log file") | |
print("Strata: "..prerepro.." "..repro.." "..postrepro) | |
print("Factors: "..basedeathfactor.." "..basefertilityfactor) | |
print("Simulations: "..sim_length.." "..desired_simulations) | |
local F99 = io.open(simSettingsFile,"w") | |
F99:write("This is the settings log file for the simulation on "..os.date().."\n") | |
F99:write("Strata: "..prerepro.." "..repro.." "..postrepro.."\n") | |
F99:write("Factors: "..basedeathfactor.." "..basefertilityfactor.."\n") | |
F99:write("Simulations: "..sim_length.." "..desired_simulations.."\n") | |
F99:write("Agent characteristics") | |
print("script head complete "..os.clock().."\n") | |
local function PreBakeDeathCharts() | |
local before = os.clock() | |
for i = 1, 5200 do --assume 100 years is maximum age for pre-baked death function charts | |
local age = i/1000 | |
PreBakedDeathFunction[i] = 0.1 * ( (0.6 * (age+0.1)^2) * (0.01 * (age-2.5)^2) * (0.9 * (age-2)^2) + basedeathfactor) | |
end | |
local timetaken = os.clock() - before | |
print("Took "..timetaken.." seconds to pre-bake death function chart") | |
end | |
PreBakeDeathCharts() | |
print("Press ENTER to start simulation") | |
--io.read() | |
--agent types | |
--[[table positions agentID, | |
age, | |
death boolean for clean up, | |
age at reproductive maturity, | |
post-reproductive maturity, | |
pregnant boolean, | |
%chance of pregnancy, | |
week of pregnancy, | |
gestation period, | |
post-gestational regeneration (multiplier of gestation time), | |
post-gestational stage boolean, | |
numpregnancies, | |
is reproductive age (adult) boolean, | |
internal boolean to smooth out pregnancies at the start, | |
]] | |
local basicAgent = { | |
ID = IDcount; | |
Age = 0; | |
Dead = false; | |
Repro = 780; | |
PRepro = 2080; | |
Pregnant = false; | |
FertilityRoll = basefertilityfactor; | |
WeekP = 0; | |
Gestation = 38; | |
PGestation = 0.5; | |
PGestational = false; | |
PGestationalWeek = 0; | |
Pregnancies = 0; | |
Adult = false; | |
Burnin = false; | |
} | |
F99:write("Repro: "..basicAgent.Repro.."\n") | |
F99:write("PRepro: "..basicAgent.PRepro.."\n") | |
F99:write("Gestation "..basicAgent.Gestation.."\n") | |
F99:write("PGestation "..basicAgent.PGestation.."\n") | |
F99:close() | |
--samity checks | |
if prerepro+repro+postrepro ~= 1 then | |
print("The simulation cannot be started as the population strata do not sum to 1!") | |
print(prerepro) | |
print(repro) | |
print(postrepro) | |
print(prerepro+repro+postrepro) | |
error("Script has been halted!") | |
end | |
--script start | |
--local F = io.open(ResultsFile,"w") | |
--F:write("week \t deaths \t population \t births \t pregnancies \t burnins \t avg age") | |
local verboseLine = "week \t deaths \t population \t births \t pregnancies \t avg age" | |
--local F2 = io.open((os.time().."PopStart.txt"),"w") | |
--F2:write("agent \t age") | |
--establish the table | |
local function deepcopy(orig) | |
local orig_type = type(orig) | |
local copy | |
if orig_type == 'table' then | |
copy = {} | |
for orig_key, orig_value in next, orig, nil do | |
copy[deepcopy(orig_key)] = deepcopy(orig_value) | |
end | |
setmetatable(copy, deepcopy(getmetatable(orig))) | |
else -- number, string, boolean, etc | |
copy = orig | |
end | |
return copy | |
end | |
local function createAgents(numAgents,typeAgent,creationType) | |
if creationType == "SimStart" then | |
local IDcount = 0 | |
local prereprocount = ceil(pop_num * prerepro) | |
local reprocount = ceil(pop_num * repro) | |
local postreprocount = ceil(pop_num * postrepro) | |
os.execute("cls") | |
print("Currently running simulation #"..sim_number.." with agent configuration "..prereprocount.."/"..reprocount.."/"..postreprocount.." for a total of "..(prereprocount+reprocount+postreprocount).." agents") | |
print("Previous simulation: "..prevActual.." agents and "..prevWeeks.." weeks") | |
print("Total time running so far: ".. os.clock()) | |
reprocount = reprocount + prereprocount | |
postreprocount = postreprocount + reprocount | |
for i = 1, numAgents do | |
if ((prereprocount > i) and (i >= 0)) then | |
local copy = deepcopy(typeAgent) | |
copy.Age = random(0,basicAgent.Repro) | |
IDcount = IDcount + 1 | |
table.insert(pop,copy) | |
--F2:write("\n"..IDcount.."\t"..copy.Age) | |
elseif ((reprocount > i) and (i >= prereprocount)) then | |
local copy = deepcopy(typeAgent) | |
copy.Age = random(basicAgent.Repro,basicAgent.PRepro) | |
local numReproAgents = ceil(pop_num * repro) | |
local burninRoll = random() | |
if burninRoll < 0.9 then | |
copy.Burnin = true | |
end | |
IDcount = IDcount + 1 | |
table.insert(pop,copy) | |
--F2:write("\n"..IDcount.."\t"..copy.Age) | |
elseif ((postreprocount > i) and (i >= reprocount)) then | |
local copy = deepcopy(typeAgent) | |
copy.Age = random(basicAgent.PRepro,3000) | |
IDcount = IDcount + 1 | |
table.insert(pop,copy) | |
--F2:write("\n"..IDcount.."\t"..copy.Age) | |
end | |
end | |
elseif creationType == "Birth" then | |
for i = 1, numAgents do | |
table.insert(pop,deepcopy(typeAgent)) | |
end | |
end | |
--F2:close() | |
end | |
--simulation | |
local function simulate(dsrlngth,startPop) | |
local pop_actual = startPop | |
--print(dsrlngth) | |
for i = 1, dsrlngth do | |
if pop_actual > max_pop_size or pop_actual <= 10 then | |
--createLastLog() | |
local time_length = os.clock() | |
--print("The simulation has ended after "..time_length.." seconds with "..pop_actual.." agents and "..week.." weeks") | |
prevActual = pop_actual | |
prevWeeks = week | |
break | |
end | |
--print(pop_actual) | |
local deaths = 0 | |
local births = 0 | |
local pregnants = 0 | |
local burnins = 0 | |
local checkavg = 0 | |
--age all the agents | |
for numAgent, agent in pairs(pop) do | |
agent.Age = agent.Age + 1 | |
--DEATH FUNCTION | |
if random() <= PreBakedDeathFunction[agent.Age] then | |
agent.Dead = true | |
deaths = deaths + 1 | |
end | |
end | |
--remove dead ones to prevent dead ones from being pregnant | |
for numAgent, agent in pairs(pop) do | |
if agent.Dead == true then | |
table.remove(pop,numAgent) | |
end | |
end | |
--count burnin agents during burnin period | |
if week < 781 then | |
for numAgent, agent in pairs(pop) do | |
if agent.Burnin == true then | |
burnins = burnins + 1 | |
end | |
end | |
else end | |
--make agents pregnant, advance their fetal ages, check for whether it is time for birth, check whether the agent can be returned to a fertile status (post-gestational period) | |
for numAgent, agent in pairs(pop) do | |
--check for Burnin and adjust the agent accordingly | |
if agent.Burnin == true or (week > dsrlngth+1) then | |
if week > dsrlngth then | |
agent.Burnin = false | |
else | |
local chance_unburn = random() | |
if chance_unburn < (1/burnins) then | |
agent.Burnin = false | |
end | |
end | |
end | |
--check for pregnant or post-gestational | |
if agent.Pregnant == true then | |
pregnants = pregnants + 1 | |
--check for whether its delivery time | |
if agent.WeekP >= agent.Gestation then | |
agent.Pregnant = false | |
agent.Pregnancies = agent.Pregnancies + 1 | |
agent.PGestational = true | |
agent.WeekP = 0 | |
createAgents(1,basicAgent,"Birth") | |
births = births + 1 | |
end | |
agent.WeekP = agent.WeekP + 1 | |
elseif agent.PGestational then | |
--check for whether the post-birth recovery period is over | |
if agent.PGestationalWeek >= (agent.Gestation * agent.PGestation ) then | |
agent.PGestational = false | |
agent.PGestationalWeek = 0 | |
end | |
agent.PGestationalWeek = agent.PGestationalWeek + 1 | |
elseif not agent.Pregnant and not agent.PGestational and not agent.Burnin then | |
--calculate chance of pregnancy and manipulate the agent accordingly | |
if agent.Age > agent.Repro and agent.Age < agent.PRepro then | |
if random() < agent.FertilityRoll then | |
agent.Pregnant = true | |
else | |
agent.Pregnant = false | |
end | |
end | |
end | |
checkavg = checkavg + agent.Age | |
end | |
pop_actual = (pop_actual - deaths) + births | |
local checkavg2 = floor(checkavg/pop_actual) | |
--print(checkavg2) | |
--createLogLine(week,deaths,pop_actual,births,pregnants,burnins,checkavg2) [single simulation] | |
--[[if sim_number == 0 then | |
local startlogline = (week.."\t"..deaths.."\t"..pop_actual.."\t"..births.."\t"..pregnants.."\t"..burnins.."\t"..checkavg2) | |
preResults[week] = startlogline | |
elseif sim_number > 0 then | |
createLogLine2(week,deaths,pop_actual,births,pregnants,burnins,checkavg2) | |
end --deprecated multiple simulations]] | |
if runSingle == false then | |
local currentLogLine = tostring((week.."\t"..deaths.."\t"..pop_actual.."\t"..births.."\t"..pregnants.."\t"..checkavg2)) | |
--preResults[week] = currentLogLine | |
table.insert(preResults,currentLogLine) | |
end | |
week = week + 1 | |
checkavg = 0 | |
end | |
end | |
--increment logging | |
local function doAverage(num1,num2) | |
local doneAverage = ((num1+num2)/2) | |
return doneAverage | |
end | |
local function createLogLine2(whatWeek, numDead, numPopulation, numBorn, numPregnant, numBurnin, numAvg) | |
local loglineOld = preResults[whatWeek] | |
local llOstrings = splitString(loglineOld,"\t") | |
print(llOstrings) | |
local logline = tostring((whatWeek.."\t"..(doAverage(llOstrings[2],numDead)).."\t"..(doAverage(llOstrings[3],numPopulation)).."\t"..(doAverage(llOstrings[4],numBorn)).."\t"..(doAverage(llOstrings[5],numPregnant)).."\t"..(doAverage(llOstrings[6],numBurnin)).."\t"..(doAverage(llOstrings[7],numAvg)))) | |
preResults[whatWeek] = logline | |
end | |
local function splitString(inputstr, sep) | |
if sep == nil then | |
sep = "%s" | |
end | |
local t={} | |
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do | |
table.insert(t, str) | |
end | |
return t | |
end | |
local function createSimTable(weekLength) | |
--tracking for 7 datapoints | |
local newLine = "0 \t 1 \t 2 \t 3 \t 4 \t 5" | |
for i = 1, weekLength do | |
table.insert(preResults,newLine) | |
--print(newLine) | |
end | |
print("Data gathering table with "..weekLength.." weeks has been prepared! "..os.clock()) | |
end | |
local function writeWholeLog(simNumber) | |
--F:write("week \t deaths \t population \t births \t pregnancies \t burnins \t avg age") | |
local currentResult = (ResultsFileName..simNumber..".txt") | |
local F9 = io.open(currentResult,"w") | |
for i = 1, sim_length do | |
F9:write("\n"..tostring(preResults[i])) | |
end | |
F9:close() | |
end | |
local function createLogLine(numDead, numPopulation, numBorn, numPregnant, numBurnin, numAvg) | |
local logline | |
logline = ("\n"..week.."\t"..numDead.."\t"..numPopulation.."\t"..numBorn.."\t"..numPregnant.."\t"..numBurnin.."\t"..numAvg) | |
F:write(logline) | |
end | |
local function createLastLog() | |
if verboseoutput == false then return end | |
local lastlogline | |
lastlogline = ("\n*At week "..week.." the simulation ended with "..pop_actual.." agents") | |
--F = io.open(ResultsFile,"w") | |
F:write(lastlogline) | |
--F:close() | |
end | |
--simulation | |
--setup simulation | |
--createAgents(pop_num,basicAgent,"SimStart") | |
--[[do simulation (single) | |
simulate(sim_length) | |
writeWholeLog() | |
print("In "..os.clock().." seconds "..desired_simulations.." simulations with a length of "..desired_length.." years were run and the averages reported in "..tostring(ResultsFile)) | |
if os.clock() > 60 then | |
local minutes = os.clock()/60 | |
print(minutes.." minutes") | |
end | |
if os.clock() > 3600 then | |
local hours = os.clock()/3600 | |
print(hours.." hours") | |
end | |
F:close()]] | |
--do simulation (multiple) | |
local function runSimulations(howMany,howLong) | |
--createSimTable(howLong) | |
local lastTime = os.clock() | |
for X = 1, howMany do | |
preResults = {} | |
pop = {} | |
createAgents(pop_num,basicAgent,"SimStart") | |
--createSimTable(howLong) | |
week = 0 | |
simulate(howLong,pop_num) | |
writeWholeLog(sim_number) | |
sim_number = sim_number + 1 | |
local completion = (X/desired_simulations)*100 | |
if desired_simulations < 51 then | |
print(completion.."% complete") | |
else | |
local checkInteger = completion/sim_interval | |
if completion == checkInteger then | |
local currentTime = (os.clock() - lastTime) | |
print(completion.."% complete (simulation "..sim_number..") in time "..currentTime.." seconds") | |
end | |
end | |
end | |
end | |
runSimulations(desired_simulations,sim_length) | |
--writeWholeLog() | |
print("In "..os.clock().." seconds "..desired_simulations.." simulations with a length of "..desired_length.." years were run and the averages reported in "..tostring(ResultsFile)) | |
if os.clock() > 60 then | |
local minutes = os.clock()/60 | |
print(minutes.." minutes") | |
end | |
if os.clock() > 3600 then | |
local hours = os.clock()/3600 | |
print(hours.." hours") | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment