mirror of
https://github.com/gsi-upm/soil
synced 2024-11-21 10:42:28 +00:00
0.13.8
This commit is contained in:
parent
65f6aa72f3
commit
a3ea434f23
12
CHANGELOG.md
12
CHANGELOG.md
@ -3,9 +3,17 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [Unreleased]
|
## [0.13.8]
|
||||||
### Changed
|
### Changed
|
||||||
|
* Moved TerroristNetworkModel to examples
|
||||||
### Added
|
### Added
|
||||||
|
* `get_agents` and `count_agents` methods now accept lists as inputs. They can be used to retrieve agents from node ids
|
||||||
|
* `subgraph` in BaseAgent
|
||||||
|
* `agents.select` method, to filter out agents
|
||||||
|
* `skip_test` property in yaml definitions, to force skipping some examples
|
||||||
|
* `agents.Geo`, with a search function based on postition
|
||||||
|
* `BaseAgent.ego_search` to get nodes from the ego network of a node
|
||||||
|
* `BaseAgent.degree` and `BaseAgent.betweenness`
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
## [0.13.7]
|
## [0.13.7]
|
||||||
@ -16,4 +24,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||||||
* Agent logging uses the agent name.
|
* Agent logging uses the agent name.
|
||||||
* FSM agents can now return a timeout in addition to a new state. e.g. `return self.idle, self.env.timeout(2)` will execute the *different_state* in 2 *units of time* (`t_step=now+2`).
|
* FSM agents can now return a timeout in addition to a new state. e.g. `return self.idle, self.env.timeout(2)` will execute the *different_state* in 2 *units of time* (`t_step=now+2`).
|
||||||
* Example of using timeouts in FSM (custom_timeouts)
|
* Example of using timeouts in FSM (custom_timeouts)
|
||||||
* `network_agents` entries may include an `ids` entry. If set, it should be a list of node ids that should be assigned that agent type. This complements the previous behavior of setting agent type with `weights`.
|
* `network_agents` entries may include an `ids` entry. If set, it should be a list of node ids that should be assigned that agent type. This complements the previous behavior of setting agent type with `weights`.
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
include requirements.txt
|
include requirements.txt
|
||||||
include test-requirements.txt
|
include test-requirements.txt
|
||||||
include README.rst
|
include README.rst
|
||||||
graft soil
|
graft soil
|
||||||
|
global-exclude __pycache__
|
||||||
|
global-exclude soil_output
|
||||||
|
global-exclude *.py[co]
|
||||||
|
@ -6,7 +6,7 @@ environment_params:
|
|||||||
prob_neighbor_spread: 0.0
|
prob_neighbor_spread: 0.0
|
||||||
prob_tv_spread: 0.01
|
prob_tv_spread: 0.01
|
||||||
interval: 1
|
interval: 1
|
||||||
max_time: 30
|
max_time: 300
|
||||||
name: Sim_all_dumb
|
name: Sim_all_dumb
|
||||||
network_agents:
|
network_agents:
|
||||||
- agent_type: DumbViewer
|
- agent_type: DumbViewer
|
||||||
@ -30,7 +30,7 @@ environment_params:
|
|||||||
prob_neighbor_spread: 0.0
|
prob_neighbor_spread: 0.0
|
||||||
prob_tv_spread: 0.01
|
prob_tv_spread: 0.01
|
||||||
interval: 1
|
interval: 1
|
||||||
max_time: 30
|
max_time: 300
|
||||||
name: Sim_half_herd
|
name: Sim_half_herd
|
||||||
network_agents:
|
network_agents:
|
||||||
- agent_type: DumbViewer
|
- agent_type: DumbViewer
|
||||||
@ -62,7 +62,7 @@ environment_params:
|
|||||||
prob_neighbor_spread: 0.0
|
prob_neighbor_spread: 0.0
|
||||||
prob_tv_spread: 0.01
|
prob_tv_spread: 0.01
|
||||||
interval: 1
|
interval: 1
|
||||||
max_time: 30
|
max_time: 300
|
||||||
name: Sim_all_herd
|
name: Sim_all_herd
|
||||||
network_agents:
|
network_agents:
|
||||||
- agent_type: HerdViewer
|
- agent_type: HerdViewer
|
||||||
@ -89,7 +89,7 @@ environment_params:
|
|||||||
prob_tv_spread: 0.01
|
prob_tv_spread: 0.01
|
||||||
prob_neighbor_cure: 0.1
|
prob_neighbor_cure: 0.1
|
||||||
interval: 1
|
interval: 1
|
||||||
max_time: 30
|
max_time: 300
|
||||||
name: Sim_wise_herd
|
name: Sim_wise_herd
|
||||||
network_agents:
|
network_agents:
|
||||||
- agent_type: HerdViewer
|
- agent_type: HerdViewer
|
||||||
@ -115,7 +115,7 @@ environment_params:
|
|||||||
prob_tv_spread: 0.01
|
prob_tv_spread: 0.01
|
||||||
prob_neighbor_cure: 0.1
|
prob_neighbor_cure: 0.1
|
||||||
interval: 1
|
interval: 1
|
||||||
max_time: 30
|
max_time: 300
|
||||||
name: Sim_all_wise
|
name: Sim_all_wise
|
||||||
network_agents:
|
network_agents:
|
||||||
- agent_type: WiseViewer
|
- agent_type: WiseViewer
|
||||||
|
208
examples/terrorism/TerroristNetworkModel.py
Normal file
208
examples/terrorism/TerroristNetworkModel.py
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
import random
|
||||||
|
import networkx as nx
|
||||||
|
from soil.agents import Geo, NetworkAgent, FSM, state, default_state
|
||||||
|
from soil import Environment
|
||||||
|
|
||||||
|
|
||||||
|
class TerroristSpreadModel(FSM, Geo):
|
||||||
|
"""
|
||||||
|
Settings:
|
||||||
|
information_spread_intensity
|
||||||
|
|
||||||
|
terrorist_additional_influence
|
||||||
|
|
||||||
|
min_vulnerability (optional else zero)
|
||||||
|
|
||||||
|
max_vulnerability
|
||||||
|
|
||||||
|
prob_interaction
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, environment=None, agent_id=0, state=()):
|
||||||
|
super().__init__(environment=environment, agent_id=agent_id, state=state)
|
||||||
|
|
||||||
|
self.information_spread_intensity = environment.environment_params['information_spread_intensity']
|
||||||
|
self.terrorist_additional_influence = environment.environment_params['terrorist_additional_influence']
|
||||||
|
self.prob_interaction = environment.environment_params['prob_interaction']
|
||||||
|
|
||||||
|
if self['id'] == self.civilian.id: # Civilian
|
||||||
|
self.mean_belief = random.uniform(0.00, 0.5)
|
||||||
|
elif self['id'] == self.terrorist.id: # Terrorist
|
||||||
|
self.mean_belief = random.uniform(0.8, 1.00)
|
||||||
|
elif self['id'] == self.leader.id: # Leader
|
||||||
|
self.mean_belief = 1.00
|
||||||
|
else:
|
||||||
|
raise Exception('Invalid state id: {}'.format(self['id']))
|
||||||
|
|
||||||
|
if 'min_vulnerability' in environment.environment_params:
|
||||||
|
self.vulnerability = random.uniform( environment.environment_params['min_vulnerability'], environment.environment_params['max_vulnerability'] )
|
||||||
|
else :
|
||||||
|
self.vulnerability = random.uniform( 0, environment.environment_params['max_vulnerability'] )
|
||||||
|
|
||||||
|
|
||||||
|
@state
|
||||||
|
def civilian(self):
|
||||||
|
neighbours = list(self.get_neighboring_agents(agent_type=TerroristSpreadModel))
|
||||||
|
if len(neighbours) > 0:
|
||||||
|
# Only interact with some of the neighbors
|
||||||
|
interactions = list(n for n in neighbours if random.random() <= self.prob_interaction)
|
||||||
|
influence = sum( self.degree(i) for i in interactions )
|
||||||
|
mean_belief = sum( i.mean_belief * self.degree(i) / influence for i in interactions )
|
||||||
|
mean_belief = mean_belief * self.information_spread_intensity + self.mean_belief * ( 1 - self.information_spread_intensity )
|
||||||
|
self.mean_belief = mean_belief * self.vulnerability + self.mean_belief * ( 1 - self.vulnerability )
|
||||||
|
|
||||||
|
if self.mean_belief >= 0.8:
|
||||||
|
return self.terrorist
|
||||||
|
|
||||||
|
@state
|
||||||
|
def leader(self):
|
||||||
|
self.mean_belief = self.mean_belief ** ( 1 - self.terrorist_additional_influence )
|
||||||
|
for neighbour in self.get_neighboring_agents(state_id=[self.terrorist.id, self.leader.id]):
|
||||||
|
if self.betweenness(neighbour) > self.betweenness(self):
|
||||||
|
return self.terrorist
|
||||||
|
|
||||||
|
@state
|
||||||
|
def terrorist(self):
|
||||||
|
neighbours = self.get_agents(state_id=[self.terrorist.id, self.leader.id],
|
||||||
|
agent_type=TerroristSpreadModel,
|
||||||
|
limit_neighbors=True)
|
||||||
|
if len(neighbours) > 0:
|
||||||
|
influence = sum( self.degree(n) for n in neighbours )
|
||||||
|
mean_belief = sum( n.mean_belief * self.degree(n) / influence for n in neighbours )
|
||||||
|
mean_belief = mean_belief * self.vulnerability + self.mean_belief * ( 1 - self.vulnerability )
|
||||||
|
self.mean_belief = self.mean_belief ** ( 1 - self.terrorist_additional_influence )
|
||||||
|
|
||||||
|
# Check if there are any leaders in the group
|
||||||
|
leaders = list(filter(lambda x: x.state.id == self.leader.id, neighbours))
|
||||||
|
if not leaders:
|
||||||
|
# Check if this is the potential leader
|
||||||
|
# Stop once it's found. Otherwise, set self as leader
|
||||||
|
for neighbour in neighbours:
|
||||||
|
if self.betweenness(self) < self.betweenness(neighbour):
|
||||||
|
return
|
||||||
|
return self.leader
|
||||||
|
|
||||||
|
|
||||||
|
class TrainingAreaModel(FSM, Geo):
|
||||||
|
"""
|
||||||
|
Settings:
|
||||||
|
training_influence
|
||||||
|
|
||||||
|
min_vulnerability
|
||||||
|
|
||||||
|
Requires TerroristSpreadModel.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, environment=None, agent_id=0, state=()):
|
||||||
|
super().__init__(environment=environment, agent_id=agent_id, state=state)
|
||||||
|
self.training_influence = environment.environment_params['training_influence']
|
||||||
|
if 'min_vulnerability' in environment.environment_params:
|
||||||
|
self.min_vulnerability = environment.environment_params['min_vulnerability']
|
||||||
|
else: self.min_vulnerability = 0
|
||||||
|
|
||||||
|
@default_state
|
||||||
|
@state
|
||||||
|
def terrorist(self):
|
||||||
|
for neighbour in self.get_neighboring_agents(agent_type=TerroristSpreadModel):
|
||||||
|
if neighbour.vulnerability > self.min_vulnerability:
|
||||||
|
neighbour.vulnerability = neighbour.vulnerability ** ( 1 - self.training_influence )
|
||||||
|
|
||||||
|
|
||||||
|
class HavenModel(FSM, Geo):
|
||||||
|
"""
|
||||||
|
Settings:
|
||||||
|
haven_influence
|
||||||
|
|
||||||
|
min_vulnerability
|
||||||
|
|
||||||
|
max_vulnerability
|
||||||
|
|
||||||
|
Requires TerroristSpreadModel.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, environment=None, agent_id=0, state=()):
|
||||||
|
super().__init__(environment=environment, agent_id=agent_id, state=state)
|
||||||
|
self.haven_influence = environment.environment_params['haven_influence']
|
||||||
|
if 'min_vulnerability' in environment.environment_params:
|
||||||
|
self.min_vulnerability = environment.environment_params['min_vulnerability']
|
||||||
|
else: self.min_vulnerability = 0
|
||||||
|
self.max_vulnerability = environment.environment_params['max_vulnerability']
|
||||||
|
|
||||||
|
def get_occupants(self, **kwargs):
|
||||||
|
return self.get_neighboring_agents(agent_type=TerroristSpreadModel, **kwargs)
|
||||||
|
|
||||||
|
@state
|
||||||
|
def civilian(self):
|
||||||
|
civilians = self.get_occupants(state_id=self.civilian.id)
|
||||||
|
if not civilians:
|
||||||
|
return self.terrorist
|
||||||
|
|
||||||
|
for neighbour in self.get_occupants():
|
||||||
|
if neighbour.vulnerability > self.min_vulnerability:
|
||||||
|
neighbour.vulnerability = neighbour.vulnerability * ( 1 - self.haven_influence )
|
||||||
|
return self.civilian
|
||||||
|
|
||||||
|
@state
|
||||||
|
def terrorist(self):
|
||||||
|
for neighbour in self.get_occupants():
|
||||||
|
if neighbour.vulnerability < self.max_vulnerability:
|
||||||
|
neighbour.vulnerability = neighbour.vulnerability ** ( 1 - self.haven_influence )
|
||||||
|
return self.terrorist
|
||||||
|
|
||||||
|
|
||||||
|
class TerroristNetworkModel(TerroristSpreadModel):
|
||||||
|
"""
|
||||||
|
Settings:
|
||||||
|
sphere_influence
|
||||||
|
|
||||||
|
vision_range
|
||||||
|
|
||||||
|
weight_social_distance
|
||||||
|
|
||||||
|
weight_link_distance
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, environment=None, agent_id=0, state=()):
|
||||||
|
super().__init__(environment=environment, agent_id=agent_id, state=state)
|
||||||
|
|
||||||
|
self.vision_range = environment.environment_params['vision_range']
|
||||||
|
self.sphere_influence = environment.environment_params['sphere_influence']
|
||||||
|
self.weight_social_distance = environment.environment_params['weight_social_distance']
|
||||||
|
self.weight_link_distance = environment.environment_params['weight_link_distance']
|
||||||
|
|
||||||
|
@state
|
||||||
|
def terrorist(self):
|
||||||
|
self.update_relationships()
|
||||||
|
return super().terrorist()
|
||||||
|
|
||||||
|
@state
|
||||||
|
def leader(self):
|
||||||
|
self.update_relationships()
|
||||||
|
return super().leader()
|
||||||
|
|
||||||
|
def update_relationships(self):
|
||||||
|
if self.count_neighboring_agents(state_id=self.civilian.id) == 0:
|
||||||
|
close_ups = set(self.geo_search(radius=self.vision_range, agent_type=TerroristNetworkModel))
|
||||||
|
step_neighbours = set(self.ego_search(self.sphere_influence, agent_type=TerroristNetworkModel, center=False))
|
||||||
|
neighbours = set(agent.id for agent in self.get_neighboring_agents(agent_type=TerroristNetworkModel))
|
||||||
|
search = (close_ups | step_neighbours) - neighbours
|
||||||
|
for agent in self.get_agents(search):
|
||||||
|
social_distance = 1 / self.shortest_path_length(agent.id)
|
||||||
|
spatial_proximity = ( 1 - self.get_distance(agent.id) )
|
||||||
|
prob_new_interaction = self.weight_social_distance * social_distance + self.weight_link_distance * spatial_proximity
|
||||||
|
if agent['id'] == agent.civilian.id and random.random() < prob_new_interaction:
|
||||||
|
self.add_edge(agent)
|
||||||
|
break
|
||||||
|
|
||||||
|
def get_distance(self, target):
|
||||||
|
source_x, source_y = nx.get_node_attributes(self.global_topology, 'pos')[self.id]
|
||||||
|
target_x, target_y = nx.get_node_attributes(self.global_topology, 'pos')[target]
|
||||||
|
dx = abs( source_x - target_x )
|
||||||
|
dy = abs( source_y - target_y )
|
||||||
|
return ( dx ** 2 + dy ** 2 ) ** ( 1 / 2 )
|
||||||
|
|
||||||
|
def shortest_path_length(self, target):
|
||||||
|
try:
|
||||||
|
return nx.shortest_path_length(self.global_topology, self.id, target)
|
||||||
|
except nx.NetworkXNoPath:
|
||||||
|
return float('inf')
|
@ -60,3 +60,4 @@ visualization_params:
|
|||||||
background_image: 'map_4800x2860.jpg'
|
background_image: 'map_4800x2860.jpg'
|
||||||
background_opacity: '0.9'
|
background_opacity: '0.9'
|
||||||
background_filter_color: 'blue'
|
background_filter_color: 'blue'
|
||||||
|
skip_test: true # This simulation takes too long for automated tests.
|
@ -5,3 +5,4 @@ numpy
|
|||||||
matplotlib
|
matplotlib
|
||||||
pyyaml
|
pyyaml
|
||||||
pandas
|
pandas
|
||||||
|
scipy
|
||||||
|
@ -1 +1 @@
|
|||||||
0.13.7
|
0.13.8
|
||||||
|
@ -22,11 +22,17 @@ class AggregatedCounter(BaseAgent):
|
|||||||
in each step and adds it to its state.
|
in each step and adds it to its state.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
defaults = {
|
||||||
|
'times': 0,
|
||||||
|
'neighbors': 0,
|
||||||
|
'total': 0
|
||||||
|
}
|
||||||
|
|
||||||
def step(self):
|
def step(self):
|
||||||
# Outside effects
|
# Outside effects
|
||||||
total = len(list(self.get_all_agents()))
|
self['times'] += 1
|
||||||
neighbors = len(list(self.get_neighboring_agents()))
|
neighbors = len(list(self.get_neighboring_agents()))
|
||||||
self['times'] = self.get('times', 0) + 1
|
self['neighbors'] += neighbors
|
||||||
self['neighbors'] = self.get('neighbors', 0) + neighbors
|
total = len(list(self.get_all_agents()))
|
||||||
self['total'] = total = self.get('total', 0) + total
|
self['total'] += total
|
||||||
self.debug('Running for step: {}. Total: {}'.format(self.now, total))
|
self.debug('Running for step: {}. Total: {}'.format(self.now, total))
|
||||||
|
@ -10,6 +10,7 @@ import logging
|
|||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
from scipy.spatial import cKDTree as KDTree
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
@ -17,6 +18,12 @@ from functools import wraps
|
|||||||
from .. import utils, history
|
from .. import utils, history
|
||||||
|
|
||||||
|
|
||||||
|
def as_node(agent):
|
||||||
|
if isinstance(agent, BaseAgent):
|
||||||
|
return agent.id
|
||||||
|
return agent
|
||||||
|
|
||||||
|
|
||||||
class BaseAgent(nxsim.BaseAgent):
|
class BaseAgent(nxsim.BaseAgent):
|
||||||
"""
|
"""
|
||||||
A special simpy BaseAgent that keeps track of its state history.
|
A special simpy BaseAgent that keeps track of its state history.
|
||||||
@ -46,8 +53,7 @@ class BaseAgent(nxsim.BaseAgent):
|
|||||||
|
|
||||||
if not hasattr(self, 'level'):
|
if not hasattr(self, 'level'):
|
||||||
self.level = logging.DEBUG
|
self.level = logging.DEBUG
|
||||||
self.logger = logging.getLogger('{}.{}'.format(self.env.name,
|
self.logger = logging.getLogger(self.env.name)
|
||||||
self.id))
|
|
||||||
self.logger.setLevel(self.level)
|
self.logger.setLevel(self.level)
|
||||||
|
|
||||||
# initialize every time an instance of the agent is created
|
# initialize every time an instance of the agent is created
|
||||||
@ -134,43 +140,21 @@ class BaseAgent(nxsim.BaseAgent):
|
|||||||
def step(self):
|
def step(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def count_agents(self, state_id=None, limit_neighbors=False):
|
def count_agents(self, **kwargs):
|
||||||
|
return len(list(self.get_agents(**kwargs)))
|
||||||
|
|
||||||
|
def count_neighboring_agents(self, state_id=None, **kwargs):
|
||||||
|
return len(super().get_neighboring_agents(state_id=state_id, **kwargs))
|
||||||
|
|
||||||
|
def get_neighboring_agents(self, state_id=None, **kwargs):
|
||||||
|
return self.get_agents(limit_neighbors=True, state_id=state_id, **kwargs)
|
||||||
|
|
||||||
|
def get_agents(self, agents=None, limit_neighbors=False, **kwargs):
|
||||||
if limit_neighbors:
|
if limit_neighbors:
|
||||||
agents = self.global_topology.neighbors(self.id)
|
agents = super().get_agents(limit_neighbors=limit_neighbors)
|
||||||
else:
|
else:
|
||||||
agents = self.global_topology.nodes()
|
agents = self.env.get_agents(agents)
|
||||||
count = 0
|
return select(agents, **kwargs)
|
||||||
for agent in agents:
|
|
||||||
if state_id and state_id != self.global_topology.node[agent]['agent']['id']:
|
|
||||||
continue
|
|
||||||
count += 1
|
|
||||||
return count
|
|
||||||
|
|
||||||
def count_neighboring_agents(self, state_id=None):
|
|
||||||
return len(super().get_agents(state_id, limit_neighbors=True))
|
|
||||||
|
|
||||||
def get_agents(self, state_id=None, agent_type=None, limit_neighbors=False, iterator=False, **kwargs):
|
|
||||||
agents = self.env.agents
|
|
||||||
if limit_neighbors:
|
|
||||||
agents = super().get_agents(state_id, limit_neighbors)
|
|
||||||
|
|
||||||
def matches_all(agent):
|
|
||||||
if state_id is not None:
|
|
||||||
if agent.state.get('id', None) != state_id:
|
|
||||||
return False
|
|
||||||
if agent_type is not None:
|
|
||||||
if type(agent) != agent_type:
|
|
||||||
return False
|
|
||||||
state = agent.state
|
|
||||||
for k, v in kwargs.items():
|
|
||||||
if state.get(k, None) != v:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
f = filter(matches_all, agents)
|
|
||||||
if iterator:
|
|
||||||
return f
|
|
||||||
return list(f)
|
|
||||||
|
|
||||||
def log(self, message, *args, level=logging.INFO, **kwargs):
|
def log(self, message, *args, level=logging.INFO, **kwargs):
|
||||||
message = message + " ".join(str(i) for i in args)
|
message = message + " ".join(str(i) for i in args)
|
||||||
@ -208,31 +192,76 @@ class BaseAgent(nxsim.BaseAgent):
|
|||||||
self._state = state['_state']
|
self._state = state['_state']
|
||||||
self.env = state['environment']
|
self.env = state['environment']
|
||||||
|
|
||||||
|
def add_edge(self, node1, node2, **attrs):
|
||||||
|
node1 = as_node(node1)
|
||||||
|
node2 = as_node(node2)
|
||||||
|
|
||||||
def state(func):
|
for n in [node1, node2]:
|
||||||
'''
|
if n not in self.global_topology.nodes(data=False):
|
||||||
A state function should return either a state id, or a tuple (state_id, when)
|
raise ValueError('"{}" not in the graph'.format(n))
|
||||||
The default value for state_id is the current state id.
|
return self.global_topology.add_edge(node1, node2, **attrs)
|
||||||
The default value for when is the interval defined in the nevironment.
|
|
||||||
'''
|
|
||||||
|
|
||||||
@wraps(func)
|
def subgraph(self, center=True, **kwargs):
|
||||||
def func_wrapper(self):
|
include = [self] if center else []
|
||||||
next_state = func(self)
|
return self.global_topology.subgraph(n.id for n in self.get_agents(**kwargs)+include)
|
||||||
when = None
|
|
||||||
if next_state is None:
|
|
||||||
|
class NetworkAgent(BaseAgent):
|
||||||
|
|
||||||
|
def add_edge(self, other, **kwargs):
|
||||||
|
return super(NetworkAgent, self).add_edge(node1=self.id, node2=other, **kwargs)
|
||||||
|
|
||||||
|
def ego_search(self, steps=1, center=False, node=None, **kwargs):
|
||||||
|
'''Get a list of nodes in the ego network of *node* of radius *steps*'''
|
||||||
|
node = as_node(node if node is not None else self)
|
||||||
|
G = self.subgraph(**kwargs)
|
||||||
|
return nx.ego_graph(G, node, center=center, radius=steps).nodes()
|
||||||
|
|
||||||
|
def degree(self, node, force=False):
|
||||||
|
node = as_node(node)
|
||||||
|
if force or (not hasattr(self.env, '_degree')) or getattr(self.env, '_last_step', 0) < self.now:
|
||||||
|
self.env._degree = nx.degree_centrality(self.global_topology)
|
||||||
|
self.env._last_step = self.now
|
||||||
|
return self.env._degree[node]
|
||||||
|
|
||||||
|
def betweenness(self, node, force=False):
|
||||||
|
node = as_node(node)
|
||||||
|
if force or (not hasattr(self.env, '_betweenness')) or getattr(self.env, '_last_step', 0) < self.now:
|
||||||
|
self.env._betweenness = nx.betweenness_centrality(self.global_topology)
|
||||||
|
self.env._last_step = self.now
|
||||||
|
return self.env._betweenness[node]
|
||||||
|
|
||||||
|
|
||||||
|
def state(name=None):
|
||||||
|
def decorator(func, name=None):
|
||||||
|
'''
|
||||||
|
A state function should return either a state id, or a tuple (state_id, when)
|
||||||
|
The default value for state_id is the current state id.
|
||||||
|
The default value for when is the interval defined in the environment.
|
||||||
|
'''
|
||||||
|
|
||||||
|
@wraps(func)
|
||||||
|
def func_wrapper(self):
|
||||||
|
next_state = func(self)
|
||||||
|
when = None
|
||||||
|
if next_state is None:
|
||||||
|
return when
|
||||||
|
try:
|
||||||
|
next_state, when = next_state
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
pass
|
||||||
|
if next_state:
|
||||||
|
self.set_state(next_state)
|
||||||
return when
|
return when
|
||||||
try:
|
|
||||||
next_state, when = next_state
|
|
||||||
except (ValueError, TypeError):
|
|
||||||
pass
|
|
||||||
if next_state:
|
|
||||||
self.set_state(next_state)
|
|
||||||
return when
|
|
||||||
|
|
||||||
func_wrapper.id = func.__name__
|
func_wrapper.id = name or func.__name__
|
||||||
func_wrapper.is_default = False
|
func_wrapper.is_default = False
|
||||||
return func_wrapper
|
return func_wrapper
|
||||||
|
|
||||||
|
if callable(name):
|
||||||
|
return decorator(name)
|
||||||
|
else:
|
||||||
|
return partial(decorator, name=name)
|
||||||
|
|
||||||
|
|
||||||
def default_state(func):
|
def default_state(func):
|
||||||
@ -340,7 +369,7 @@ def calculate_distribution(network_agents=None,
|
|||||||
elif agent_type:
|
elif agent_type:
|
||||||
network_agents = [{'agent_type': agent_type}]
|
network_agents = [{'agent_type': agent_type}]
|
||||||
else:
|
else:
|
||||||
return []
|
raise ValueError('Specify a distribution or a default agent type')
|
||||||
|
|
||||||
# Calculate the thresholds
|
# Calculate the thresholds
|
||||||
total = sum(x.get('weight', 1) for x in network_agents)
|
total = sum(x.get('weight', 1) for x in network_agents)
|
||||||
@ -427,6 +456,58 @@ def _agent_from_distribution(distribution, value=-1, agent_id=None):
|
|||||||
raise Exception('Distribution for value {} not found in: {}'.format(value, distribution))
|
raise Exception('Distribution for value {} not found in: {}'.format(value, distribution))
|
||||||
|
|
||||||
|
|
||||||
|
class Geo(NetworkAgent):
|
||||||
|
'''In this type of network, nodes have a "pos" attribute.'''
|
||||||
|
|
||||||
|
def geo_search(self, radius, node=None, center=False, **kwargs):
|
||||||
|
'''Get a list of nodes whose coordinates are closer than *radius* to *node*.'''
|
||||||
|
node = as_node(node if node is not None else self)
|
||||||
|
|
||||||
|
G = self.subgraph(**kwargs)
|
||||||
|
|
||||||
|
pos = nx.get_node_attributes(G, 'pos')
|
||||||
|
if not pos:
|
||||||
|
return []
|
||||||
|
nodes, coords = list(zip(*pos.items()))
|
||||||
|
kdtree = KDTree(coords) # Cannot provide generator.
|
||||||
|
indices = kdtree.query_ball_point(pos[node], radius)
|
||||||
|
return [nodes[i] for i in indices if center or (nodes[i] != node)]
|
||||||
|
|
||||||
|
|
||||||
|
def select(agents, state_id=None, agent_type=None, ignore=None, iterator=False, **kwargs):
|
||||||
|
|
||||||
|
if state_id is not None:
|
||||||
|
try:
|
||||||
|
state_id = tuple(state_id)
|
||||||
|
except TypeError:
|
||||||
|
state_id = tuple([state_id])
|
||||||
|
if agent_type is not None:
|
||||||
|
try:
|
||||||
|
agent_type = tuple(agent_type)
|
||||||
|
except TypeError:
|
||||||
|
agent_type = tuple([agent_type])
|
||||||
|
|
||||||
|
def matches_all(agent):
|
||||||
|
if state_id is not None:
|
||||||
|
if agent.state.get('id', None) not in state_id:
|
||||||
|
return False
|
||||||
|
if agent_type is not None:
|
||||||
|
if not isinstance(agent, agent_type):
|
||||||
|
return False
|
||||||
|
state = agent.state
|
||||||
|
for k, v in kwargs.items():
|
||||||
|
if state.get(k, None) != v:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
f = filter(matches_all, agents)
|
||||||
|
if ignore:
|
||||||
|
f = filter(lambda x: x not in ignore, f)
|
||||||
|
if iterator:
|
||||||
|
return f
|
||||||
|
return list(f)
|
||||||
|
|
||||||
|
|
||||||
from .BassModel import *
|
from .BassModel import *
|
||||||
from .BigMarketModel import *
|
from .BigMarketModel import *
|
||||||
from .IndependentCascadeModel import *
|
from .IndependentCascadeModel import *
|
||||||
|
@ -102,8 +102,7 @@ class Environment(nxsim.NetworkEnvironment):
|
|||||||
|
|
||||||
@network_agents.setter
|
@network_agents.setter
|
||||||
def network_agents(self, network_agents):
|
def network_agents(self, network_agents):
|
||||||
if not network_agents:
|
self._network_agents = network_agents
|
||||||
return
|
|
||||||
for ix in self.G.nodes():
|
for ix in self.G.nodes():
|
||||||
self.init_agent(ix, agent_distribution=network_agents)
|
self.init_agent(ix, agent_distribution=network_agents)
|
||||||
|
|
||||||
@ -124,6 +123,9 @@ class Environment(nxsim.NetworkEnvironment):
|
|||||||
agent_type = agents.deserialize_type(agent_type)
|
agent_type = agents.deserialize_type(agent_type)
|
||||||
elif agent_distribution:
|
elif agent_distribution:
|
||||||
agent_type, state = agents._agent_from_distribution(agent_distribution, agent_id=agent_id)
|
agent_type, state = agents._agent_from_distribution(agent_distribution, agent_id=agent_id)
|
||||||
|
else:
|
||||||
|
utils.logger.debug('Skipping node {}'.format(agent_id))
|
||||||
|
return
|
||||||
return self.set_agent(agent_id, agent_type, state)
|
return self.set_agent(agent_id, agent_type, state)
|
||||||
|
|
||||||
def set_agent(self, agent_id, agent_type, state=None):
|
def set_agent(self, agent_id, agent_type, state=None):
|
||||||
@ -149,12 +151,13 @@ class Environment(nxsim.NetworkEnvironment):
|
|||||||
a['visible'] = True
|
a['visible'] = True
|
||||||
return a
|
return a
|
||||||
|
|
||||||
def add_edge(self, agent1, agent2, attrs=None):
|
def add_edge(self, agent1, agent2, start=None, **attrs):
|
||||||
if hasattr(agent1, 'id'):
|
if hasattr(agent1, 'id'):
|
||||||
agent1 = agent1.id
|
agent1 = agent1.id
|
||||||
if hasattr(agent2, 'id'):
|
if hasattr(agent2, 'id'):
|
||||||
agent2 = agent2.id
|
agent2 = agent2.id
|
||||||
return self.G.add_edge(agent1, agent2)
|
start = start or self.now
|
||||||
|
return self.G.add_edge(agent1, agent2, **attrs)
|
||||||
|
|
||||||
def run(self, *args, **kwargs):
|
def run(self, *args, **kwargs):
|
||||||
self._save_state()
|
self._save_state()
|
||||||
@ -231,8 +234,10 @@ class Environment(nxsim.NetworkEnvironment):
|
|||||||
def get_agent(self, agent_id):
|
def get_agent(self, agent_id):
|
||||||
return self.G.node[agent_id]['agent']
|
return self.G.node[agent_id]['agent']
|
||||||
|
|
||||||
def get_agents(self):
|
def get_agents(self, nodes=None):
|
||||||
return list(self.agents)
|
if nodes is None:
|
||||||
|
return list(self.agents)
|
||||||
|
return [self.G.node[i]['agent'] for i in nodes]
|
||||||
|
|
||||||
def dump_csv(self, dir_path=None):
|
def dump_csv(self, dir_path=None):
|
||||||
csv_name = os.path.join(self.get_path(dir_path),
|
csv_name = os.path.join(self.get_path(dir_path),
|
||||||
|
@ -147,7 +147,7 @@ def deserializer(type_, known_modules=[]):
|
|||||||
module = importlib.import_module(modname)
|
module = importlib.import_module(modname)
|
||||||
cls = getattr(module, tname)
|
cls = getattr(module, tname)
|
||||||
return getattr(cls, 'deserialize', cls)
|
return getattr(cls, 'deserialize', cls)
|
||||||
except (ImportError, AttributeError) as ex:
|
except (ModuleNotFoundError, AttributeError) as ex:
|
||||||
errors.append((modname, tname, ex))
|
errors.append((modname, tname, ex))
|
||||||
raise Exception('Could not find type {}. Tried: {}'.format(type_, errors))
|
raise Exception('Could not find type {}. Tried: {}'.format(type_, errors))
|
||||||
|
|
||||||
|
@ -1,255 +0,0 @@
|
|||||||
import random
|
|
||||||
import networkx as nx
|
|
||||||
from soil.agents import BaseAgent, FSM, state, default_state
|
|
||||||
from scipy.spatial import cKDTree as KDTree
|
|
||||||
|
|
||||||
global betweenness_centrality_global
|
|
||||||
global degree_centrality_global
|
|
||||||
|
|
||||||
betweenness_centrality_global = None
|
|
||||||
degree_centrality_global = None
|
|
||||||
|
|
||||||
class TerroristSpreadModel(FSM):
|
|
||||||
"""
|
|
||||||
Settings:
|
|
||||||
information_spread_intensity
|
|
||||||
|
|
||||||
terrorist_additional_influence
|
|
||||||
|
|
||||||
min_vulnerability (optional else zero)
|
|
||||||
|
|
||||||
max_vulnerability
|
|
||||||
|
|
||||||
prob_interaction
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, environment=None, agent_id=0, state=()):
|
|
||||||
super().__init__(environment=environment, agent_id=agent_id, state=state)
|
|
||||||
|
|
||||||
global betweenness_centrality_global
|
|
||||||
global degree_centrality_global
|
|
||||||
|
|
||||||
if betweenness_centrality_global == None:
|
|
||||||
betweenness_centrality_global = nx.betweenness_centrality(self.global_topology)
|
|
||||||
if degree_centrality_global == None:
|
|
||||||
degree_centrality_global = nx.degree_centrality(self.global_topology)
|
|
||||||
|
|
||||||
self.information_spread_intensity = environment.environment_params['information_spread_intensity']
|
|
||||||
self.terrorist_additional_influence = environment.environment_params['terrorist_additional_influence']
|
|
||||||
self.prob_interaction = environment.environment_params['prob_interaction']
|
|
||||||
|
|
||||||
if self['id'] == self.civilian.id: # Civilian
|
|
||||||
self.initial_belief = random.uniform(0.00, 0.5)
|
|
||||||
elif self['id'] == self.terrorist.id: # Terrorist
|
|
||||||
self.initial_belief = random.uniform(0.8, 1.00)
|
|
||||||
elif self['id'] == self.leader.id: # Leader
|
|
||||||
self.initial_belief = 1.00
|
|
||||||
else:
|
|
||||||
raise Exception('Invalid state id: {}'.format(self['id']))
|
|
||||||
|
|
||||||
if 'min_vulnerability' in environment.environment_params:
|
|
||||||
self.vulnerability = random.uniform( environment.environment_params['min_vulnerability'], environment.environment_params['max_vulnerability'] )
|
|
||||||
else :
|
|
||||||
self.vulnerability = random.uniform( 0, environment.environment_params['max_vulnerability'] )
|
|
||||||
|
|
||||||
self.mean_belief = self.initial_belief
|
|
||||||
self.betweenness_centrality = betweenness_centrality_global[self.id]
|
|
||||||
self.degree_centrality = degree_centrality_global[self.id]
|
|
||||||
|
|
||||||
# self.state['radicalism'] = self.mean_belief
|
|
||||||
|
|
||||||
def count_neighboring_agents(self, state_id=None):
|
|
||||||
if isinstance(state_id, list):
|
|
||||||
return len(self.get_neighboring_agents(state_id))
|
|
||||||
else:
|
|
||||||
return len(super().get_agents(state_id, limit_neighbors=True))
|
|
||||||
|
|
||||||
def get_neighboring_agents(self, state_id=None):
|
|
||||||
if isinstance(state_id, list):
|
|
||||||
_list = []
|
|
||||||
for i in state_id:
|
|
||||||
_list += super().get_agents(i, limit_neighbors=True)
|
|
||||||
return [ neighbour for neighbour in _list if isinstance(neighbour, TerroristSpreadModel) ]
|
|
||||||
else:
|
|
||||||
_list = super().get_agents(state_id, limit_neighbors=True)
|
|
||||||
return [ neighbour for neighbour in _list if isinstance(neighbour, TerroristSpreadModel) ]
|
|
||||||
|
|
||||||
@state
|
|
||||||
def civilian(self):
|
|
||||||
if self.count_neighboring_agents() > 0:
|
|
||||||
neighbours = []
|
|
||||||
for neighbour in self.get_neighboring_agents():
|
|
||||||
if random.random() < self.prob_interaction:
|
|
||||||
neighbours.append(neighbour)
|
|
||||||
influence = sum( neighbour.degree_centrality for neighbour in neighbours )
|
|
||||||
mean_belief = sum( neighbour.mean_belief * neighbour.degree_centrality / influence for neighbour in neighbours )
|
|
||||||
self.initial_belief = self.mean_belief
|
|
||||||
mean_belief = mean_belief * self.information_spread_intensity + self.initial_belief * ( 1 - self.information_spread_intensity )
|
|
||||||
self.mean_belief = mean_belief * self.vulnerability + self.initial_belief * ( 1 - self.vulnerability )
|
|
||||||
|
|
||||||
if self.mean_belief >= 0.8:
|
|
||||||
return self.terrorist
|
|
||||||
|
|
||||||
@state
|
|
||||||
def leader(self):
|
|
||||||
self.mean_belief = self.mean_belief ** ( 1 - self.terrorist_additional_influence )
|
|
||||||
if self.count_neighboring_agents(state_id=[self.terrorist.id, self.leader.id]) > 0:
|
|
||||||
for neighbour in self.get_neighboring_agents(state_id=[self.terrorist.id, self.leader.id]):
|
|
||||||
if neighbour.betweenness_centrality > self.betweenness_centrality:
|
|
||||||
return self.terrorist
|
|
||||||
|
|
||||||
@state
|
|
||||||
def terrorist(self):
|
|
||||||
if self.count_neighboring_agents(state_id=[self.terrorist.id, self.leader.id]) > 0:
|
|
||||||
neighbours = self.get_neighboring_agents(state_id=[self.terrorist.id, self.leader.id])
|
|
||||||
influence = sum( neighbour.degree_centrality for neighbour in neighbours )
|
|
||||||
mean_belief = sum( neighbour.mean_belief * neighbour.degree_centrality / influence for neighbour in neighbours )
|
|
||||||
self.initial_belief = self.mean_belief
|
|
||||||
self.mean_belief = mean_belief * self.vulnerability + self.initial_belief * ( 1 - self.vulnerability )
|
|
||||||
self.mean_belief = self.mean_belief ** ( 1 - self.terrorist_additional_influence )
|
|
||||||
|
|
||||||
if self.count_neighboring_agents(state_id=self.leader.id) == 0 and self.count_neighboring_agents(state_id=self.terrorist.id) > 0:
|
|
||||||
max_betweenness_centrality = self
|
|
||||||
for neighbour in self.get_neighboring_agents(state_id=self.terrorist.id):
|
|
||||||
if neighbour.betweenness_centrality > max_betweenness_centrality.betweenness_centrality:
|
|
||||||
max_betweenness_centrality = neighbour
|
|
||||||
if max_betweenness_centrality == self:
|
|
||||||
return self.leader
|
|
||||||
|
|
||||||
def add_edge(self, G, source, target):
|
|
||||||
G.add_edge(source.id, target.id, start=self.env._now)
|
|
||||||
|
|
||||||
def link_search(self, G, node, radius):
|
|
||||||
pos = nx.get_node_attributes(G, 'pos')
|
|
||||||
nodes, coords = list(zip(*pos.items()))
|
|
||||||
kdtree = KDTree(coords) # Cannot provide generator.
|
|
||||||
edge_indexes = kdtree.query_pairs(radius, 2)
|
|
||||||
_list = [ edge[int(not edge.index(node))] for edge in edge_indexes if node in edge ]
|
|
||||||
return [ G.nodes()[index]['agent'] for index in _list ]
|
|
||||||
|
|
||||||
def social_search(self, G, node, steps):
|
|
||||||
nodes = list(nx.ego_graph(G, node, radius=steps).nodes())
|
|
||||||
nodes.remove(node)
|
|
||||||
return [ G.nodes()[index]['agent'] for index in nodes ]
|
|
||||||
|
|
||||||
|
|
||||||
class TrainingAreaModel(FSM):
|
|
||||||
"""
|
|
||||||
Settings:
|
|
||||||
training_influence
|
|
||||||
|
|
||||||
min_vulnerability
|
|
||||||
|
|
||||||
Requires TerroristSpreadModel.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, environment=None, agent_id=0, state=()):
|
|
||||||
super().__init__(environment=environment, agent_id=agent_id, state=state)
|
|
||||||
self.training_influence = environment.environment_params['training_influence']
|
|
||||||
if 'min_vulnerability' in environment.environment_params:
|
|
||||||
self.min_vulnerability = environment.environment_params['min_vulnerability']
|
|
||||||
else: self.min_vulnerability = 0
|
|
||||||
|
|
||||||
@default_state
|
|
||||||
@state
|
|
||||||
def terrorist(self):
|
|
||||||
for neighbour in self.get_neighboring_agents():
|
|
||||||
if isinstance(neighbour, TerroristSpreadModel) and neighbour.vulnerability > self.min_vulnerability:
|
|
||||||
neighbour.vulnerability = neighbour.vulnerability ** ( 1 - self.training_influence )
|
|
||||||
|
|
||||||
|
|
||||||
class HavenModel(FSM):
|
|
||||||
"""
|
|
||||||
Settings:
|
|
||||||
haven_influence
|
|
||||||
|
|
||||||
min_vulnerability
|
|
||||||
|
|
||||||
max_vulnerability
|
|
||||||
|
|
||||||
Requires TerroristSpreadModel.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, environment=None, agent_id=0, state=()):
|
|
||||||
super().__init__(environment=environment, agent_id=agent_id, state=state)
|
|
||||||
self.haven_influence = environment.environment_params['haven_influence']
|
|
||||||
if 'min_vulnerability' in environment.environment_params:
|
|
||||||
self.min_vulnerability = environment.environment_params['min_vulnerability']
|
|
||||||
else: self.min_vulnerability = 0
|
|
||||||
self.max_vulnerability = environment.environment_params['max_vulnerability']
|
|
||||||
|
|
||||||
@state
|
|
||||||
def civilian(self):
|
|
||||||
for neighbour_agent in self.get_neighboring_agents():
|
|
||||||
if isinstance(neighbour_agent, TerroristSpreadModel) and neighbour_agent['id'] == neighbour_agent.civilian.id:
|
|
||||||
for neighbour in self.get_neighboring_agents():
|
|
||||||
if isinstance(neighbour, TerroristSpreadModel) and neighbour.vulnerability > self.min_vulnerability:
|
|
||||||
neighbour.vulnerability = neighbour.vulnerability * ( 1 - self.haven_influence )
|
|
||||||
return self.civilian
|
|
||||||
return self.terrorist
|
|
||||||
|
|
||||||
@state
|
|
||||||
def terrorist(self):
|
|
||||||
for neighbour in self.get_neighboring_agents():
|
|
||||||
if isinstance(neighbour, TerroristSpreadModel) and neighbour.vulnerability < self.max_vulnerability:
|
|
||||||
neighbour.vulnerability = neighbour.vulnerability ** ( 1 - self.haven_influence )
|
|
||||||
return self.terrorist
|
|
||||||
|
|
||||||
|
|
||||||
class TerroristNetworkModel(TerroristSpreadModel):
|
|
||||||
"""
|
|
||||||
Settings:
|
|
||||||
sphere_influence
|
|
||||||
|
|
||||||
vision_range
|
|
||||||
|
|
||||||
weight_social_distance
|
|
||||||
|
|
||||||
weight_link_distance
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, environment=None, agent_id=0, state=()):
|
|
||||||
super().__init__(environment=environment, agent_id=agent_id, state=state)
|
|
||||||
|
|
||||||
self.vision_range = environment.environment_params['vision_range']
|
|
||||||
self.sphere_influence = environment.environment_params['sphere_influence']
|
|
||||||
self.weight_social_distance = environment.environment_params['weight_social_distance']
|
|
||||||
self.weight_link_distance = environment.environment_params['weight_link_distance']
|
|
||||||
|
|
||||||
@state
|
|
||||||
def terrorist(self):
|
|
||||||
self.update_relationships()
|
|
||||||
return super().terrorist()
|
|
||||||
|
|
||||||
@state
|
|
||||||
def leader(self):
|
|
||||||
self.update_relationships()
|
|
||||||
return super().leader()
|
|
||||||
|
|
||||||
def update_relationships(self):
|
|
||||||
if self.count_neighboring_agents(state_id=self.civilian.id) == 0:
|
|
||||||
close_ups = self.link_search(self.global_topology, self.id, self.vision_range)
|
|
||||||
step_neighbours = self.social_search(self.global_topology, self.id, self.sphere_influence)
|
|
||||||
search = list(set(close_ups).union(step_neighbours))
|
|
||||||
neighbours = self.get_neighboring_agents()
|
|
||||||
search = [item for item in search if not item in neighbours and isinstance(item, TerroristNetworkModel)]
|
|
||||||
for agent in search:
|
|
||||||
social_distance = 1 / self.shortest_path_length(self.global_topology, self.id, agent.id)
|
|
||||||
spatial_proximity = ( 1 - self.get_distance(self.global_topology, self.id, agent.id) )
|
|
||||||
prob_new_interaction = self.weight_social_distance * social_distance + self.weight_link_distance * spatial_proximity
|
|
||||||
if agent['id'] == agent.civilian.id and random.random() < prob_new_interaction:
|
|
||||||
self.add_edge(self.global_topology, self, agent)
|
|
||||||
break
|
|
||||||
|
|
||||||
def get_distance(self, G, source, target):
|
|
||||||
source_x, source_y = nx.get_node_attributes(G, 'pos')[source]
|
|
||||||
target_x, target_y = nx.get_node_attributes(G, 'pos')[target]
|
|
||||||
dx = abs( source_x - target_x )
|
|
||||||
dy = abs( source_y - target_y )
|
|
||||||
return ( dx ** 2 + dy ** 2 ) ** ( 1 / 2 )
|
|
||||||
|
|
||||||
def shortest_path_length(self, G, source, target):
|
|
||||||
try:
|
|
||||||
return nx.shortest_path_length(G, source, target)
|
|
||||||
except nx.NetworkXNoPath:
|
|
||||||
return float('inf')
|
|
@ -7,6 +7,8 @@ from soil import utils, simulation
|
|||||||
ROOT = os.path.abspath(os.path.dirname(__file__))
|
ROOT = os.path.abspath(os.path.dirname(__file__))
|
||||||
EXAMPLES = join(ROOT, '..', 'examples')
|
EXAMPLES = join(ROOT, '..', 'examples')
|
||||||
|
|
||||||
|
FORCE_TESTS = os.environ.get('FORCE_TESTS', '')
|
||||||
|
|
||||||
|
|
||||||
class TestExamples(TestCase):
|
class TestExamples(TestCase):
|
||||||
pass
|
pass
|
||||||
@ -19,7 +21,10 @@ def make_example_test(path, config):
|
|||||||
s = simulation.from_config(config)
|
s = simulation.from_config(config)
|
||||||
iterations = s.max_time * s.num_trials
|
iterations = s.max_time * s.num_trials
|
||||||
if iterations > 1000:
|
if iterations > 1000:
|
||||||
self.skipTest('This example would probably take too long')
|
s.max_time = 100
|
||||||
|
s.num_trials = 1
|
||||||
|
if config.get('skip_test', False) and not FORCE_TESTS:
|
||||||
|
self.skipTest('Example ignored.')
|
||||||
envs = s.run_simulation(dry_run=True)
|
envs = s.run_simulation(dry_run=True)
|
||||||
assert envs
|
assert envs
|
||||||
for env in envs:
|
for env in envs:
|
||||||
|
@ -320,3 +320,19 @@ class TestMain(TestCase):
|
|||||||
h = history.History()
|
h = history.History()
|
||||||
h.save_record(agent_id=0, t_step=0, key="test", value="hello")
|
h.save_record(agent_id=0, t_step=0, key="test", value="hello")
|
||||||
assert h[0, 0, "test"] == "hello"
|
assert h[0, 0, "test"] == "hello"
|
||||||
|
|
||||||
|
def test_subgraph(self):
|
||||||
|
'''An agent should be able to subgraph the global topology'''
|
||||||
|
G = nx.Graph()
|
||||||
|
G.add_node(3)
|
||||||
|
G.add_edge(1, 2)
|
||||||
|
distro = agents.calculate_distribution(agent_type=agents.NetworkAgent)
|
||||||
|
env = Environment(name='Test', topology=G, network_agents=distro)
|
||||||
|
lst = list(env.network_agents)
|
||||||
|
|
||||||
|
a2 = env.get_agent(2)
|
||||||
|
a3 = env.get_agent(3)
|
||||||
|
assert len(a2.subgraph(limit_neighbors=True)) == 2
|
||||||
|
assert len(a3.subgraph(limit_neighbors=True)) == 1
|
||||||
|
assert len(a3.subgraph(limit_neighbors=True, center=False)) == 0
|
||||||
|
assert len(a3.subgraph(agent_type=agents.NetworkAgent)) == 3
|
||||||
|
Loading…
Reference in New Issue
Block a user