-
-
Notifications
You must be signed in to change notification settings - Fork 270
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #92 from BlockScience/staging
Merge pred-prey demo
- Loading branch information
Showing
29 changed files
with
1,123 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# Prey Predator demo | ||
|
||
cadCAD P&P modelling in an dynamical system and agent-based modelling approaches. | ||
|
||
## File Structure | ||
|
||
* lab_notebook.ipynb - The notebook for experimenting and visualizing | ||
* run.py - Script for running all configurated experiments | ||
* prey_predator_abm/ - Folder for the ABM simulation and model | ||
* prey_predator_sd/ - Folder for the SD simulation and model | ||
* {simulation}/config.py - Simulation configuration object | ||
* {simulation}/sim_params.py - Simulation parameters | ||
* {simulation}/model/partial_state_update_block.py - The structure of the logic behind the model | ||
* {simulation}/model/state_variables.py - Model initial state | ||
* {simulation}/model/sys_params.py - Model parameters | ||
* {simulation}/model/parts/ - Model logic |
Large diffs are not rendered by default.
Oops, something went wrong.
Empty file.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
""" | ||
Simulation configuration object. | ||
""" | ||
|
||
|
||
MONTE_CARLO_RUNS = 1 # N monte carlo runs | ||
|
||
from cadCAD.configuration import append_configs | ||
from cadCAD.configuration.utils import config_sim | ||
from .model.state_variables import genesis_states | ||
from .model.partial_state_update_block import partial_state_update_block | ||
from .model.sys_params import sys_params as sys_params | ||
from .sim_params import SIMULATION_TIME_STEPS | ||
|
||
|
||
sim_config = config_sim ( | ||
{ | ||
'N': MONTE_CARLO_RUNS, | ||
'T': range(SIMULATION_TIME_STEPS), # number of timesteps | ||
'M': sys_params, | ||
} | ||
) | ||
append_configs( | ||
sim_configs=sim_config, | ||
initial_state=genesis_states, | ||
partial_state_update_blocks=partial_state_update_block | ||
) |
Empty file.
76 changes: 76 additions & 0 deletions
76
demos/prey-predator/prey_predator_abm/model/partial_state_update_block.py
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
""" | ||
Model logic structure. | ||
""" | ||
|
||
|
||
from .parts.environment.food_regeneration import * | ||
from .parts.agents.feeding import * | ||
from .parts.agents.reproduction import * | ||
from .parts.agents.movement import * | ||
from .parts.agents.ageing import * | ||
from .parts.agents.natural_death import * | ||
|
||
|
||
partial_state_update_block = [ | ||
{ | ||
'policies': { | ||
'grow_food': p_grow_food | ||
}, | ||
'variables': { | ||
'sites': s_update_food | ||
} | ||
}, | ||
{ | ||
'policies': { | ||
'increase_agent_age': p_digest_and_olden | ||
}, | ||
'variables': { | ||
'agents': s_agent_food_age | ||
|
||
} | ||
}, | ||
{ | ||
'policies': { | ||
'move_agent': p_move_agents | ||
}, | ||
'variables': { | ||
'agents': s_agent_location | ||
|
||
} | ||
}, | ||
{ | ||
'policies': { | ||
'reproduce_agents': p_reproduce_agents | ||
|
||
}, | ||
'variables': { | ||
'agents': s_agent_create, | ||
|
||
} | ||
}, | ||
{ | ||
'policies': { | ||
'feed_prey': p_feed_prey | ||
}, | ||
'variables': { | ||
'agents': s_agent_food, | ||
'sites': s_site_food | ||
} | ||
}, | ||
{ | ||
'policies': { | ||
'hunt_prey': p_hunt_prey | ||
}, | ||
'variables': { | ||
'agents': s_agent_food | ||
} | ||
}, | ||
{ | ||
'policies': { | ||
'natural_death': p_natural_death | ||
}, | ||
'variables': { | ||
'agents': s_agent_remove | ||
} | ||
} | ||
] |
Empty file.
Empty file.
18 changes: 18 additions & 0 deletions
18
demos/prey-predator/prey_predator_abm/model/parts/agents/ageing.py
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
def p_digest_and_olden(params, substep, state_history, prev_state): | ||
agents = prev_state['agents'] | ||
delta_food = {agent: -1 for agent in agents.keys()} | ||
delta_age = {agent: +1 for agent in agents.keys()} | ||
return {'agent_delta_food': delta_food, | ||
'agent_delta_age': delta_age} | ||
|
||
|
||
def s_agent_food_age(params, substep, state_history, prev_state, policy_input): | ||
delta_food_by_agent = policy_input['agent_delta_food'] | ||
delta_age_by_agent = policy_input['agent_delta_age'] | ||
updated_agents = prev_state['agents'].copy() | ||
|
||
for agent, delta_food in delta_food_by_agent.items(): | ||
updated_agents[agent]['food'] += delta_food | ||
for agent, delta_age in delta_age_by_agent.items(): | ||
updated_agents[agent]['age'] += delta_age | ||
return ('agents', updated_agents) |
64 changes: 64 additions & 0 deletions
64
demos/prey-predator/prey_predator_abm/model/parts/agents/feeding.py
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
from ..location import nearby_agents | ||
import random | ||
|
||
def p_feed_prey(params, substep, state_history, prev_state): | ||
""" | ||
Feeds the hungry prey with all food located on its site. | ||
""" | ||
agents = prev_state['agents'] | ||
sites = prev_state['sites'] | ||
preys = {k: v for k, v in agents.items() if v['type'] == 'prey'} | ||
hungry_preys = {label: properties for label, properties in preys.items() | ||
if properties['food'] < params['hunger_threshold']} | ||
|
||
agent_delta_food = {} | ||
site_delta_food = {} | ||
for label, properties in hungry_preys.items(): | ||
location = properties['location'] | ||
available_food = sites[location] | ||
agent_delta_food[label] = available_food | ||
site_delta_food[location] = -available_food | ||
|
||
return {'agent_delta_food': agent_delta_food, | ||
'site_delta_food': site_delta_food} | ||
|
||
|
||
def s_agent_food(params, substep, state_history, prev_state, policy_input): | ||
updated_agents = prev_state['agents'].copy() | ||
for label, delta_food in policy_input['agent_delta_food'].items(): | ||
updated_agents[label]['food'] += delta_food | ||
return ('agents', updated_agents) | ||
|
||
|
||
def s_site_food(params, substep, state_history, prev_state, policy_input): | ||
updated_sites = prev_state['sites'].copy() | ||
for label, delta_food in policy_input['site_delta_food'].items(): | ||
updated_sites[label] += delta_food | ||
return ('sites', updated_sites) | ||
|
||
def p_hunt_prey(params, substep, state_history, prev_state): | ||
""" | ||
Feeds the hungry predators with an random nearby prey. | ||
""" | ||
agents = prev_state['agents'] | ||
sites = prev_state['sites'] | ||
hungry_threshold = params['hunger_threshold'] | ||
preys = {k: v for k, v in agents.items() | ||
if v['type'] == 'prey'} | ||
predators = {k: v for k, v in agents.items() | ||
if v['type'] == 'predator'} | ||
hungry_predators = {k: v for k, v in predators.items() | ||
if v['food'] < hungry_threshold} | ||
agent_delta_food = {} | ||
for predator_label, predator_properties in hungry_predators.items(): | ||
location = predator_properties['location'] | ||
nearby_preys = nearby_agents(location, preys) | ||
if len(nearby_preys) > 0: | ||
eaten_prey_label = random.choice(list(nearby_preys.keys())) | ||
delta_food = preys.pop(eaten_prey_label)['food'] | ||
agent_delta_food[predator_label] = delta_food | ||
agent_delta_food[eaten_prey_label] = -1 * delta_food | ||
else: | ||
continue | ||
|
||
return {'agent_delta_food': agent_delta_food} |
26 changes: 26 additions & 0 deletions
26
demos/prey-predator/prey_predator_abm/model/parts/agents/movement.py
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
from ..location import get_free_location | ||
|
||
|
||
def p_move_agents(params, substep, state_history, prev_state): | ||
""" | ||
Move agents. | ||
""" | ||
sites = prev_state['sites'] | ||
agents = prev_state['agents'] | ||
busy_locations = [agent['location'] for agent in agents.values()] | ||
new_locations = {} | ||
for label, properties in agents.items(): | ||
new_location = get_free_location(properties['location'], sites, busy_locations) | ||
if new_location is not False: | ||
new_locations[label] = new_location | ||
busy_locations.append(new_location) | ||
else: | ||
continue | ||
return {'update_agent_location': new_locations} | ||
|
||
|
||
def s_agent_location(params, substep, state_history, prev_state, policy_input): | ||
updated_agents = prev_state['agents'].copy() | ||
for label, location in policy_input['update_agent_location'].items(): | ||
updated_agents[label]['location'] = location | ||
return ('agents', updated_agents) |
21 changes: 21 additions & 0 deletions
21
demos/prey-predator/prey_predator_abm/model/parts/agents/natural_death.py
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
|
||
def p_natural_death(params, substep, state_history, prev_state): | ||
""" | ||
Remove agents which are old or hungry enough. | ||
""" | ||
agents = prev_state['agents'] | ||
maximum_age = params['agent_lifespan'] | ||
agents_to_remove = [] | ||
for agent_label, agent_properties in agents.items(): | ||
to_remove = agent_properties['age'] > maximum_age | ||
to_remove |= (agent_properties['food'] <= 0) | ||
if to_remove: | ||
agents_to_remove.append(agent_label) | ||
return {'remove_agents': agents_to_remove} | ||
|
||
|
||
def s_agent_remove(params, substep, state_history, prev_state, policy_input): | ||
agents_to_remove = policy_input['remove_agents'] | ||
surviving_agents = {k: v for k, v in prev_state['agents'].items() | ||
if k not in agents_to_remove} | ||
return ('agents', surviving_agents) |
57 changes: 57 additions & 0 deletions
57
demos/prey-predator/prey_predator_abm/model/parts/agents/reproduction.py
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import random | ||
from uuid import uuid4 | ||
from ..location import nearby_agents, get_free_location | ||
|
||
def p_reproduce_agents(params, substep, state_history, prev_state): | ||
""" | ||
Generates an new agent through an nearby agent pair, subject to rules. | ||
Not done. | ||
""" | ||
agents = prev_state['agents'] | ||
sites = prev_state['sites'] | ||
food_threshold = params['reproduction_food_threshold'] | ||
reproduction_food = params['reproduction_food'] | ||
reproduction_probability = params['reproduction_probability'] | ||
busy_locations = [agent['location'] for agent in agents.values()] | ||
already_reproduced = [] | ||
new_agents = {} | ||
agent_delta_food = {} | ||
for agent_type in set(agent['type'] for agent in agents.values()): | ||
# Only reproduce agents of the same type | ||
specific_agents = {label: agent for label, agent in agents.items() | ||
if agent['type'] == agent_type} | ||
for agent_label, agent_properties in specific_agents.items(): | ||
location = agent_properties['location'] | ||
if (agent_properties['food'] < food_threshold or agent_label in already_reproduced): | ||
continue | ||
kind_neighbors = nearby_agents(location, specific_agents) | ||
available_partners = [label for label, agent in kind_neighbors.items() | ||
if agent['food'] >= food_threshold | ||
and label not in already_reproduced] | ||
|
||
reproduction_location = get_free_location(location, sites, busy_locations) | ||
|
||
if reproduction_location is not False and len(available_partners) > 0: | ||
reproduction_partner_label = random.choice(available_partners) | ||
reproduction_partner = agents[reproduction_partner_label] | ||
already_reproduced += [agent_label, reproduction_partner_label] | ||
|
||
agent_delta_food[agent_label] = -1.0 * reproduction_food | ||
agent_delta_food[reproduction_partner_label] = -1.0 * reproduction_food | ||
new_agent_properties = {'type': agent_type, | ||
'location': reproduction_location, | ||
'food': 2.0 * reproduction_food, | ||
'age': 0} | ||
new_agents[uuid4()] = new_agent_properties | ||
busy_locations.append(reproduction_location) | ||
|
||
return {'agent_delta_food': agent_delta_food, | ||
'agent_create': new_agents} | ||
|
||
def s_agent_create(params, substep, state_history, prev_state, policy_input): | ||
updated_agents = prev_state['agents'].copy() | ||
for label, food in policy_input['agent_delta_food'].items(): | ||
updated_agents[label]['food'] += food | ||
for label, properties in policy_input['agent_create'].items(): | ||
updated_agents[label] = properties | ||
return ('agents', updated_agents) |
Empty file.
24 changes: 24 additions & 0 deletions
24
demos/prey-predator/prey_predator_abm/model/parts/environment/food_regeneration.py
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import numpy as np | ||
|
||
@np.vectorize | ||
def calculate_increment(value, growth_rate, max_value): | ||
new_value = (value + growth_rate | ||
if value + growth_rate < max_value | ||
else max_value) | ||
return new_value | ||
|
||
|
||
def p_grow_food(params, substep, state_history, prev_state): | ||
""" | ||
Increases the food supply in all sites, subject to an maximum. | ||
""" | ||
regenerated_sites = calculate_increment(prev_state['sites'], | ||
params['food_growth_rate'], | ||
params['maximum_food_per_site']) | ||
return {'update_food': regenerated_sites} | ||
|
||
|
||
def s_update_food(params, substep, state_history, prev_state, policy_input): | ||
key = 'sites' | ||
value = policy_input['update_food'] | ||
return (key, value) |
58 changes: 58 additions & 0 deletions
58
demos/prey-predator/prey_predator_abm/model/parts/location.py
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
""" | ||
Helper functions associated with location | ||
""" | ||
|
||
|
||
import numpy as np | ||
import random | ||
from typing import * | ||
|
||
|
||
def check_location(position: tuple, | ||
all_sites: np.matrix, | ||
busy_locations: List[tuple]) -> List[tuple]: | ||
""" | ||
Returns an list of available location tuples neighboring an given | ||
position location. | ||
""" | ||
N, M = all_sites.shape | ||
potential_sites = [(position[0], position[1] + 1), | ||
(position[0], position[1] - 1), | ||
(position[0] + 1, position[1]), | ||
(position[0] - 1, position[1])] | ||
potential_sites = [(site[0] % N, site[1] % M) for site in potential_sites] | ||
valid_sites = [site for site in potential_sites if site not in busy_locations] | ||
return valid_sites | ||
|
||
|
||
def get_free_location(position: tuple, | ||
all_sites: np.matrix, | ||
used_sites: List[tuple]) -> tuple: | ||
""" | ||
Gets an random free location neighboring an position. Returns False if | ||
there aren't any location available. | ||
""" | ||
available_locations = check_location(position, all_sites, used_sites) | ||
if len(available_locations) > 0: | ||
return random.choice(available_locations) | ||
else: | ||
return False | ||
|
||
|
||
def nearby_agents(location: tuple, agents: Dict[str, dict]) -> Dict[str, dict]: | ||
""" | ||
Filter the non-nearby agents. | ||
""" | ||
neighbors = {label: agent for label, agent in agents.items() | ||
if is_neighbor(agent['location'], location)} | ||
return neighbors | ||
|
||
|
||
def is_neighbor(location_1: tuple, location_2: tuple) -> bool: | ||
dx = np.abs(location_1[0] - location_2[0]) | ||
dy = (location_1[1] - location_2[0]) | ||
distance = dx + dy | ||
if distance == 1: | ||
return True | ||
else: | ||
return False |
Oops, something went wrong.