Compare commits

...

7 Commits

Author SHA1 Message Date
J. Fernando Sánchez eca4cae298 Update tutorial and fix plotting bug 1 year ago
J. Fernando Sánchez 47a67f6665 Add jupyter to test-requirements 1 year ago
J. Fernando Sánchez c13550cf83 Tweak ipynb testing 1 year ago
J. Fernando Sánchez 55bbc76b2a Improve tutorial
* Improve text
* Move to docs
* Autogenerate with sphinx
* Fix naming issue `environment.run` (double name)
* Add to tests
1 year ago
J. Fernando Sánchez d13e4eb4b9 Plot env without agent reporters 1 year ago
J. Fernando Sánchez 93d23e4cab Add tutorial test to CI/CD 1 year ago
J. Fernando Sánchez 3802578ad5 Use context manager to add source files 1 year ago

@ -4,7 +4,7 @@
Soil is an extensible and user-friendly Agent-based Social Simulator for Social Networks. Soil is an extensible and user-friendly Agent-based Social Simulator for Social Networks.
Learn how to run your own simulations with our [documentation](http://soilsim.readthedocs.io). Learn how to run your own simulations with our [documentation](http://soilsim.readthedocs.io).
Follow our [tutorial](examples/tutorial/soil_tutorial.ipynb) to develop your own agent models. Follow our [tutorial](docs/tutorial/soil_tutorial.ipynb) to develop your own agent models.
> **Warning** > **Warning**
> Soil 1.0 introduced many fundamental changes. Check the [documention on how to update your simulations to work with newer versions](docs/notes_v1.0.rst) > Soil 1.0 introduced many fundamental changes. Check the [documention on how to update your simulations to work with newer versions](docs/notes_v1.0.rst)

@ -31,7 +31,10 @@
# Add any Sphinx extension module names here, as strings. They can be # Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones. # ones.
extensions = ['IPython.sphinxext.ipython_console_highlighting'] extensions = [
"IPython.sphinxext.ipython_console_highlighting",
"nbsphinx",
]
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates'] templates_path = ['_templates']
@ -64,7 +67,7 @@ release = '0.1'
# #
# This is also used if you do content translation via gettext catalogs. # This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases. # Usually you set "language" from the command line for these cases.
language = None language = "en"
# List of patterns, relative to source directory, that match files and # List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files. # directories to ignore when looking for source files.
@ -152,6 +155,3 @@ texinfo_documents = [
author, 'Soil', 'One line description of project.', author, 'Soil', 'One line description of project.',
'Miscellaneous'), 'Miscellaneous'),
] ]

@ -2,19 +2,20 @@ Welcome to Soil's documentation!
================================ ================================
Soil is an opinionated Agent-based Social Simulator in Python focused on Social Networks. Soil is an opinionated Agent-based Social Simulator in Python focused on Social Networks.
To get started developing your own simulations and agent behaviors, check out our :doc:`Tutorial <soil_tutorial>` and the `examples on GitHub <https://github.com/gsi-upm/soil/tree/master/examples>`.
Soil can be installed through pip (see more details in the :doc:`installation` page):.
.. image:: soil.png .. image:: soil.png
:width: 80% :width: 80%
:align: center :align: center
Soil can be installed through pip (see more details in the :doc:`installation` page):
.. code:: bash .. code:: bash
pip install soil pip install soil
To get started developing your own simulations and agent behaviors, check out our :doc:`Tutorial <soil_tutorial>` and the `examples on GitHub <https://github.com/gsi-upm/soil/tree/master/examples>.
If you use Soil in your research, do not forget to cite this paper: If you use Soil in your research, do not forget to cite this paper:
@ -46,7 +47,8 @@ If you use Soil in your research, do not forget to cite this paper:
:caption: Learn more about soil: :caption: Learn more about soil:
installation installation
Tutorial <soil_tutorial> Tutorial <tutorial/soil_tutorial>
notes_v1.0
.. ..

@ -1,3 +1,6 @@
Upgrading to Soil 1.0
---------------------
What are the main changes in version 1.0? What are the main changes in version 1.0?
######################################### #########################################

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 952 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

@ -1 +1,2 @@
ipython>=7.31.1 ipython>=7.31.1
nbsphinx==0.9.1

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

@ -17,9 +17,9 @@ def parse_requirements(filename):
install_reqs = parse_requirements("requirements.txt") install_reqs = parse_requirements("requirements.txt")
test_reqs = parse_requirements("test-requirements.txt") test_reqs = parse_requirements("test-requirements.txt")
extras_require={ extras_require={
'mesa': ['mesa>=0.8.9'],
'geo': ['scipy>=1.3'], 'geo': ['scipy>=1.3'],
'web': ['tornado'] 'web': ['tornado'],
'ipython': ['ipython==8.12', 'nbformat==5.8'],
} }
extras_require['all'] = [dep for package in extras_require.values() for dep in package] extras_require['all'] = [dep for package in extras_require.values() for dep in package]

@ -1,12 +1,11 @@
import os import os
import sys
import sqlalchemy import sqlalchemy
import pandas as pd import pandas as pd
from collections import namedtuple from collections import namedtuple
def plot(env, agent_df=None, model_df=None, steps=False, ignore=["agent_count", ]): def plot(env, agent_df=None, model_df=None, steps=False, ignore=["agent_count", ]):
"""Plot the model dataframe and agent dataframe together.""" """Plot the model dataframe and agent dataframe together."""
if agent_df is None:
agent_df = env.agent_df()
if model_df is None: if model_df is None:
model_df = env.model_df() model_df = env.model_df()
ignore = list(ignore) ignore = list(ignore)
@ -16,9 +15,17 @@ def plot(env, agent_df=None, model_df=None, steps=False, ignore=["agent_count",
ignore.append("time") ignore.append("time")
ax = model_df.drop(ignore, axis='columns').plot(); ax = model_df.drop(ignore, axis='columns').plot();
if agent_df is None:
try:
agent_df = env.agent_df()
except UserWarning:
print("No agent dataframe provided and no agent reporters found. Skipping agent plot.", file=sys.stderr)
return
if not agent_df.empty: if not agent_df.empty:
agent_df.unstack().apply(lambda x: x.value_counts(), agent_df.unstack().apply(lambda x: x.value_counts(),
axis=1).fillna(0).plot(ax=ax, secondary_y=True); axis=1).fillna(0).plot(ax=ax,
secondary_y=True)
Results = namedtuple("Results", ["config", "parameters", "env", "agents"]) Results = namedtuple("Results", ["config", "parameters", "env", "agents"])
#TODO implement reading from CSV and SQLITE #TODO implement reading from CSV and SQLITE

@ -216,10 +216,11 @@ class BaseEnvironment(Model):
@classmethod @classmethod
def run(cls, *, def run(cls, *,
name=None,
iterations=1, iterations=1,
num_processes=1, **kwargs): num_processes=1, **kwargs):
from .simulation import Simulation from .simulation import Simulation
return Simulation(name=cls.__name__, return Simulation(name=name or cls.__name__,
model=cls, iterations=iterations, model=cls, iterations=iterations,
num_processes=num_processes, **kwargs).run() num_processes=num_processes, **kwargs).run()

@ -8,6 +8,8 @@ import importlib.machinery, importlib.util
from glob import glob from glob import glob
from itertools import product, chain from itertools import product, chain
from contextlib import contextmanager
import yaml import yaml
import networkx as nx import networkx as nx
@ -110,12 +112,12 @@ KNOWN_MODULES = {
MODULE_FILES = {} MODULE_FILES = {}
def add_source_file(file): def _add_source_file(file):
"""Add a file to the list of known modules""" """Add a file to the list of known modules"""
file = os.path.abspath(file) file = os.path.abspath(file)
if file in MODULE_FILES: if file in MODULE_FILES:
logger.warning(f"File {file} already added as module {MODULE_FILES[file]}. Reloading") logger.warning(f"File {file} already added as module {MODULE_FILES[file]}. Reloading")
remove_source_file(file) _remove_source_file(file)
modname = f"imported_module_{len(MODULE_FILES)}" modname = f"imported_module_{len(MODULE_FILES)}"
loader = importlib.machinery.SourceFileLoader(modname, file) loader = importlib.machinery.SourceFileLoader(modname, file)
spec = importlib.util.spec_from_loader(loader.name, loader) spec = importlib.util.spec_from_loader(loader.name, loader)
@ -124,7 +126,7 @@ def add_source_file(file):
MODULE_FILES[file] = modname MODULE_FILES[file] = modname
KNOWN_MODULES[modname] = my_module KNOWN_MODULES[modname] = my_module
def remove_source_file(file): def _remove_source_file(file):
"""Remove a file from the list of known modules""" """Remove a file from the list of known modules"""
file = os.path.abspath(file) file = os.path.abspath(file)
modname = None modname = None
@ -134,6 +136,18 @@ def remove_source_file(file):
except KeyError as ex: except KeyError as ex:
raise ValueError(f"File {file} had not been added as a module: {ex}") raise ValueError(f"File {file} had not been added as a module: {ex}")
@contextmanager
def with_source(file=None):
"""Add a file to the list of known modules, and remove it afterwards"""
if file:
_add_source_file(file)
try:
yield
finally:
if file:
_remove_source_file(file)
def get_module(modname): def get_module(modname):
"""Get a module from the list of known modules""" """Get a module from the list of known modules"""
if modname not in KNOWN_MODULES or KNOWN_MODULES[modname] is None: if modname not in KNOWN_MODULES or KNOWN_MODULES[modname] is None:

@ -119,28 +119,23 @@ class Simulation:
self.logger = logger.getChild(self.name) self.logger = logger.getChild(self.name)
self.logger.setLevel(self.level) self.logger.setLevel(self.level)
if self.source_file: if self.source_file and (not os.path.isabs(self.source_file)):
source_file = self.source_file self.source_file = os.path.abspath(os.path.join(self.dir_path, self.source_file))
if not os.path.isabs(source_file): with serialization.with_source(self.source_file):
source_file = os.path.abspath(os.path.join(self.dir_path, source_file))
serialization.add_source_file(source_file) if isinstance(self.model, str):
self.source_file = source_file self.model = serialization.deserialize(self.model)
if isinstance(self.model, str): def deserialize_reporters(reporters):
self.model = serialization.deserialize(self.model) for (k, v) in reporters.items():
if isinstance(v, str) and v.startswith("py:"):
def deserialize_reporters(reporters): reporters[k] = serialization.deserialize(v.split(":", 1)[1])
for (k, v) in reporters.items(): return reporters
if isinstance(v, str) and v.startswith("py:"):
reporters[k] = serialization.deserialize(v.split(":", 1)[1]) self.agent_reporters = deserialize_reporters(self.agent_reporters)
return reporters self.model_reporters = deserialize_reporters(self.model_reporters)
self.tables = deserialize_reporters(self.tables)
self.agent_reporters = deserialize_reporters(self.agent_reporters) self.id = f"{self.name}_{current_time()}"
self.model_reporters = deserialize_reporters(self.model_reporters)
self.tables = deserialize_reporters(self.tables)
if self.source_file:
serialization.remove_source_file(self.source_file)
self.id = f"{self.name}_{current_time()}"
def run(self, **kwargs): def run(self, **kwargs):
"""Run the simulation and return the list of resulting environments""" """Run the simulation and return the list of resulting environments"""
@ -217,10 +212,7 @@ class Simulation:
): ):
"""Run the simulation and yield the resulting environments.""" """Run the simulation and yield the resulting environments."""
try: with serialization.with_source(self.source_file):
if self.source_file:
serialization.add_source_file(self.source_file)
with utils.timer(f"running for config {params}"): with utils.timer(f"running for config {params}"):
if self.dry_run: if self.dry_run:
def func(*args, **kwargs): def func(*args, **kwargs):
@ -237,9 +229,6 @@ class Simulation:
continue continue
yield env yield env
finally:
if self.source_file:
serialization.remove_source_file(self.source_file)
def _get_env(self, iteration_id, params): def _get_env(self, iteration_id, params):
"""Create an environment for a iteration of the simulation""" """Create an environment for a iteration of the simulation"""

@ -2,3 +2,6 @@ pytest
pytest-profiling pytest-profiling
scipy>=1.3 scipy>=1.3
tornado tornado
nbconvert==7.3.1
nbformat==5.8.0
jupyter==1.0.0

@ -0,0 +1,18 @@
from unittest import TestCase
import os
import nbformat
from nbconvert.preprocessors import ExecutePreprocessor
ROOT = os.path.abspath(os.path.dirname(__file__))
class TestNotebooks(TestCase):
def test_tutorial(self):
notebook = os.path.join(ROOT, "../docs/tutorial/soil_tutorial.ipynb")
with open(notebook) as f:
nb = nbformat.read(f, as_version=4)
ep = ExecutePreprocessor(timeout=60000)
try:
assert ep.preprocess(nb) is not None, f"Got empty notebook for {notebook}"
except Exception:
assert False, f"Failed executing {notebook}"
Loading…
Cancel
Save