1
0
mirror of https://github.com/gsi-upm/soil synced 2025-11-29 03:18:16 +00:00

Compare commits

...

5 Commits

Author SHA1 Message Date
J. Fernando Sánchez
bf481f0f88 v0.20.8 fix bugs 2023-03-23 14:49:09 +01:00
J. Fernando Sánchez
a40aa55b6a Release 0.20.7 2022-07-06 09:23:46 +02:00
J. Fernando Sánchez
50cba751a6 Release 0.20.6 2022-07-05 12:08:34 +02:00
J. Fernando Sánchez
dfb6d13649 version 0.20.5 2022-05-18 16:13:53 +02:00
J. Fernando Sánchez
5559d37e57 version 0.20.4 2022-05-18 15:20:58 +02:00
71 changed files with 5484 additions and 85862 deletions

View File

@@ -1,5 +1,7 @@
**/soil_output **/soil_output
.* .*
**/.*
**/__pycache__ **/__pycache__
__pycache__ __pycache__
*.pyc *.pyc
**/backup

1
.gitignore vendored
View File

@@ -9,3 +9,4 @@ docs/_build*
build/* build/*
dist/* dist/*
prof prof
backup

View File

@@ -3,6 +3,34 @@ 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.20.8]
### Changed
* Tsih bumped to version 0.1.8
### Fixed
* Mentions to `id` in docs. It should be `state_id` now.
* Fixed bug: environment agents were not being added to the simulation
## [0.20.7]
### Changed
* Creating a `time.When` from another `time.When` does not nest them anymore (it returns the argument)
### Fixed
* Bug with time.NEVER/time.INFINITY
## [0.20.6]
### Fixed
* Agents now return `time.INFINITY` when dead, instead of 'inf'
* `soil.__init__` does not re-export built-in time (change in `soil.simulation`. It used to create subtle import conflicts when importing soil.time.
* Parallel simulations were broken because lambdas cannot be pickled properly, which is needed for multiprocessing.
### Changed
* Some internal simulation methods do not accept `*args` anymore, to avoid ambiguity and bugs.
## [0.20.5]
### Changed
* Defaults are now set in the agent __init__, not in the environment. This decouples both classes a bit more, and it is more intuitive
## [0.20.4]
### Added
* Agents can now be given any kwargs, which will be used to set their state
* Environments have a default logger `self.logger` and a log method, just like agents
## [0.20.3] ## [0.20.3]
### Fixed ### Fixed
* Default state values are now deepcopied again. * Default state values are now deepcopied again.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

BIN
docs/output_58_0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
docs/output_58_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
docs/output_58_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
docs/output_58_3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
docs/output_58_4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
docs/output_60_0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
docs/output_60_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
docs/output_60_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
docs/output_60_3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
docs/output_60_4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

BIN
docs/output_62_0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
docs/output_62_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
docs/output_62_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
docs/output_62_3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
docs/output_62_4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

BIN
docs/output_68_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 952 KiB

BIN
docs/output_70_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

BIN
docs/output_77_0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
docs/output_81_0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
docs/output_82_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
docs/output_83_0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -2,13 +2,12 @@
"cells": [ "cells": [
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 4, "execution_count": 1,
"metadata": { "metadata": {
"ExecuteTime": { "ExecuteTime": {
"end_time": "2017-11-08T16:22:30.732107Z", "end_time": "2017-11-08T16:22:30.732107Z",
"start_time": "2017-11-08T17:22:30.059855+01:00" "start_time": "2017-11-08T17:22:30.059855+01:00"
}, }
"collapsed": true
}, },
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -28,24 +27,16 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 5, "execution_count": 2,
"metadata": { "metadata": {
"ExecuteTime": { "ExecuteTime": {
"end_time": "2017-11-08T16:22:35.580593Z", "end_time": "2017-11-08T16:22:35.580593Z",
"start_time": "2017-11-08T17:22:35.542745+01:00" "start_time": "2017-11-08T17:22:35.542745+01:00"
} }
}, },
"outputs": [ "outputs": [],
{
"name": "stdout",
"output_type": "stream",
"text": [
"Populating the interactive namespace from numpy and matplotlib\n"
]
}
],
"source": [ "source": [
"%pylab inline\n", "%matplotlib inline\n",
"\n", "\n",
"from soil import *" "from soil import *"
] ]
@@ -66,7 +57,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 6, "execution_count": 3,
"metadata": { "metadata": {
"ExecuteTime": { "ExecuteTime": {
"end_time": "2017-11-08T16:22:37.242327Z", "end_time": "2017-11-08T16:22:37.242327Z",
@@ -86,7 +77,7 @@
" prob_neighbor_spread: 0.0\r\n", " prob_neighbor_spread: 0.0\r\n",
" prob_tv_spread: 0.01\r\n", " prob_tv_spread: 0.01\r\n",
"interval: 1\r\n", "interval: 1\r\n",
"max_time: 30\r\n", "max_time: 300\r\n",
"name: Sim_all_dumb\r\n", "name: Sim_all_dumb\r\n",
"network_agents:\r\n", "network_agents:\r\n",
"- agent_type: DumbViewer\r\n", "- agent_type: DumbViewer\r\n",
@@ -110,7 +101,7 @@
" prob_neighbor_spread: 0.0\r\n", " prob_neighbor_spread: 0.0\r\n",
" prob_tv_spread: 0.01\r\n", " prob_tv_spread: 0.01\r\n",
"interval: 1\r\n", "interval: 1\r\n",
"max_time: 30\r\n", "max_time: 300\r\n",
"name: Sim_half_herd\r\n", "name: Sim_half_herd\r\n",
"network_agents:\r\n", "network_agents:\r\n",
"- agent_type: DumbViewer\r\n", "- agent_type: DumbViewer\r\n",
@@ -142,18 +133,18 @@
" prob_neighbor_spread: 0.0\r\n", " prob_neighbor_spread: 0.0\r\n",
" prob_tv_spread: 0.01\r\n", " prob_tv_spread: 0.01\r\n",
"interval: 1\r\n", "interval: 1\r\n",
"max_time: 30\r\n", "max_time: 300\r\n",
"name: Sim_all_herd\r\n", "name: Sim_all_herd\r\n",
"network_agents:\r\n", "network_agents:\r\n",
"- agent_type: HerdViewer\r\n", "- agent_type: HerdViewer\r\n",
" state:\r\n", " state:\r\n",
" has_tv: true\r\n", " has_tv: true\r\n",
" id: neutral\r\n", " state_id: neutral\r\n",
" weight: 1\r\n", " weight: 1\r\n",
"- agent_type: HerdViewer\r\n", "- agent_type: HerdViewer\r\n",
" state:\r\n", " state:\r\n",
" has_tv: true\r\n", " has_tv: true\r\n",
" id: neutral\r\n", " state_id: neutral\r\n",
" weight: 1\r\n", " weight: 1\r\n",
"network_params:\r\n", "network_params:\r\n",
" generator: barabasi_albert_graph\r\n", " generator: barabasi_albert_graph\r\n",
@@ -169,13 +160,13 @@
" prob_tv_spread: 0.01\r\n", " prob_tv_spread: 0.01\r\n",
" prob_neighbor_cure: 0.1\r\n", " prob_neighbor_cure: 0.1\r\n",
"interval: 1\r\n", "interval: 1\r\n",
"max_time: 30\r\n", "max_time: 300\r\n",
"name: Sim_wise_herd\r\n", "name: Sim_wise_herd\r\n",
"network_agents:\r\n", "network_agents:\r\n",
"- agent_type: HerdViewer\r\n", "- agent_type: HerdViewer\r\n",
" state:\r\n", " state:\r\n",
" has_tv: true\r\n", " has_tv: true\r\n",
" id: neutral\r\n", " state_id: neutral\r\n",
" weight: 1\r\n", " weight: 1\r\n",
"- agent_type: WiseViewer\r\n", "- agent_type: WiseViewer\r\n",
" state:\r\n", " state:\r\n",
@@ -195,13 +186,13 @@
" prob_tv_spread: 0.01\r\n", " prob_tv_spread: 0.01\r\n",
" prob_neighbor_cure: 0.1\r\n", " prob_neighbor_cure: 0.1\r\n",
"interval: 1\r\n", "interval: 1\r\n",
"max_time: 30\r\n", "max_time: 300\r\n",
"name: Sim_all_wise\r\n", "name: Sim_all_wise\r\n",
"network_agents:\r\n", "network_agents:\r\n",
"- agent_type: WiseViewer\r\n", "- agent_type: WiseViewer\r\n",
" state:\r\n", " state:\r\n",
" has_tv: true\r\n", " has_tv: true\r\n",
" id: neutral\r\n", " state_id: neutral\r\n",
" weight: 1\r\n", " weight: 1\r\n",
"- agent_type: WiseViewer\r\n", "- agent_type: WiseViewer\r\n",
" state:\r\n", " state:\r\n",
@@ -225,7 +216,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 22, "execution_count": 4,
"metadata": { "metadata": {
"ExecuteTime": { "ExecuteTime": {
"end_time": "2017-11-08T18:07:46.781745Z", "end_time": "2017-11-08T18:07:46.781745Z",
@@ -233,7 +224,24 @@
}, },
"scrolled": true "scrolled": true
}, },
"outputs": [], "outputs": [
{
"ename": "ValueError",
"evalue": "No objects to concatenate",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)",
"Cell \u001b[0;32mIn[4], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m evodumb \u001b[38;5;241m=\u001b[39m \u001b[43manalysis\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mread_data\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43msoil_output/Sim_all_dumb/\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mprocess\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43manalysis\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget_count\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mgroup\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkeys\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mid\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m;\n",
"File \u001b[0;32m/mnt/data/home/j/git/lab.gsi/soil/soil/soil/analysis.py:14\u001b[0m, in \u001b[0;36mread_data\u001b[0;34m(group, *args, **kwargs)\u001b[0m\n\u001b[1;32m 12\u001b[0m iterable \u001b[38;5;241m=\u001b[39m _read_data(\u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 13\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m group:\n\u001b[0;32m---> 14\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mgroup_trials\u001b[49m\u001b[43m(\u001b[49m\u001b[43miterable\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 15\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 16\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mlist\u001b[39m(iterable)\n",
"File \u001b[0;32m/mnt/data/home/j/git/lab.gsi/soil/soil/soil/analysis.py:201\u001b[0m, in \u001b[0;36mgroup_trials\u001b[0;34m(trials, aggfunc)\u001b[0m\n\u001b[1;32m 199\u001b[0m trials \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mlist\u001b[39m(trials)\n\u001b[1;32m 200\u001b[0m trials \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mlist\u001b[39m(\u001b[38;5;28mmap\u001b[39m(\u001b[38;5;28;01mlambda\u001b[39;00m x: x[\u001b[38;5;241m1\u001b[39m] \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(x, \u001b[38;5;28mtuple\u001b[39m) \u001b[38;5;28;01melse\u001b[39;00m x, trials))\n\u001b[0;32m--> 201\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mpd\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mconcat\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtrials\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241m.\u001b[39mgroupby(level\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m0\u001b[39m)\u001b[38;5;241m.\u001b[39magg(aggfunc)\u001b[38;5;241m.\u001b[39mreorder_levels([\u001b[38;5;241m2\u001b[39m, \u001b[38;5;241m0\u001b[39m,\u001b[38;5;241m1\u001b[39m] ,axis\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m1\u001b[39m)\n",
"File \u001b[0;32m/mnt/data/home/j/git/lab.gsi/soil/soil/.env-v0.20/lib/python3.8/site-packages/pandas/util/_decorators.py:331\u001b[0m, in \u001b[0;36mdeprecate_nonkeyword_arguments.<locals>.decorate.<locals>.wrapper\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 325\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(args) \u001b[38;5;241m>\u001b[39m num_allow_args:\n\u001b[1;32m 326\u001b[0m warnings\u001b[38;5;241m.\u001b[39mwarn(\n\u001b[1;32m 327\u001b[0m msg\u001b[38;5;241m.\u001b[39mformat(arguments\u001b[38;5;241m=\u001b[39m_format_argument_list(allow_args)),\n\u001b[1;32m 328\u001b[0m \u001b[38;5;167;01mFutureWarning\u001b[39;00m,\n\u001b[1;32m 329\u001b[0m stacklevel\u001b[38;5;241m=\u001b[39mfind_stack_level(),\n\u001b[1;32m 330\u001b[0m )\n\u001b[0;32m--> 331\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n",
"File \u001b[0;32m/mnt/data/home/j/git/lab.gsi/soil/soil/.env-v0.20/lib/python3.8/site-packages/pandas/core/reshape/concat.py:368\u001b[0m, in \u001b[0;36mconcat\u001b[0;34m(objs, axis, join, ignore_index, keys, levels, names, verify_integrity, sort, copy)\u001b[0m\n\u001b[1;32m 146\u001b[0m \u001b[38;5;129m@deprecate_nonkeyword_arguments\u001b[39m(version\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, allowed_args\u001b[38;5;241m=\u001b[39m[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mobjs\u001b[39m\u001b[38;5;124m\"\u001b[39m])\n\u001b[1;32m 147\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mconcat\u001b[39m(\n\u001b[1;32m 148\u001b[0m objs: Iterable[NDFrame] \u001b[38;5;241m|\u001b[39m Mapping[HashableT, NDFrame],\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 157\u001b[0m copy: \u001b[38;5;28mbool\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mTrue\u001b[39;00m,\n\u001b[1;32m 158\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m DataFrame \u001b[38;5;241m|\u001b[39m Series:\n\u001b[1;32m 159\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 160\u001b[0m \u001b[38;5;124;03m Concatenate pandas objects along a particular axis.\u001b[39;00m\n\u001b[1;32m 161\u001b[0m \n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 366\u001b[0m \u001b[38;5;124;03m 1 3 4\u001b[39;00m\n\u001b[1;32m 367\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m--> 368\u001b[0m op \u001b[38;5;241m=\u001b[39m \u001b[43m_Concatenator\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 369\u001b[0m \u001b[43m \u001b[49m\u001b[43mobjs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 370\u001b[0m \u001b[43m \u001b[49m\u001b[43maxis\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43maxis\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 371\u001b[0m \u001b[43m \u001b[49m\u001b[43mignore_index\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mignore_index\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 372\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 373\u001b[0m \u001b[43m \u001b[49m\u001b[43mkeys\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mkeys\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 374\u001b[0m \u001b[43m \u001b[49m\u001b[43mlevels\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mlevels\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 375\u001b[0m \u001b[43m \u001b[49m\u001b[43mnames\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mnames\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 376\u001b[0m \u001b[43m \u001b[49m\u001b[43mverify_integrity\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mverify_integrity\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 377\u001b[0m \u001b[43m \u001b[49m\u001b[43mcopy\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcopy\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 378\u001b[0m \u001b[43m \u001b[49m\u001b[43msort\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43msort\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 379\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 381\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m op\u001b[38;5;241m.\u001b[39mget_result()\n",
"File \u001b[0;32m/mnt/data/home/j/git/lab.gsi/soil/soil/.env-v0.20/lib/python3.8/site-packages/pandas/core/reshape/concat.py:425\u001b[0m, in \u001b[0;36m_Concatenator.__init__\u001b[0;34m(self, objs, axis, join, keys, levels, names, ignore_index, verify_integrity, copy, sort)\u001b[0m\n\u001b[1;32m 422\u001b[0m objs \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mlist\u001b[39m(objs)\n\u001b[1;32m 424\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(objs) \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[0;32m--> 425\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mNo objects to concatenate\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 427\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m keys \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 428\u001b[0m objs \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mlist\u001b[39m(com\u001b[38;5;241m.\u001b[39mnot_none(\u001b[38;5;241m*\u001b[39mobjs))\n",
"\u001b[0;31mValueError\u001b[0m: No objects to concatenate"
]
}
],
"source": [ "source": [
"evodumb = analysis.read_data('soil_output/Sim_all_dumb/', process=analysis.get_count, group=True, keys=['id']);" "evodumb = analysis.read_data('soil_output/Sim_all_dumb/', process=analysis.get_count, group=True, keys=['id']);"
] ]
@@ -721,9 +729,9 @@
], ],
"metadata": { "metadata": {
"kernelspec": { "kernelspec": {
"display_name": "Python 3", "display_name": "venv-soil",
"language": "python", "language": "python",
"name": "python3" "name": "venv-soil"
}, },
"language_info": { "language_info": {
"codemirror_mode": { "codemirror_mode": {
@@ -735,7 +743,7 @@
"name": "python", "name": "python",
"nbconvert_exporter": "python", "nbconvert_exporter": "python",
"pygments_lexer": "ipython3", "pygments_lexer": "ipython3",
"version": "3.6.2" "version": "3.8.10"
}, },
"toc": { "toc": {
"colors": { "colors": {

View File

@@ -1,7 +1,7 @@
--- ---
load_module: rabbit_agents load_module: rabbit_agents
name: rabbits_example name: rabbits_example
max_time: 100 max_time: 1000
interval: 1 interval: 1
seed: MySeed seed: MySeed
agent_type: rabbit_agents.RabbitModel agent_type: rabbit_agents.RabbitModel

File diff suppressed because one or more lines are too long

View File

@@ -6,4 +6,4 @@ pandas>=0.23
SALib>=1.3 SALib>=1.3
Jinja2 Jinja2
Mesa>=0.8 Mesa>=0.8
tsih>=0.1.5 tsih>=0.1.9

View File

@@ -1 +1 @@
0.20.3 0.20.8

View File

@@ -35,7 +35,9 @@ class BaseAgent(Agent):
unique_id, unique_id,
model, model,
name=None, name=None,
interval=None): interval=None,
**kwargs
):
# Check for REQUIRED arguments # Check for REQUIRED arguments
# Initialize agent parameters # Initialize agent parameters
if isinstance(unique_id, Agent): if isinstance(unique_id, Agent):
@@ -52,6 +54,12 @@ class BaseAgent(Agent):
if hasattr(self, 'level'): if hasattr(self, 'level'):
self.logger.setLevel(self.level) self.logger.setLevel(self.level)
for (k, v) in self.defaults.items():
if not hasattr(self, k) or getattr(self, k) is None:
setattr(self, k, deepcopy(v))
for (k, v) in kwargs.items():
setattr(self, k, v)
# TODO: refactor to clean up mesa compatibility # TODO: refactor to clean up mesa compatibility
@@ -137,6 +145,7 @@ class BaseAgent(Agent):
self.alive = False self.alive = False
if remove: if remove:
self.remove_node(self.id) self.remove_node(self.id)
return time.NEVER
def step(self): def step(self):
if not self.alive: if not self.alive:
@@ -272,7 +281,7 @@ def default_state(func):
class MetaFSM(type): class MetaFSM(type):
def __init__(cls, name, bases, nmspc): def __init__(cls, name, bases, nmspc):
super(MetaFSM, cls).__init__(name, bases, nmspc) super().__init__(name, bases, nmspc)
states = {} states = {}
# Re-use states from inherited classes # Re-use states from inherited classes
default_state = None default_state = None
@@ -305,18 +314,16 @@ class FSM(NetworkAgent, metaclass=MetaFSM):
def step(self): def step(self):
self.debug(f'Agent {self.unique_id} @ state {self.state_id}') self.debug(f'Agent {self.unique_id} @ state {self.state_id}')
try: interval = super().step()
interval = super().step()
except DeadAgent:
return time.When('inf')
if 'id' not in self.state: if 'id' not in self.state:
# if 'id' in self.state:
# self.set_state(self.state['id'])
if self.default_state: if self.default_state:
self.set_state(self.default_state.id) self.set_state(self.default_state.id)
else: else:
raise Exception('{} has no valid state id or default state'.format(self)) raise Exception('{} has no valid state id or default state'.format(self))
return self.states[self.state_id](self) or interval interval = self.states[self.state_id](self) or interval
if not self.alive:
return time.NEVER
return interval
def set_state(self, state): def set_state(self, state):
if hasattr(state, 'id'): if hasattr(state, 'id'):
@@ -475,6 +482,7 @@ def _definition_to_dict(definition, size=None, default_state=None):
distro = sorted([item for item in definition if 'weight' in item]) distro = sorted([item for item in definition if 'weight' in item])
ix = 0 ix = 0
def init_agent(item, id=ix): def init_agent(item, id=ix):
while id in agents: while id in agents:
id += 1 id += 1

View File

@@ -112,7 +112,7 @@ def get_types(df):
Get the value type for every key stored in a raw history dataframe. Get the value type for every key stored in a raw history dataframe.
''' '''
dtypes = df.groupby(by=['key'])['value_type'].unique() dtypes = df.groupby(by=['key'])['value_type'].unique()
return {k:v[0] for k,v in dtypes.iteritems()} return {k:v[0] for k,v in dtypes.items()}
def process_one(df, *keys, columns=['key', 'agent_id'], values='value', def process_one(df, *keys, columns=['key', 'agent_id'], values='value',
@@ -146,7 +146,7 @@ def get_count(df, *keys):
counts = pd.DataFrame() counts = pd.DataFrame()
for key in df.columns.levels[0]: for key in df.columns.levels[0]:
g = df[[key]].apply(pd.Series.value_counts, axis=1).fillna(0) g = df[[key]].apply(pd.Series.value_counts, axis=1).fillna(0)
for value, series in g.iteritems(): for value, series in g.items():
counts[key, value] = series counts[key, value] = series
counts.columns = pd.MultiIndex.from_tuples(counts.columns) counts.columns = pd.MultiIndex.from_tuples(counts.columns)
return counts return counts

View File

@@ -5,6 +5,7 @@ import math
import random import random
import yaml import yaml
import tempfile import tempfile
import logging
import pandas as pd import pandas as pd
from time import time as current_time from time import time as current_time
from copy import deepcopy from copy import deepcopy
@@ -101,6 +102,8 @@ class Environment(Model):
environment_agents = agents._convert_agent_types(distro) environment_agents = agents._convert_agent_types(distro)
self.environment_agents = environment_agents self.environment_agents = environment_agents
self.logger = utils.logger.getChild(self.name)
@property @property
def now(self): def now(self):
if self.schedule: if self.schedule:
@@ -121,7 +124,8 @@ class Environment(Model):
def environment_agents(self, environment_agents): def environment_agents(self, environment_agents):
self._environment_agents = environment_agents self._environment_agents = environment_agents
self._env_agents = agents._definition_to_dict(definition=environment_agents) for (ix, agent) in enumerate(self._environment_agents):
self.init_agent(len(self.G) + ix, agent_definitions=environment_agents, with_node=False)
@property @property
def network_agents(self): def network_agents(self):
@@ -136,15 +140,19 @@ class Environment(Model):
for ix in self.G.nodes(): for ix in self.G.nodes():
self.init_agent(ix, agent_definitions=network_agents) self.init_agent(ix, agent_definitions=network_agents)
def init_agent(self, agent_id, agent_definitions): def init_agent(self, agent_id, agent_definitions, with_node=True):
node = self.G.nodes[agent_id]
init = False init = False
state = dict(node)
state = {}
if with_node:
node = self.G.nodes[agent_id]
state = dict(node)
state.update(self.states.get(agent_id, {}))
agent_type = None agent_type = None
if 'agent_type' in self.states.get(agent_id, {}): if 'agent_type' in state:
agent_type = self.states[agent_id]['agent_type'] agent_type = state['agent_type']
elif 'agent_type' in node: elif with_node and 'agent_type' in node:
agent_type = node['agent_type'] agent_type = node['agent_type']
elif 'agent_type' in self.default_state: elif 'agent_type' in self.default_state:
agent_type = self.default_state['agent_type'] agent_type = self.default_state['agent_type']
@@ -154,15 +162,16 @@ class Environment(Model):
elif agent_definitions: elif agent_definitions:
agent_type, state = agents._agent_from_definition(agent_definitions, unique_id=agent_id) agent_type, state = agents._agent_from_definition(agent_definitions, unique_id=agent_id)
else: else:
serialization.logger.debug('Skipping node {}'.format(agent_id)) serialization.logger.debug('Skipping agent {}'.format(agent_id))
return return
return self.set_agent(agent_id, agent_type, state) return self.set_agent(agent_id, agent_type, state, with_node=with_node)
def set_agent(self, agent_id, agent_type, state=None): def set_agent(self, agent_id, agent_type, state=None, with_node=True):
node = self.G.nodes[agent_id]
defstate = deepcopy(self.default_state) or {} defstate = deepcopy(self.default_state) or {}
defstate.update(self.states.get(agent_id, {})) defstate.update(self.states.get(agent_id, {}))
defstate.update(node.get('state', {})) if with_node:
node = self.G.nodes[agent_id]
defstate.update(node.get('state', {}))
if state: if state:
defstate.update(state) defstate.update(state)
a = None a = None
@@ -172,14 +181,11 @@ class Environment(Model):
unique_id=agent_id unique_id=agent_id
) )
for (k, v) in getattr(a, 'defaults', {}).items():
if not hasattr(a, k) or getattr(a, k) is None:
setattr(a, k, deepcopy(v))
for (k, v) in state.items(): for (k, v) in state.items():
setattr(a, k, v) setattr(a, k, v)
node['agent'] = a if with_node:
node['agent'] = a
self.schedule.add(a) self.schedule.add(a)
return a return a
@@ -198,6 +204,18 @@ class Environment(Model):
start = start or self.now start = start or self.now
return self.G.add_edge(agent1, agent2, **attrs) return self.G.add_edge(agent1, agent2, **attrs)
def log(self, message, *args, level=logging.INFO, **kwargs):
if not self.logger.isEnabledFor(level):
return
message = message + " ".join(str(i) for i in args)
message = " @{:>3}: {}".format(self.now, message)
for k, v in kwargs:
message += " {k}={v} ".format(k, v)
extra = {}
extra['now'] = self.now
extra['unique_id'] = self.name
return self.logger.log(level, message, extra=extra)
def step(self): def step(self):
super().step() super().step()
self.schedule.step() self.schedule.step()

View File

@@ -1,11 +1,12 @@
import os import os
import time
import importlib import importlib
import sys import sys
import yaml import yaml
import traceback import traceback
import logging import logging
import networkx as nx import networkx as nx
from time import strftime
from networkx.readwrite import json_graph from networkx.readwrite import json_graph
from multiprocessing import Pool from multiprocessing import Pool
from functools import partial from functools import partial
@@ -98,7 +99,7 @@ class Simulation:
self.network_params = network_params self.network_params = network_params
self.name = name or 'Unnamed' self.name = name or 'Unnamed'
self.seed = str(seed or name) self.seed = str(seed or name)
self._id = '{}_{}'.format(self.name, time.strftime("%Y-%m-%d_%H.%M.%S")) self._id = '{}_{}'.format(self.name, strftime("%Y-%m-%d_%H.%M.%S"))
self.group = group or '' self.group = group or ''
self.num_trials = num_trials self.num_trials = num_trials
self.max_time = max_time self.max_time = max_time
@@ -142,10 +143,10 @@ class Simulation:
'''Run the simulation and return the list of resulting environments''' '''Run the simulation and return the list of resulting environments'''
return list(self.run_gen(*args, **kwargs)) return list(self.run_gen(*args, **kwargs))
def _run_sync_or_async(self, parallel=False, *args, **kwargs): def _run_sync_or_async(self, parallel=False, **kwargs):
if parallel and not os.environ.get('SENPY_DEBUG', None): if parallel and not os.environ.get('SENPY_DEBUG', None):
p = Pool() p = Pool()
func = lambda x: self.run_trial_exceptions(trial_id=x, *args, **kwargs) func = partial(self.run_trial_exceptions, **kwargs)
for i in p.imap_unordered(func, range(self.num_trials)): for i in p.imap_unordered(func, range(self.num_trials)):
if isinstance(i, Exception): if isinstance(i, Exception):
logger.error('Trial failed:\n\t%s', i.message) logger.error('Trial failed:\n\t%s', i.message)
@@ -154,10 +155,9 @@ class Simulation:
else: else:
for i in range(self.num_trials): for i in range(self.num_trials):
yield self.run_trial(trial_id=i, yield self.run_trial(trial_id=i,
*args,
**kwargs) **kwargs)
def run_gen(self, *args, parallel=False, dry_run=False, def run_gen(self, parallel=False, dry_run=False,
exporters=[default, ], stats=[], outdir=None, exporter_params={}, exporters=[default, ], stats=[], outdir=None, exporter_params={},
stats_params={}, log_level=None, stats_params={}, log_level=None,
**kwargs): **kwargs):
@@ -183,8 +183,7 @@ class Simulation:
for exporter in exporters: for exporter in exporters:
exporter.start() exporter.start()
for env in self._run_sync_or_async(*args, for env in self._run_sync_or_async(parallel=parallel,
parallel=parallel,
log_level=log_level, log_level=log_level,
**kwargs): **kwargs):

View File

@@ -52,7 +52,7 @@ class distribution(Stats):
except TypeError: except TypeError:
pass pass
for name, count in t.value_counts().iteritems(): for name, count in t.value_counts().items():
if a not in stats['count']: if a not in stats['count']:
stats['count'][a] = {} stats['count'][a] = {}
stats['count'][a][name] = count stats['count'][a][name] = count
@@ -68,10 +68,10 @@ class distribution(Stats):
mean = {} mean = {}
if self.means: if self.means:
res = dfm.groupby(by=['key']).agg(['mean', 'std', 'count', 'median', 'max', 'min']) res = dfm.drop('metric', axis=1).groupby(by=['key']).agg(['mean', 'std', 'count', 'median', 'max', 'min'])
mean = res['value'].to_dict() mean = res['value'].to_dict()
if self.counts: if self.counts:
res = dfc.groupby(by=['key', 'value']).agg(['mean', 'std', 'count', 'median', 'max', 'min']) res = dfc.drop('metric', axis=1).groupby(by=['key', 'value']).agg(['mean', 'std', 'count', 'median', 'max', 'min'])
for k,v in res['count'].to_dict().items(): for k,v in res['count'].to_dict().items():
if k not in count: if k not in count:
count[k] = {} count[k] = {}

View File

@@ -10,13 +10,17 @@ INFINITY = float('inf')
class When: class When:
def __init__(self, time): def __init__(self, time):
if isinstance(time, When):
return time
self._time = time self._time = time
def abs(self, time): def abs(self, time):
return self._time return self._time
NEVER = When(INFINITY)
class Delta:
class Delta(When):
def __init__(self, delta): def __init__(self, delta):
self._delta = delta self._delta = delta
@@ -58,7 +62,8 @@ class TimedActivation(BaseScheduler):
(when, agent_id) = heappop(self._queue) (when, agent_id) = heappop(self._queue)
logger.debug(f'Stepping agent {agent_id}') logger.debug(f'Stepping agent {agent_id}')
when = (self._agents[agent_id].step() or Delta(1)).abs(self.time) returned = self._agents[agent_id].step()
when = (returned or Delta(1)).abs(self.time)
if when < self.time: if when < self.time:
raise Exception("Cannot schedule an agent for a time in the past ({} < {})".format(when, self.time)) raise Exception("Cannot schedule an agent for a time in the past ({} < {})".format(when, self.time))

22
tests/test_agents.py Normal file
View File

@@ -0,0 +1,22 @@
from unittest import TestCase
import pytest
from soil import agents, environment
from soil import time as stime
class Dead(agents.FSM):
@agents.default_state
@agents.state
def only(self):
self.die()
class TestMain(TestCase):
def test_die_raises_exception(self):
d = Dead(unique_id=0, model=environment.Environment())
d.step()
with pytest.raises(agents.DeadAgent):
d.step()
def test_die_returns_infinity(self):
d = Dead(unique_id=0, model=environment.Environment())
assert d.step().abs(0) == stime.INFINITY