Compare commits
132 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
092591fb97 | ||
|
263b4e0e33 | ||
|
189836408f | ||
|
ee0c4517cb | ||
|
3041156f19 | ||
|
f49be3af68 | ||
|
5e93399d58 | ||
|
eca4cae298 | ||
|
47a67f6665 | ||
|
c13550cf83 | ||
|
55bbc76b2a | ||
|
d13e4eb4b9 | ||
|
93d23e4cab | ||
|
3802578ad5 | ||
|
4e296e0cf1 | ||
|
302075a65d | ||
|
fba379c97c | ||
|
50bca88362 | ||
|
cc238d84ec | ||
|
be65592055 | ||
|
1d882dcff6 | ||
|
b3e77cbff5 | ||
|
05748a3250 | ||
|
a3fc6a5efa | ||
|
4e95709188 | ||
|
feab0ba79e | ||
|
73282530fd | ||
|
bf481f0f88 | ||
|
2869b1e1e6 | ||
|
d3cee18635 | ||
|
9a7b62e88e | ||
|
c09e480d37 | ||
|
b2d48cb4df | ||
|
a1262edd2a | ||
|
cbbaf73538 | ||
|
2f5e5d0a74 | ||
|
a2fb25c160 | ||
|
5fcf610108 | ||
|
159c9a9077 | ||
|
3776c4e5c5 | ||
|
880a9f2a1c | ||
|
227fdf050e | ||
|
5d759d0072 | ||
|
77d08fc592 | ||
|
0efcd24d90 | ||
|
78833a9e08 | ||
|
d9947c2c52 | ||
|
cd62c23cb9 | ||
|
f811ee18c5 | ||
|
0a9c6d8b19 | ||
|
3dc56892c1 | ||
|
e41dc3dae2 | ||
|
bbaed636a8 | ||
|
6f7481769e | ||
|
1a8313e4f6 | ||
|
a40aa55b6a | ||
|
50cba751a6 | ||
|
dfb6d13649 | ||
|
5559d37e57 | ||
|
2116fe6f38 | ||
|
affeeb9643 | ||
|
42ddc02318 | ||
|
cab9a3440b | ||
|
db505da49c | ||
|
8eb8eb16eb | ||
|
3fc5ca8c08 | ||
|
c02e6ea2e8 | ||
|
38f8a8d110 | ||
|
cb72aac980 | ||
|
6c4f44b4cb | ||
|
af9a392a93 | ||
|
5d7e57675a | ||
|
e860bdb922 | ||
|
d6b684c1c1 | ||
|
05f7f49233 | ||
|
3b2c6a3db5 | ||
|
6118f917ee | ||
|
6adc8d36ba | ||
|
c8b8149a17 | ||
|
6690b6ee5f | ||
|
97835b3d10 | ||
|
b0add8552e | ||
|
1cf85ea450 | ||
|
c32e167fb8 | ||
|
5f68b5321d | ||
|
2a2843bd19 | ||
|
d1006bd55c | ||
|
9bc036d185 | ||
|
a3ea434f23 | ||
|
65f6aa72f3 | ||
|
09e14c6e84 | ||
|
8593ac999d | ||
|
90338c3549 | ||
|
1d532dacfe | ||
|
a1f8d8c9c5 | ||
|
de326eb331 | ||
|
04b4380c61 | ||
|
d70a0c865c | ||
|
625c28e4ee | ||
|
9749f4ca14 | ||
|
3526fa29d7 | ||
|
53604c1e66 | ||
|
01cc8e9128 | ||
|
a47ffa815b | ||
|
b41927d7bf | ||
|
70d033b3a9 | ||
|
3afed06656 | ||
|
0a7ef27844 | ||
|
2e28b36f6e | ||
|
bd4700567e | ||
|
ff1df62eec | ||
|
9165979b49 | ||
|
078f8ace9e | ||
|
8fec544772 | ||
|
5420501d36 | ||
|
5d89827ccf | ||
|
fc48ed7e09 | ||
|
73c90887e8 | ||
|
497c8a55db | ||
|
7d1c800490 | ||
|
a4b32afa2f | ||
|
a7c51742f6 | ||
|
78364d89d5 | ||
|
af76f54a28 | ||
|
dbc182c6d0 | ||
|
eafecc9e5e | ||
|
e8988015e2 | ||
|
ccc8e43416 | ||
|
347d295b09 | ||
|
bbc5386cf5 | ||
|
f8538fe057 | ||
|
e1be3a730e |
7
.dockerignore
Normal file
@@ -0,0 +1,7 @@
|
||||
**/soil_output
|
||||
.*
|
||||
**/.*
|
||||
**/__pycache__
|
||||
__pycache__
|
||||
*.pyc
|
||||
**/backup
|
10
.gitignore
vendored
@@ -1,5 +1,13 @@
|
||||
__pycache__/
|
||||
.idea/
|
||||
.ipynb_checkpoints/
|
||||
*.png
|
||||
*.gexf
|
||||
.*
|
||||
results
|
||||
soil_output
|
||||
docs/_build*
|
||||
build/*
|
||||
dist/*
|
||||
prof
|
||||
backup
|
||||
*.egg-info
|
||||
|
53
.gitlab-ci.yml
Normal file
@@ -0,0 +1,53 @@
|
||||
stages:
|
||||
- test
|
||||
- publish
|
||||
- check_published
|
||||
|
||||
docker:
|
||||
stage: publish
|
||||
image:
|
||||
name: gcr.io/kaniko-project/executor:debug
|
||||
entrypoint: [""]
|
||||
tags:
|
||||
- docker
|
||||
script:
|
||||
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
|
||||
# The skip-tls-verify flag is there because our registry certificate is self signed
|
||||
- /kaniko/executor --context $CI_PROJECT_DIR --skip-tls-verify --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
|
||||
only:
|
||||
- tags
|
||||
|
||||
test:
|
||||
tags:
|
||||
- docker
|
||||
image: python:3.8
|
||||
stage: test
|
||||
script:
|
||||
- pip install -r requirements.txt -r test-requirements.txt
|
||||
- python setup.py test
|
||||
|
||||
push_pypi:
|
||||
only:
|
||||
- tags
|
||||
tags:
|
||||
- docker
|
||||
image: python:3.8
|
||||
stage: publish
|
||||
script:
|
||||
- echo $CI_COMMIT_TAG > soil/VERSION
|
||||
- pip install twine
|
||||
- python setup.py sdist bdist_wheel
|
||||
- TWINE_PASSWORD=$PYPI_PASSWORD TWINE_USERNAME=$PYPI_USERNAME python -m twine upload dist/*
|
||||
|
||||
check_pypi:
|
||||
only:
|
||||
- tags
|
||||
tags:
|
||||
- docker
|
||||
image: python:3.8
|
||||
stage: check_published
|
||||
script:
|
||||
- pip install soil==$CI_COMMIT_TAG
|
||||
# Allow PYPI to update its index before we try to install
|
||||
when: delayed
|
||||
start_in: 2 minutes
|
199
CHANGELOG.md
Normal file
@@ -0,0 +1,199 @@
|
||||
# Changelog
|
||||
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).
|
||||
|
||||
## [1.0 UNRELEASED]
|
||||
|
||||
Version 1.0 introduced multiple changes, especially on the `Simulation` class and anything related to how configuration is handled.
|
||||
For an explanation of the general changes in version 1.0, please refer to the file `docs/notes_v1.0.rst`.
|
||||
|
||||
### Added
|
||||
* A modular set of classes for environments/models. Now the ability to configure the agents through an agent definition and a topology through a network configuration is split into two classes (`soil.agents.BaseEnvironment` for agents, `soil.agents.NetworkEnvironment` to add topology).
|
||||
* Environments now have a class method to make them easier to use without a simulation`.run`. Notice that this is different from `run_model`, which is an instance method.
|
||||
* Ability to run simulations using mesa models
|
||||
* The `soil.exporters` module to export the results of datacollectors (`model.datacollector`) into files at the end of trials/simulations
|
||||
* Agents can now have generators or async functions as their step or as states. They work similar to normal functions, with one caveat in the case of `FSM`: only time values (a float, int or None) can be awaited or yielded, not a state. This is because the state will not change, it will be resumed after the yield, at the appropriate time. To return to a different state, use the `delay` and `at` functions of the state.
|
||||
* Simulations can now specify a `matrix` with possible values for every simulation parameter. The final parameters will be calculated based on the `parameters` used and a cartesian product (i.e., all possible combinations) of each parameter.
|
||||
* Simple debugging capabilities in `soil.debugging`, with a custom `pdb.Debugger` subclass that exposes commands to list agents and their status and set breakpoints on states (for FSM agents). Try it with `soil --debug <simulation file>`
|
||||
* The `agent.after` and `agent.at` methods, to avoid having to return a time manually.
|
||||
### Changed
|
||||
* Configuration schema (`Simulation`) is very simplified. All simulations should be checked
|
||||
* Agents that wish to
|
||||
* Model / environment variables are expected (but not enforced) to be a single value. This is done to more closely align with mesa
|
||||
* `Exporter.iteration_end` now takes two parameters: `env` (same as before) and `params` (specific parameters for this environment). We considered including a `parameters` attribute in the environment, but this would not be compatible with mesa.
|
||||
* `num_trials` renamed to `iterations`
|
||||
* General renaming of `trial` to `iteration`, to work better with `mesa`
|
||||
* `model_parameters` renamed to `parameters` in simulation
|
||||
* Simulation results for every iteration of a simulation with the same name are stored in a single `sqlite` database
|
||||
|
||||
### Removed
|
||||
* The `time.When` and `time.Cond` classes are removed
|
||||
* Any `tsih` and `History` integration in the main classes. To record the state of environments/agents, just use a datacollector. In some cases this may be slower or consume more memory than the previous system. However, few cases actually used the full potential of the history, and it came at the cost of unnecessary complexity and worse performance for the majority of cases.
|
||||
|
||||
## [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]
|
||||
### Fixed
|
||||
* Default state values are now deepcopied again.
|
||||
* Seeds for environments only concatenate the trial id (i.e., a number), to provide repeatable results.
|
||||
* `Environment.run` now calls `Environment.step`, to allow for easy overloading of the environment step
|
||||
### Removed
|
||||
* Datacollectors are not being used for now.
|
||||
* `time.TimedActivation.step` does not use an `until` parameter anymore.
|
||||
### Changed
|
||||
* Simulations now run right up to `until` (open interval)
|
||||
* Time instants (`time.When`) don't need to be floats anymore. Now we can avoid precision issues with big numbers by using ints.
|
||||
* Rabbits simulation is more idiomatic (using subclasses)
|
||||
|
||||
## [0.20.2]
|
||||
### Fixed
|
||||
* CI/CD testing issues
|
||||
## [0.20.1]
|
||||
### Fixed
|
||||
* Agents would run another step after dying.
|
||||
## [0.20.0]
|
||||
### Added
|
||||
* Integration with MESA
|
||||
* `not_agent_ids` parameter to get sql in history
|
||||
### Changed
|
||||
* `soil.Environment` now also inherits from `mesa.Model`
|
||||
* `soil.Agent` now also inherits from `mesa.Agent`
|
||||
* `soil.time` to replace `simpy` events, delays, duration, etc.
|
||||
* `agent.id` is not `agent.unique_id` to be compatible with `mesa`. A property `BaseAgent.id` has been added for compatibility.
|
||||
* `agent.environment` is now `agent.model`, for the same reason as above. The parameter name in `BaseAgent.__init__` has also been renamed.
|
||||
### Removed
|
||||
* `simpy` dependency and compatibility. Each agent used to be a simpy generator, but that made debugging and error handling more complex. That has been replaced by a scheduler within the `soil.Environment` class, similar to how `mesa` does it.
|
||||
* `soil.history` is now a separate package named `tsih`. The keys namedtuple uses `dict_id` instead of `agent_id`.
|
||||
### Added
|
||||
* An option to choose whether a database should be used for history
|
||||
## [0.15.2]
|
||||
### Fixed
|
||||
* Pass the right known_modules and parameters to stats discovery in simulation
|
||||
* The configuration file must exist when launching through the CLI. If it doesn't, an error will be logged
|
||||
* Minor changes in the documentation of the CLI arguments
|
||||
### Changed
|
||||
* Stats are now exported by default
|
||||
## [0.15.1]
|
||||
### Added
|
||||
* read-only `History`
|
||||
### Fixed
|
||||
* Serialization problem with the `Environment` on parallel mode.
|
||||
* Analysis functions now work as they should in the tutorial
|
||||
## [0.15.0]
|
||||
### Added
|
||||
* Control logging level in CLI and simulation
|
||||
* `Stats` to calculate trial and simulation-wide statistics
|
||||
* Simulation statistics are stored in a separate table in history (see `History.get_stats` and `History.save_stats`, as well as `soil.stats`)
|
||||
* Aliased `NetworkAgent.G` to `NetworkAgent.topology`.
|
||||
### Changed
|
||||
* Templates in config files can be given as dictionaries in addition to strings
|
||||
* Samplers are used more explicitly
|
||||
* Removed nxsim dependency. We had already made a lot of changes, and nxsim has not been updated in 5 years.
|
||||
* Exporter methods renamed to `trial` and `end`. Added `start`.
|
||||
* `Distribution` exporter now a stats class
|
||||
* `global_topology` renamed to `topology`
|
||||
* Moved topology-related methods to `NetworkAgent`
|
||||
### Fixed
|
||||
* Temporary files used for history in dry_run mode are not longer left open
|
||||
|
||||
## [0.14.9]
|
||||
### Changed
|
||||
* Seed random before environment initialization
|
||||
## [0.14.8]
|
||||
### Fixed
|
||||
* Invalid directory names in Windows gsi-upm/soil#5
|
||||
## [0.14.7]
|
||||
### Changed
|
||||
* Minor change to traceback handling in async simulations
|
||||
### Fixed
|
||||
* Incomplete example in the docs (example.yml) caused an exception
|
||||
## [0.14.6]
|
||||
### Fixed
|
||||
* Bug with newer versions of networkx (0.24) where the Graph.node attribute has been removed. We have updated our calls, but the code in nxsim is not under our control, so we have pinned the networkx version until that issue is solved.
|
||||
### Changed
|
||||
* Explicit yaml.SafeLoader to avoid deprecation warnings when using yaml.load. It should not break any existing setups, but we could move to the FullLoader in the future if needed.
|
||||
|
||||
## [0.14.4]
|
||||
### Fixed
|
||||
* Bug in `agent.get_agents()` when `state_id` is passed as a string. The tests have been modified accordingly.
|
||||
## [0.14.3]
|
||||
### Fixed
|
||||
* Incompatibility with py3.3-3.6 due to ModuleNotFoundError and TypeError in DryRunner
|
||||
## [0.14.2]
|
||||
### Fixed
|
||||
* Output path for exporters is now soil_output
|
||||
### Changed
|
||||
* CSV output to stdout in dry_run mode
|
||||
## [0.14.1]
|
||||
### Changed
|
||||
* Exporter names in lower case
|
||||
* Add default exporter in runs
|
||||
## [0.14.0]
|
||||
### Added
|
||||
* Loading configuration from template definitions in the yaml, in preparation for SALib support.
|
||||
The definition of the variables and their possible values (i.e., a problem in SALib terms), as well as a sampler function, can be provided.
|
||||
Soil uses this definition and the template to generate a set of configurations.
|
||||
* Simulation group names, to link related simulations. For now, they are only used to group all simulations in the same group under the same folder.
|
||||
* Exporters unify exporting/dumping results and other files to disk. If `dry_run` is set to `True`, exporters will write to stdout instead of a file (useful for testing/debugging).
|
||||
* Distribution exporter, to write statistics about values and value_counts in every simulation. The results are dumped to two CSV files.
|
||||
|
||||
### Changed
|
||||
* `dir_path` is now the directory for resources (modules, files)
|
||||
* Environments and simulations do not export or write anything by default. That task is delegated to Exporters
|
||||
|
||||
### Removed
|
||||
* The output dir for environments and simulations (see Exporters)
|
||||
* DrawingAgent, because it wrote to disk and was not being used. We provide a partial alternative in the form of the GraphDrawing exporter. A complete alternative will be provided once the network at each state can be accessed by exporters.
|
||||
|
||||
## Fixed
|
||||
* Modules with custom agents/environments failed to load when they were run from outside the directory of the definition file. Modules are now loaded from the directory of the simulation file in addition to the working directory
|
||||
* Memory databases (in history) can now be shared between threads.
|
||||
* Testing all examples, not just subdirectories
|
||||
|
||||
## [0.13.8]
|
||||
### Changed
|
||||
* Moved TerroristNetworkModel to examples
|
||||
### 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
|
||||
|
||||
## [0.13.7]
|
||||
### Changed
|
||||
* History now defaults to not backing up! This makes it more intuitive to load the history for examination, at the expense of rewriting something. That should not happen because History is only created in the Environment, and that has `backup=True`.
|
||||
### Added
|
||||
* Agent names are assigned based on their agent types
|
||||
* 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`).
|
||||
* 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`.
|
12
Dockerfile
Normal file
@@ -0,0 +1,12 @@
|
||||
FROM python:3.7
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY test-requirements.txt requirements.txt /usr/src/app/
|
||||
RUN pip install --no-cache-dir -r test-requirements.txt -r requirements.txt
|
||||
|
||||
COPY ./ /usr/src/app
|
||||
|
||||
RUN pip install '.[web]'
|
||||
|
||||
ENTRYPOINT ["python", "-m", "soil"]
|
7
MANIFEST.in
Normal file
@@ -0,0 +1,7 @@
|
||||
include requirements.txt
|
||||
include test-requirements.txt
|
||||
include README.rst
|
||||
graft soil
|
||||
global-exclude __pycache__
|
||||
global-exclude soil_output
|
||||
global-exclude *.py[co]
|
7
Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
quick-test:
|
||||
docker-compose exec dev python -m pytest -s -v
|
||||
|
||||
test:
|
||||
docker run -t -v $$PWD:/usr/src/app -w /usr/src/app python:3.7 python setup.py test
|
||||
|
||||
.PHONY: test
|
89
README.md
@@ -1,12 +1,91 @@
|
||||
#[Soil](https://github.com/gsi-upm/soil)
|
||||
|
||||
The purpose of Soil (SOcial network sImuLator) is provding an Agent-based Social Simulator written in Python for Social Networks.
|
||||
# [SOIL](https://github.com/gsi-upm/soil)
|
||||
|
||||
|
||||
In order to see quickly how to use Soil, you can follow the following [tutorial](https://github.com/gsi-upm/soil/blob/master/soil_tutorial.ipynb).
|
||||
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).
|
||||
|
||||
Follow our [tutorial](docs/tutorial/soil_tutorial.ipynb) to develop your own agent models.
|
||||
|
||||
> **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)
|
||||
|
||||
## Features
|
||||
|
||||
* Integration with (social) networks (through `networkx`)
|
||||
* Convenience functions and methods to easily assign agents to your model (and optionally to its network):
|
||||
* Following a given distribution (e.g., 2 agents of type `Foo`, 10% of the network should be agents of type `Bar`)
|
||||
* Based on the topology of the network
|
||||
* **Several types of abstractions for agents**:
|
||||
* Finite state machine, where methods can be turned into a state
|
||||
* Network agents, which have convenience methods to access the model's topology
|
||||
* Generator-based agents, whose state is paused though a `yield` and resumed on the next step
|
||||
* **Reporting and data collection**:
|
||||
* Soil models include data collection and record some data by default (# of agents, state of each agent, etc.)
|
||||
* All data collected are exported by default to a SQLite database and a description file
|
||||
* Options to export to other formats, such as CSV, or defining your own exporters
|
||||
* A summary of the data collected is shown in the command line, for easy inspection
|
||||
* **An event-based scheduler**
|
||||
* Agents can be explicit about when their next time/step should be, and not all agents run in every step. This avoids unnecessary computation.
|
||||
* Time intervals between each step are flexible.
|
||||
* There are primitives to specify when the next execution of an agent should be (or conditions)
|
||||
* **Actor-inspired** message-passing
|
||||
* A simulation runner (`soil.Simulation`) that can:
|
||||
* Run models in parallel
|
||||
* Save results to different formats
|
||||
* Simulation configuration files
|
||||
* A command line interface (`soil`), to quickly run simulations with different parameters
|
||||
* An integrated debugger (`soil --debug`) with custom functions to print agent states and break at specific states
|
||||
|
||||
## Mesa compatibility
|
||||
|
||||
SOIL has been redesigned to integrate well with [Mesa](https://github.com/projectmesa/mesa).
|
||||
For instance, it should be possible to run a `mesa.Model` models using a `soil.Simulation` and the `soil` CLI, or to integrate the `soil.TimedActivation` scheduler on a `mesa.Model`.
|
||||
|
||||
Note that some combinations of `mesa` and `soil` components, while technically possible, are much less useful or might yield surprising results.
|
||||
For instance, you may add any `soil.agent` agent on a regular `mesa.Model` with a vanilla scheduler from `mesa.time`.
|
||||
But in that case the agents will not get any of the advanced event-based scheduling, and most agent behaviors that depend on that may not work.
|
||||
|
||||
|
||||
## Changes in version 0.3
|
||||
|
||||
Version 0.3 came packed with many changes to provide much better integration with MESA.
|
||||
For a long time, we tried to keep soil backwards-compatible, but it turned out to be a big endeavour and the resulting code was less readable.
|
||||
This translates to harder maintenance and a worse experience for newcomers.
|
||||
In the end, we decided to make some breaking changes.
|
||||
|
||||
If you have an older Soil simulation, you have two options:
|
||||
|
||||
* Update the necessary configuration files and code. You may use the examples in the `examples` folder for reference, as well as the documentation.
|
||||
* Keep using a previous `soil` version.
|
||||
|
||||
|
||||
|
||||
## Citation
|
||||
|
||||
|
||||
@Copyright GSI - Universidad Politécnica de Madrid 2017
|
||||
If you use Soil in your research, don't forget to cite this paper:
|
||||
|
||||
```bibtex
|
||||
@inbook{soil-gsi-conference-2017,
|
||||
author = "S{\'a}nchez, Jes{\'u}s M. and Iglesias, Carlos A. and S{\'a}nchez-Rada, J. Fernando",
|
||||
booktitle = "Advances in Practical Applications of Cyber-Physical Multi-Agent Systems: The PAAMS Collection",
|
||||
doi = "10.1007/978-3-319-59930-4_19",
|
||||
editor = "Demazeau Y., Davidsson P., Bajo J., Vale Z.",
|
||||
isbn = "978-3-319-59929-8",
|
||||
keywords = "soil;social networks;agent based social simulation;python",
|
||||
month = "June",
|
||||
organization = "PAAMS 2017",
|
||||
pages = "234-245",
|
||||
publisher = "Springer Verlag",
|
||||
series = "LNAI",
|
||||
title = "{S}oil: {A}n {A}gent-{B}ased {S}ocial {S}imulator in {P}ython for {M}odelling and {S}imulation of {S}ocial {N}etworks",
|
||||
url = "https://link.springer.com/chapter/10.1007/978-3-319-59930-4_19",
|
||||
volume = "10349",
|
||||
year = "2017",
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@Copyright GSI - Universidad Politécnica de Madrid 2017-2021
|
||||
|
||||
[](https://www.gsi.upm.es)
|
||||
|
12
benchmarks/noop-bench-async.csv
Normal file
@@ -0,0 +1,12 @@
|
||||
command,mean,stddev,median,user,system,min,max,parameter_sim
|
||||
python noop/mesa_batchrunner.py,1.3258325165599998,0.05822826666377271,1.31279976286,1.2978164199999997,0.25767558,1.2780627573599999,1.46763559736,mesa_batchrunner
|
||||
python noop/mesa_simulation.py,1.3915081544599999,0.07311646048704976,1.37166811936,1.35267662,0.29222067999999995,1.32746067836,1.58495303336,mesa_simulation
|
||||
python noop/soil_step.py,1.9859962588599998,0.12143759641749913,1.93586195486,2.0000750199999997,0.54126188,1.9061700903599998,2.2532835533599997,soil_step
|
||||
python noop/soil_step_pqueue.py,2.1347049971600005,0.01336179424666973,2.13492341986,2.1368160200000004,0.56862948,2.11810132936,2.16042739636,soil_step_pqueue
|
||||
python noop/soil_gens.py,2.1284937893599998,0.03030587681163665,2.13585231586,2.14158812,0.54900038,2.0768625143599997,2.19043625236,soil_gens
|
||||
python noop/soil_gens_pqueue.py,2.3469003942599995,0.019461346004472344,2.3486906343599996,2.36505852,0.54629858,2.31766326036,2.37998102136,soil_gens_pqueue
|
||||
python noop/soil_async.py,2.85755484126,0.0314955571121844,2.84774029536,2.86388112,0.55261338,2.81428668936,2.90567961636,soil_async
|
||||
python noop/soil_async_pqueue.py,3.1999731134600005,0.04432336803797717,3.20255954186,3.2162337199999995,0.5501872800000001,3.1406816913599997,3.26137401936,soil_async_pqueue
|
||||
python noop/soilent_step.py,1.30038977816,0.017973958957989845,1.30187804986,1.3231730199999998,0.5452653799999999,1.27058263436,1.31902240836,soilent_step
|
||||
python noop/soilent_step_pqueue.py,1.4708435788599998,0.027193290392962755,1.4707784423599999,1.4900387199999998,0.54749428,1.43498127536,1.53065598436,soilent_step_pqueue
|
||||
python noop/soilent_gens.py,1.6338810973599998,0.05752539125688073,1.63513330036,1.65216122,0.51846678,1.54135944036,1.7038832853599999,soilent_gens
|
|
11
benchmarks/noop-bench.csv
Normal file
@@ -0,0 +1,11 @@
|
||||
command,mean,stddev,median,user,system,min,max,parameter_sim
|
||||
python noop/mesa1_batchrunner.py,1.2559917394000002,0.012031173494887278,1.2572688413000002,1.2168630799999998,0.31825289999999995,1.2346063853,1.2735512493,mesa1_batchrunner
|
||||
python noop/mesa1_simulation.py,1.3024417227,0.022498874113931668,1.2994157323,1.2595484799999999,0.3087897,1.2697029703,1.3350640403,mesa1_simulation
|
||||
python noop/soil1.py,1.8789492443,0.18023367899835044,1.8186795393000001,1.86076288,0.5309521,1.7326687413000001,2.2928370642999996,soil1
|
||||
python noop/soil1_pqueue.py,1.9841675890000001,0.01735524088843906,1.9884363323,2.01830338,0.5787977999999999,1.9592171483,2.0076169282999996,soil1_pqueue
|
||||
python noop/soil2.py,2.0135188921999996,0.02869307129649681,2.0184709453,2.03951308,0.5885591,1.9680417823,2.0567112592999997,soil2
|
||||
python noop/soil2_pqueue.py,2.2367320454999997,0.024339667344486046,2.2357249777999995,2.2515216799999997,0.5978869,2.1957917303,2.2688685033,soil2_pqueue
|
||||
python noop/soilent1.py,1.1309301329,0.015133005948737871,1.1276461497999999,1.14056688,0.6027519,1.1135821423,1.1625753893,soilent1
|
||||
python noop/soilent1_pqueue.py,1.3097537665000003,0.018821977712258842,1.3073709358,1.3270259799999997,0.6000067999999998,1.2874580013,1.3381646823,soilent1_pqueue
|
||||
python noop/soilent2.py,1.5055360476,0.05166674417574119,1.4883118568,1.5121205799999997,0.5817363999999999,1.4490918363,1.6005909333000001,soilent2
|
||||
python noop/soilent2_pqueue.py,1.6622598218,0.031130739036296016,1.6588702603,1.6862567799999997,0.5854159,1.6289724583,1.7330545383,soilent2_pqueue
|
|
26
benchmarks/noop/_config.py
Normal file
@@ -0,0 +1,26 @@
|
||||
import os
|
||||
|
||||
NUM_AGENTS = int(os.environ.get('NUM_AGENTS', 100))
|
||||
NUM_ITERS = int(os.environ.get('NUM_ITERS', 10))
|
||||
MAX_STEPS = int(os.environ.get('MAX_STEPS', 1000))
|
||||
|
||||
|
||||
def run_sim(model, **kwargs):
|
||||
from soil import Simulation
|
||||
opts = dict(model=model,
|
||||
dump=False,
|
||||
num_processes=1,
|
||||
parameters={'num_agents': NUM_AGENTS},
|
||||
seed="",
|
||||
max_steps=MAX_STEPS,
|
||||
iterations=NUM_ITERS)
|
||||
opts.update(kwargs)
|
||||
res = Simulation(**opts).run()
|
||||
|
||||
total = sum(a.num_calls for e in res for a in e.schedule.agents)
|
||||
expected = NUM_AGENTS * NUM_ITERS * MAX_STEPS
|
||||
print(total)
|
||||
print(expected)
|
||||
|
||||
assert total == expected
|
||||
return res
|
43
benchmarks/noop/mesa_batchrunner.py
Normal file
@@ -0,0 +1,43 @@
|
||||
from mesa import batch_run, DataCollector, Agent, Model
|
||||
from mesa.time import RandomActivation
|
||||
|
||||
|
||||
class NoopAgent(Agent):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.num_calls = 0
|
||||
|
||||
def step(self):
|
||||
self.num_calls += 1
|
||||
|
||||
|
||||
class NoopModel(Model):
|
||||
def __init__(self, N):
|
||||
super().__init__()
|
||||
self.schedule = RandomActivation(self)
|
||||
for i in range(N):
|
||||
self.schedule.add(NoopAgent(self.next_id(), self))
|
||||
self.datacollector = DataCollector(model_reporters={"num_agents": lambda m: m.schedule.get_agent_count(),
|
||||
"time": lambda m: m.schedule.time},
|
||||
agent_reporters={"num_calls": "num_calls"})
|
||||
self.datacollector.collect(self)
|
||||
|
||||
def step(self):
|
||||
self.schedule.step()
|
||||
self.datacollector.collect(self)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from _config import *
|
||||
|
||||
res = batch_run(model_cls=NoopModel,
|
||||
max_steps=MAX_STEPS,
|
||||
iterations=NUM_ITERS,
|
||||
number_processes=1,
|
||||
parameters={'N': NUM_AGENTS})
|
||||
total = sum(s["num_calls"] for s in res)
|
||||
total_agents = sum(s["num_agents"] for s in res)
|
||||
assert len(res) == NUM_AGENTS * NUM_ITERS
|
||||
assert total == NUM_AGENTS * NUM_ITERS * MAX_STEPS
|
||||
assert total_agents == NUM_AGENTS * NUM_AGENTS * NUM_ITERS
|
||||
|
37
benchmarks/noop/mesa_simulation.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from mesa import batch_run, DataCollector, Agent, Model
|
||||
from mesa.time import RandomActivation
|
||||
from soil import Simulation
|
||||
from _config import *
|
||||
|
||||
|
||||
class NoopAgent(Agent):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.num_calls = 0
|
||||
|
||||
def step(self):
|
||||
self.num_calls += 1
|
||||
|
||||
|
||||
class NoopModel(Model):
|
||||
def __init__(self, num_agents, *args, **kwargs):
|
||||
super().__init__()
|
||||
self.schedule = RandomActivation(self)
|
||||
for i in range(num_agents):
|
||||
self.schedule.add(NoopAgent(self.next_id(), self))
|
||||
self.datacollector = DataCollector(model_reporters={"num_agents": lambda m: m.schedule.get_agent_count(),
|
||||
"time": lambda m: m.schedule.time},
|
||||
agent_reporters={"num_calls": "num_calls"})
|
||||
self.datacollector.collect(self)
|
||||
|
||||
def step(self):
|
||||
self.schedule.step()
|
||||
self.datacollector.collect(self)
|
||||
|
||||
|
||||
def run():
|
||||
run_sim(model=NoopModel)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
3
benchmarks/noop/noop-bench.csv
Normal file
@@ -0,0 +1,3 @@
|
||||
command,mean,stddev,median,user,system,min,max,parameter_sim
|
||||
python mesa1_batchrunner.py,1.2932078178200002,0.05649377020829272,1.2705532802200001,1.25902256,0.27242284,1.22210926572,1.40867459172,mesa1_batchrunner
|
||||
python mesa1_simulation.py,1.81112963812,0.015491072368938567,1.81342524572,1.8594407599999996,0.8005329399999999,1.78538603972,1.84176361172,mesa1_simulation
|
|
24
benchmarks/noop/soil_async.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from soil import BaseAgent, Environment, Simulation
|
||||
|
||||
|
||||
class NoopAgent(BaseAgent):
|
||||
num_calls = 0
|
||||
|
||||
async def step(self):
|
||||
while True:
|
||||
self.num_calls += 1
|
||||
await self.delay()
|
||||
|
||||
|
||||
class NoopEnvironment(Environment):
|
||||
num_agents = 100
|
||||
|
||||
def init(self):
|
||||
self.add_agents(NoopAgent, k=self.num_agents)
|
||||
self.add_agent_reporter("num_calls")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from _config import *
|
||||
|
||||
run_sim(model=NoopEnvironment)
|
25
benchmarks/noop/soil_async_pqueue.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from soil import BaseAgent, Environment, Simulation, PQueueActivation
|
||||
|
||||
|
||||
class NoopAgent(BaseAgent):
|
||||
num_calls = 0
|
||||
|
||||
async def step(self):
|
||||
while True:
|
||||
self.num_calls += 1
|
||||
await self.delay()
|
||||
|
||||
|
||||
class NoopEnvironment(Environment):
|
||||
num_agents = 100
|
||||
schedule_class = PQueueActivation
|
||||
|
||||
def init(self):
|
||||
self.add_agents(NoopAgent, k=self.num_agents)
|
||||
self.add_agent_reporter("num_calls")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from _config import *
|
||||
|
||||
run_sim(model=NoopEnvironment)
|
24
benchmarks/noop/soil_gens.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from soil import BaseAgent, Environment, Simulation
|
||||
|
||||
|
||||
class NoopAgent(BaseAgent):
|
||||
num_calls = 0
|
||||
|
||||
def step(self):
|
||||
while True:
|
||||
self.num_calls += 1
|
||||
yield self.delay()
|
||||
|
||||
|
||||
class NoopEnvironment(Environment):
|
||||
num_agents = 100
|
||||
|
||||
def init(self):
|
||||
self.add_agents(NoopAgent, k=self.num_agents)
|
||||
self.add_agent_reporter("num_calls")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from _config import *
|
||||
|
||||
run_sim(model=NoopEnvironment)
|
25
benchmarks/noop/soil_gens_pqueue.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from soil import BaseAgent, Environment, Simulation, PQueueActivation
|
||||
|
||||
|
||||
class NoopAgent(BaseAgent):
|
||||
num_calls = 0
|
||||
|
||||
def step(self):
|
||||
while True:
|
||||
self.num_calls += 1
|
||||
yield self.delay()
|
||||
|
||||
|
||||
class NoopEnvironment(Environment):
|
||||
num_agents = 100
|
||||
schedule_class = PQueueActivation
|
||||
|
||||
def init(self):
|
||||
self.add_agents(NoopAgent, k=self.num_agents)
|
||||
self.add_agent_reporter("num_calls")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from _config import *
|
||||
|
||||
run_sim(model=NoopEnvironment)
|
21
benchmarks/noop/soil_state.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from soil import Agent, Environment, Simulation, state
|
||||
|
||||
|
||||
class NoopAgent(Agent):
|
||||
num_calls = 0
|
||||
|
||||
@state(default=True)
|
||||
def unique(self):
|
||||
self.num_calls += 1
|
||||
|
||||
class NoopEnvironment(Environment):
|
||||
num_agents = 100
|
||||
|
||||
def init(self):
|
||||
self.add_agents(NoopAgent, k=self.num_agents)
|
||||
self.add_agent_reporter("num_calls")
|
||||
|
||||
|
||||
from _config import *
|
||||
|
||||
run_sim(model=NoopEnvironment)
|
20
benchmarks/noop/soil_step.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from soil import Agent, Environment, Simulation
|
||||
|
||||
|
||||
class NoopAgent(Agent):
|
||||
num_calls = 0
|
||||
|
||||
def step(self):
|
||||
self.num_calls += 1
|
||||
|
||||
class NoopEnvironment(Environment):
|
||||
num_agents = 100
|
||||
|
||||
def init(self):
|
||||
self.add_agents(NoopAgent, k=self.num_agents)
|
||||
self.add_agent_reporter("num_calls")
|
||||
|
||||
|
||||
from _config import *
|
||||
|
||||
run_sim(model=NoopEnvironment)
|
22
benchmarks/noop/soil_step_pqueue.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from soil import BaseAgent, Environment, Simulation, PQueueActivation
|
||||
|
||||
|
||||
class NoopAgent(BaseAgent):
|
||||
num_calls = 0
|
||||
|
||||
def step(self):
|
||||
self.num_calls += 1
|
||||
|
||||
class NoopEnvironment(Environment):
|
||||
num_agents = 100
|
||||
schedule_class = PQueueActivation
|
||||
|
||||
def init(self):
|
||||
self.add_agents(NoopAgent, k=self.num_agents)
|
||||
self.add_agent_reporter("num_calls")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from _config import *
|
||||
|
||||
run_sim(model=NoopEnvironment)
|
29
benchmarks/noop/soilent_async.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from soil import Agent, Environment, Simulation
|
||||
from soil.time import SoilentActivation
|
||||
|
||||
|
||||
class NoopAgent(Agent):
|
||||
num_calls = 0
|
||||
|
||||
async def step(self):
|
||||
while True:
|
||||
self.num_calls += 1
|
||||
# yield self.delay(1)
|
||||
await self.delay()
|
||||
|
||||
|
||||
class NoopEnvironment(Environment):
|
||||
num_agents = 100
|
||||
schedule_class = SoilentActivation
|
||||
|
||||
def init(self):
|
||||
self.add_agents(NoopAgent, k=self.num_agents)
|
||||
self.add_agent_reporter("num_calls")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from _config import *
|
||||
|
||||
res = run_sim(model=NoopEnvironment)
|
||||
for r in res:
|
||||
assert isinstance(r.schedule, SoilentActivation)
|
27
benchmarks/noop/soilent_async_pqueue.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from soil import Agent, Environment
|
||||
from soil.time import SoilentPQueueActivation
|
||||
|
||||
|
||||
class NoopAgent(Agent):
|
||||
num_calls = 0
|
||||
|
||||
async def step(self):
|
||||
while True:
|
||||
self.num_calls += 1
|
||||
await self.delay()
|
||||
|
||||
class NoopEnvironment(Environment):
|
||||
num_agents = 100
|
||||
schedule_class = SoilentPQueueActivation
|
||||
|
||||
def init(self):
|
||||
self.add_agents(NoopAgent, k=self.num_agents)
|
||||
self.add_agent_reporter("num_calls")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from _config import *
|
||||
|
||||
res = run_sim(model=NoopEnvironment)
|
||||
for r in res:
|
||||
assert isinstance(r.schedule, SoilentPQueueActivation)
|
28
benchmarks/noop/soilent_gens.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from soil import Agent, Environment, Simulation
|
||||
from soil.time import SoilentActivation
|
||||
|
||||
|
||||
class NoopAgent(Agent):
|
||||
num_calls = 0
|
||||
|
||||
def step(self):
|
||||
while True:
|
||||
self.num_calls += 1
|
||||
# yield self.delay(1)
|
||||
yield self.delay()
|
||||
|
||||
class NoopEnvironment(Environment):
|
||||
num_agents = 100
|
||||
schedule_class = SoilentActivation
|
||||
|
||||
def init(self):
|
||||
self.add_agents(NoopAgent, k=self.num_agents)
|
||||
self.add_agent_reporter("num_calls")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from _config import *
|
||||
|
||||
res = run_sim(model=NoopEnvironment)
|
||||
for r in res:
|
||||
assert isinstance(r.schedule, SoilentActivation)
|
28
benchmarks/noop/soilent_gens_pqueue.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from soil import Agent, Environment
|
||||
from soil.time import SoilentPQueueActivation
|
||||
|
||||
|
||||
class NoopAgent(Agent):
|
||||
num_calls = 0
|
||||
|
||||
def step(self):
|
||||
while True:
|
||||
self.num_calls += 1
|
||||
# yield self.delay(1)
|
||||
yield
|
||||
|
||||
class NoopEnvironment(Environment):
|
||||
num_agents = 100
|
||||
schedule_class = SoilentPQueueActivation
|
||||
|
||||
def init(self):
|
||||
self.add_agents(NoopAgent, k=self.num_agents)
|
||||
self.add_agent_reporter("num_calls")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from _config import *
|
||||
|
||||
res = run_sim(model=NoopEnvironment)
|
||||
for r in res:
|
||||
assert isinstance(r.schedule, SoilentPQueueActivation)
|
30
benchmarks/noop/soilent_state.py
Normal file
@@ -0,0 +1,30 @@
|
||||
from soil import Agent, Environment, Simulation, state
|
||||
from soil.time import SoilentActivation
|
||||
|
||||
|
||||
class NoopAgent(Agent):
|
||||
num_calls = 0
|
||||
|
||||
@state(default=True)
|
||||
async def unique(self):
|
||||
while True:
|
||||
self.num_calls += 1
|
||||
# yield self.delay(1)
|
||||
await self.delay()
|
||||
|
||||
|
||||
class NoopEnvironment(Environment):
|
||||
num_agents = 100
|
||||
schedule_class = SoilentActivation
|
||||
|
||||
def init(self):
|
||||
self.add_agents(NoopAgent, k=self.num_agents)
|
||||
self.add_agent_reporter("num_calls")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from _config import *
|
||||
|
||||
res = run_sim(model=NoopEnvironment)
|
||||
for r in res:
|
||||
assert isinstance(r.schedule, SoilentActivation)
|
24
benchmarks/noop/soilent_step.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from soil import BaseAgent, Environment, Simulation
|
||||
from soil.time import SoilentActivation
|
||||
|
||||
|
||||
class NoopAgent(BaseAgent):
|
||||
num_calls = 0
|
||||
|
||||
def step(self):
|
||||
self.num_calls += 1
|
||||
|
||||
class NoopEnvironment(Environment):
|
||||
num_agents = 100
|
||||
schedule_class = SoilentActivation
|
||||
|
||||
def init(self):
|
||||
self.add_agents(NoopAgent, k=self.num_agents)
|
||||
self.add_agent_reporter("num_calls")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from _config import *
|
||||
res = run_sim(model=NoopEnvironment)
|
||||
for r in res:
|
||||
assert isinstance(r.schedule, SoilentActivation)
|
24
benchmarks/noop/soilent_step_pqueue.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from soil import BaseAgent, Environment, Simulation
|
||||
from soil.time import SoilentPQueueActivation
|
||||
|
||||
|
||||
class NoopAgent(BaseAgent):
|
||||
num_calls = 0
|
||||
|
||||
def step(self):
|
||||
self.num_calls += 1
|
||||
|
||||
class NoopEnvironment(Environment):
|
||||
num_agents = 100
|
||||
schedule_class = SoilentPQueueActivation
|
||||
|
||||
def init(self):
|
||||
self.add_agents(NoopAgent, k=self.num_agents)
|
||||
self.add_agent_reporter("num_calls")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from _config import *
|
||||
res = run_sim(model=NoopEnvironment)
|
||||
for r in res:
|
||||
assert isinstance(r.schedule, SoilentPqueueActivation)
|
19
benchmarks/run.py
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/bin/env python
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(
|
||||
prog='Profiler for soil')
|
||||
parser.add_argument('--suffix', default=None)
|
||||
parser.add_argument('files', nargs="+")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
for fname in args.files:
|
||||
suffix = ("_" + args.suffix) if args.suffix else ""
|
||||
simname = f"{fname.replace('/', '-')}{suffix}"
|
||||
profpath = os.path.join("profs", simname + ".prof")
|
||||
|
||||
print(f"Running {fname} and saving profile to {profpath}")
|
||||
subprocess.call(["python", "-m", "cProfile", "-o", profpath, fname])
|
4
benchmarks/virusonnetwork.csv
Normal file
@@ -0,0 +1,4 @@
|
||||
command,mean,stddev,median,user,system,min,max,parameter_sim
|
||||
python virusonnetwork/mesa_basic.py,3.8381473157,0.0518143371442526,3.8475315791,3.873109219999999,0.55102658,3.7523016936,3.9095182436,mesa_basic.py
|
||||
python virusonnetwork/soil_step.py,3.2167258977000004,0.02337131987357665,3.2257620261,3.28374132,0.51343958,3.1792271306,3.2511521286000002,soil_step.py
|
||||
python virusonnetwork/soil_states.py,3.4908183217,0.03726734070349347,3.4912775086,3.5684004200000006,0.50416068,3.4272087936,3.5529207346000002,soil_states.py
|
|
38
benchmarks/virusonnetwork/_config.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import os
|
||||
from soil import simulation
|
||||
|
||||
NUM_AGENTS = int(os.environ.get('NUM_AGENTS', 100))
|
||||
NUM_ITERS = int(os.environ.get('NUM_ITERS', 10))
|
||||
MAX_STEPS = int(os.environ.get('MAX_STEPS', 500))
|
||||
|
||||
|
||||
def run_sim(model, **kwargs):
|
||||
from soil import Simulation
|
||||
opts = dict(model=model,
|
||||
dump=False,
|
||||
num_processes=1,
|
||||
parameters={'num_nodes': NUM_AGENTS,
|
||||
"avg_node_degree": 3,
|
||||
"initial_outbreak_size": 5,
|
||||
"virus_spread_chance": 0.25,
|
||||
"virus_check_frequency": 0.25,
|
||||
"recovery_chance": 0.3,
|
||||
"gain_resistance_chance": 0.1,
|
||||
},
|
||||
max_steps=MAX_STEPS,
|
||||
iterations=NUM_ITERS)
|
||||
opts.update(kwargs)
|
||||
its = Simulation(**opts).run()
|
||||
assert len(its) == NUM_ITERS
|
||||
|
||||
if not simulation._AVOID_RUNNING:
|
||||
ratios = list(it.resistant_susceptible_ratio for it in its)
|
||||
print("Max - Avg - Min ratio:", max(ratios), sum(ratios)/len(ratios), min(ratios))
|
||||
infected = list(it.number_infected for it in its)
|
||||
print("Max - Avg - Min infected:", max(infected), sum(infected)/len(infected), min(infected))
|
||||
|
||||
assert all((it.schedule.steps == MAX_STEPS or it.number_infected == 0) for it in its)
|
||||
assert all(sum([it.number_susceptible,
|
||||
it.number_infected,
|
||||
it.number_resistant]) == NUM_AGENTS for it in its)
|
||||
return its
|
180
benchmarks/virusonnetwork/mesa_basic.py
Normal file
@@ -0,0 +1,180 @@
|
||||
# Verbatim copy from mesa
|
||||
# https://github.com/projectmesa/mesa/blob/976ddfc8a1e5feaaf8007a7abaa9abc7093881a0/examples/virus_on_network/virus_on_network/model.py
|
||||
import math
|
||||
from enum import Enum
|
||||
import networkx as nx
|
||||
|
||||
import mesa
|
||||
|
||||
|
||||
class State(Enum):
|
||||
SUSCEPTIBLE = 0
|
||||
INFECTED = 1
|
||||
RESISTANT = 2
|
||||
|
||||
|
||||
def number_state(model, state):
|
||||
return sum(1 for a in model.grid.get_all_cell_contents() if a.state is state)
|
||||
|
||||
|
||||
def number_infected(model):
|
||||
return number_state(model, State.INFECTED)
|
||||
|
||||
|
||||
def number_susceptible(model):
|
||||
return number_state(model, State.SUSCEPTIBLE)
|
||||
|
||||
|
||||
def number_resistant(model):
|
||||
return number_state(model, State.RESISTANT)
|
||||
|
||||
|
||||
class VirusOnNetwork(mesa.Model):
|
||||
"""A virus model with some number of agents"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*args,
|
||||
num_nodes=10,
|
||||
avg_node_degree=3,
|
||||
initial_outbreak_size=1,
|
||||
virus_spread_chance=0.4,
|
||||
virus_check_frequency=0.4,
|
||||
recovery_chance=0.3,
|
||||
gain_resistance_chance=0.5,
|
||||
**kwargs,
|
||||
):
|
||||
|
||||
self.num_nodes = num_nodes
|
||||
prob = avg_node_degree / self.num_nodes
|
||||
self.G = nx.erdos_renyi_graph(n=self.num_nodes, p=prob)
|
||||
self.grid = mesa.space.NetworkGrid(self.G)
|
||||
self.schedule = mesa.time.RandomActivation(self)
|
||||
self.initial_outbreak_size = (
|
||||
initial_outbreak_size if initial_outbreak_size <= num_nodes else num_nodes
|
||||
)
|
||||
self.virus_spread_chance = virus_spread_chance
|
||||
self.virus_check_frequency = virus_check_frequency
|
||||
self.recovery_chance = recovery_chance
|
||||
self.gain_resistance_chance = gain_resistance_chance
|
||||
|
||||
self.datacollector = mesa.DataCollector(
|
||||
{
|
||||
"Ratio": "resistant_susceptible_ratio",
|
||||
"Infected": number_infected,
|
||||
"Susceptible": number_susceptible,
|
||||
"Resistant": number_resistant,
|
||||
}
|
||||
)
|
||||
|
||||
# Create agents
|
||||
for i, node in enumerate(self.G.nodes()):
|
||||
a = VirusAgent(
|
||||
i,
|
||||
self,
|
||||
State.SUSCEPTIBLE,
|
||||
self.virus_spread_chance,
|
||||
self.virus_check_frequency,
|
||||
self.recovery_chance,
|
||||
self.gain_resistance_chance,
|
||||
)
|
||||
self.schedule.add(a)
|
||||
# Add the agent to the node
|
||||
self.grid.place_agent(a, node)
|
||||
|
||||
# Infect some nodes
|
||||
infected_nodes = self.random.sample(list(self.G), self.initial_outbreak_size)
|
||||
for a in self.grid.get_cell_list_contents(infected_nodes):
|
||||
a.state = State.INFECTED
|
||||
|
||||
self.running = True
|
||||
self.datacollector.collect(self)
|
||||
|
||||
@property
|
||||
def number_susceptible(self):
|
||||
return number_susceptible(self)
|
||||
@property
|
||||
def number_resistant(self):
|
||||
return number_resistant(self)
|
||||
@property
|
||||
def number_infected(self):
|
||||
return number_infected(self)
|
||||
|
||||
@property
|
||||
def resistant_susceptible_ratio(self):
|
||||
try:
|
||||
return number_state(self, State.RESISTANT) / number_state(
|
||||
self, State.SUSCEPTIBLE
|
||||
)
|
||||
except ZeroDivisionError:
|
||||
return math.inf
|
||||
|
||||
def step(self):
|
||||
self.schedule.step()
|
||||
# collect data
|
||||
self.datacollector.collect(self)
|
||||
|
||||
def run_model(self, n):
|
||||
for i in range(n):
|
||||
self.step()
|
||||
|
||||
|
||||
class VirusAgent(mesa.Agent):
|
||||
def __init__(
|
||||
self,
|
||||
unique_id,
|
||||
model,
|
||||
initial_state,
|
||||
virus_spread_chance,
|
||||
virus_check_frequency,
|
||||
recovery_chance,
|
||||
gain_resistance_chance,
|
||||
):
|
||||
super().__init__(unique_id, model)
|
||||
|
||||
self.state = initial_state
|
||||
|
||||
self.virus_spread_chance = virus_spread_chance
|
||||
self.virus_check_frequency = virus_check_frequency
|
||||
self.recovery_chance = recovery_chance
|
||||
self.gain_resistance_chance = gain_resistance_chance
|
||||
|
||||
def try_to_infect_neighbors(self):
|
||||
neighbors_nodes = self.model.grid.get_neighbors(self.pos, include_center=False)
|
||||
susceptible_neighbors = [
|
||||
agent
|
||||
for agent in self.model.grid.get_cell_list_contents(neighbors_nodes)
|
||||
if agent.state is State.SUSCEPTIBLE
|
||||
]
|
||||
for a in susceptible_neighbors:
|
||||
if self.random.random() < self.virus_spread_chance:
|
||||
a.state = State.INFECTED
|
||||
|
||||
def try_gain_resistance(self):
|
||||
if self.random.random() < self.gain_resistance_chance:
|
||||
self.state = State.RESISTANT
|
||||
|
||||
def try_remove_infection(self):
|
||||
# Try to remove
|
||||
if self.random.random() < self.recovery_chance:
|
||||
# Success
|
||||
self.state = State.SUSCEPTIBLE
|
||||
self.try_gain_resistance()
|
||||
else:
|
||||
# Failed
|
||||
self.state = State.INFECTED
|
||||
|
||||
def try_check_situation(self):
|
||||
if self.random.random() < self.virus_check_frequency:
|
||||
# Checking...
|
||||
if self.state is State.INFECTED:
|
||||
self.try_remove_infection()
|
||||
|
||||
def step(self):
|
||||
if self.state is State.INFECTED:
|
||||
self.try_to_infect_neighbors()
|
||||
self.try_check_situation()
|
||||
|
||||
|
||||
from _config import run_sim
|
||||
run_sim(model=VirusOnNetwork)
|
91
benchmarks/virusonnetwork/soil_states.py
Normal file
@@ -0,0 +1,91 @@
|
||||
# Verbatim copy from mesa
|
||||
# https://github.com/projectmesa/mesa/blob/976ddfc8a1e5feaaf8007a7abaa9abc7093881a0/examples/virus_on_network/virus_on_network/model.py
|
||||
import math
|
||||
from enum import Enum
|
||||
import networkx as nx
|
||||
|
||||
from soil import *
|
||||
|
||||
|
||||
class VirusOnNetwork(Environment):
|
||||
"""A virus model with some number of agents"""
|
||||
num_nodes = 10
|
||||
avg_node_degree = 3
|
||||
initial_outbreak_size = 1
|
||||
virus_spread_chance = 0.4
|
||||
virus_check_frequency = 0.4
|
||||
recovery_chance = 0
|
||||
gain_resistance_chance = 0
|
||||
|
||||
def init(self):
|
||||
prob = self.avg_node_degree / self.num_nodes
|
||||
# Use internal seed with the networkx generator
|
||||
self.create_network(generator=nx.erdos_renyi_graph, n=self.num_nodes, p=prob)
|
||||
|
||||
self.initial_outbreak_size = min(self.initial_outbreak_size, self.num_nodes)
|
||||
self.populate_network(VirusAgent)
|
||||
|
||||
# Infect some nodes
|
||||
infected_nodes = self.random.sample(list(self.G), self.initial_outbreak_size)
|
||||
for a in self.agents(node_id=infected_nodes):
|
||||
a.set_state(VirusAgent.infected)
|
||||
assert self.number_infected == self.initial_outbreak_size
|
||||
|
||||
def step(self):
|
||||
super().step()
|
||||
|
||||
@report
|
||||
@property
|
||||
def resistant_susceptible_ratio(self):
|
||||
try:
|
||||
return self.number_resistant / self.number_susceptible
|
||||
except ZeroDivisionError:
|
||||
return math.inf
|
||||
|
||||
@report
|
||||
@property
|
||||
def number_infected(self):
|
||||
return self.count_agents(state_id=VirusAgent.infected.id)
|
||||
|
||||
@report
|
||||
@property
|
||||
def number_susceptible(self):
|
||||
return self.count_agents(state_id=VirusAgent.susceptible.id)
|
||||
|
||||
@report
|
||||
@property
|
||||
def number_resistant(self):
|
||||
return self.count_agents(state_id=VirusAgent.resistant.id)
|
||||
|
||||
|
||||
class VirusAgent(Agent):
|
||||
virus_spread_chance = None # Inherit from model
|
||||
virus_check_frequency = None # Inherit from model
|
||||
recovery_chance = None # Inherit from model
|
||||
gain_resistance_chance = None # Inherit from model
|
||||
|
||||
@state(default=True)
|
||||
async def susceptible(self):
|
||||
await self.received()
|
||||
return self.infected
|
||||
|
||||
@state
|
||||
def infected(self):
|
||||
susceptible_neighbors = self.get_neighbors(state_id=self.susceptible.id)
|
||||
for a in susceptible_neighbors:
|
||||
if self.prob(self.virus_spread_chance):
|
||||
a.tell(True, sender=self)
|
||||
if self.prob(self.virus_check_frequency):
|
||||
if self.prob(self.recovery_chance):
|
||||
if self.prob(self.gain_resistance_chance):
|
||||
return self.resistant
|
||||
else:
|
||||
return self.susceptible
|
||||
|
||||
@state
|
||||
def resistant(self):
|
||||
return self.at(INFINITY)
|
||||
|
||||
|
||||
from _config import run_sim
|
||||
run_sim(model=VirusOnNetwork)
|
104
benchmarks/virusonnetwork/soil_step.py
Normal file
@@ -0,0 +1,104 @@
|
||||
# Verbatim copy from mesa
|
||||
# https://github.com/projectmesa/mesa/blob/976ddfc8a1e5feaaf8007a7abaa9abc7093881a0/examples/virus_on_network/virus_on_network/model.py
|
||||
import math
|
||||
from enum import Enum
|
||||
import networkx as nx
|
||||
|
||||
from soil import *
|
||||
|
||||
|
||||
class State(Enum):
|
||||
SUSCEPTIBLE = 0
|
||||
INFECTED = 1
|
||||
RESISTANT = 2
|
||||
|
||||
|
||||
class VirusOnNetwork(Environment):
|
||||
"""A virus model with some number of agents"""
|
||||
num_nodes = 10
|
||||
avg_node_degree = 3
|
||||
initial_outbreak_size = 1
|
||||
virus_spread_chance = 0.4
|
||||
virus_check_frequency = 0.4
|
||||
recovery_chance = 0
|
||||
gain_resistance_chance = 0
|
||||
|
||||
def init(self):
|
||||
prob = self.avg_node_degree / self.num_nodes
|
||||
# Use internal seed with the networkx generator
|
||||
self.create_network(generator=nx.erdos_renyi_graph, n=self.num_nodes, p=prob)
|
||||
|
||||
self.initial_outbreak_size = min(self.initial_outbreak_size, self.num_nodes)
|
||||
self.populate_network(VirusAgent)
|
||||
|
||||
# Infect some nodes
|
||||
infected_nodes = self.random.sample(list(self.G), self.initial_outbreak_size)
|
||||
for a in self.agents(node_id=infected_nodes):
|
||||
a.status = State.INFECTED
|
||||
assert self.number_infected == self.initial_outbreak_size
|
||||
|
||||
@report
|
||||
@property
|
||||
def resistant_susceptible_ratio(self):
|
||||
try:
|
||||
return self.number_resistant / self.number_susceptible
|
||||
except ZeroDivisionError:
|
||||
return math.inf
|
||||
|
||||
@report
|
||||
@property
|
||||
def number_infected(self):
|
||||
return self.count_agents(status=State.INFECTED)
|
||||
|
||||
@report
|
||||
@property
|
||||
def number_susceptible(self):
|
||||
return self.count_agents(status=State.SUSCEPTIBLE)
|
||||
|
||||
@report
|
||||
@property
|
||||
def number_resistant(self):
|
||||
return self.count_agents(status=State.RESISTANT)
|
||||
|
||||
|
||||
|
||||
class VirusAgent(Agent):
|
||||
status = State.SUSCEPTIBLE
|
||||
virus_spread_chance = None # Inherit from model
|
||||
virus_check_frequency = None # Inherit from model
|
||||
recovery_chance = None # Inherit from model
|
||||
gain_resistance_chance = None # Inherit from model
|
||||
|
||||
def try_to_infect_neighbors(self):
|
||||
susceptible_neighbors = self.get_neighbors(status=State.SUSCEPTIBLE)
|
||||
for a in susceptible_neighbors:
|
||||
if self.prob(self.virus_spread_chance):
|
||||
a.status = State.INFECTED
|
||||
|
||||
def try_gain_resistance(self):
|
||||
if self.prob(self.gain_resistance_chance):
|
||||
self.status = State.RESISTANT
|
||||
return self.at(INFINITY)
|
||||
|
||||
def try_remove_infection(self):
|
||||
# Try to remove
|
||||
if self.prob(self.recovery_chance):
|
||||
# Success
|
||||
self.status = State.SUSCEPTIBLE
|
||||
return self.try_gain_resistance()
|
||||
|
||||
def try_check_situation(self):
|
||||
if self.prob(self.virus_check_frequency):
|
||||
# Checking...
|
||||
if self.status is State.INFECTED:
|
||||
return self.try_remove_infection()
|
||||
|
||||
def step(self):
|
||||
if self.status is State.INFECTED:
|
||||
self.try_to_infect_neighbors()
|
||||
return self.try_check_situation()
|
||||
|
||||
|
||||
|
||||
from _config import run_sim
|
||||
run_sim(model=VirusOnNetwork)
|
BIN
clase_base.pyc
12
docker-compose.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
version: '3'
|
||||
services:
|
||||
dev:
|
||||
build: .
|
||||
environment:
|
||||
PYTHONDONTWRITEBYTECODE: 1
|
||||
volumes:
|
||||
- .:/usr/src/app
|
||||
tty: true
|
||||
entrypoint: /bin/bash
|
||||
ports:
|
||||
- '8001:8001'
|
BIN
docs/_build/doctrees/demo.doctree
vendored
BIN
docs/_build/doctrees/environment.pickle
vendored
BIN
docs/_build/doctrees/index.doctree
vendored
BIN
docs/_build/doctrees/installation.doctree
vendored
BIN
docs/_build/doctrees/models.doctree
vendored
BIN
docs/_build/doctrees/usage.doctree
vendored
4
docs/_build/html/.buildinfo
vendored
@@ -1,4 +0,0 @@
|
||||
# Sphinx build info version 1
|
||||
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
|
||||
config: d241ad8149638a17f61def88be87f400
|
||||
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
0
docs/_build/html/_sources/demo.rst.txt
vendored
25
docs/_build/html/_sources/index.rst.txt
vendored
@@ -1,25 +0,0 @@
|
||||
.. Soil documentation master file, created by
|
||||
sphinx-quickstart on Tue Apr 25 12:48:56 2017.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to Soil's documentation!
|
||||
================================
|
||||
|
||||
Soil is an Agent-based Social Simulator in Python for modelling and simulation of Social Networks.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Learn more about soil:
|
||||
|
||||
installation
|
||||
usage
|
||||
models
|
||||
|
||||
|
||||
|
||||
.. Indices and tables
|
||||
==================
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
@@ -1,7 +0,0 @@
|
||||
Installation
|
||||
------------
|
||||
The latest version can be installed through GitLab.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
git clone https://lab.cluster.gsi.dit.upm.es/soil/soil.git
|
112
docs/_build/html/_sources/models.rst.txt
vendored
@@ -1,112 +0,0 @@
|
||||
Developing new models
|
||||
---------------------
|
||||
This document describes how to develop a new analysis model.
|
||||
|
||||
What is a model?
|
||||
================
|
||||
|
||||
A model defines the behaviour of the agents with a view to assessing their effects on the system as a whole.
|
||||
In practice, a model consists of at least two parts:
|
||||
|
||||
* Python module: the actual code that describes the behaviour.
|
||||
* Setting up the variables in the Settings JSON file.
|
||||
|
||||
This separation allows us to run the simulation with different agents.
|
||||
|
||||
Models Code
|
||||
===========
|
||||
|
||||
All the models are imported to the main file. The initialization look like this:
|
||||
|
||||
.. code:: python
|
||||
|
||||
import settings
|
||||
|
||||
networkStatus = {} # Dict that will contain the status of every agent in the network
|
||||
|
||||
sentimentCorrelationNodeArray = []
|
||||
for x in range(0, settings.network_params["number_of_nodes"]):
|
||||
sentimentCorrelationNodeArray.append({'id': x})
|
||||
# Initialize agent states. Let's assume everyone is normal.
|
||||
init_states = [{'id': 0, } for _ in range(settings.network_params["number_of_nodes"])]
|
||||
# add keys as as necessary, but "id" must always refer to that state category
|
||||
|
||||
A new model have to inherit the BaseBehaviour class which is in the same module.
|
||||
There are two basics methods:
|
||||
|
||||
* __init__
|
||||
* step: used to define the behaviour over time.
|
||||
|
||||
Variable Initialization
|
||||
=======================
|
||||
|
||||
The different parameters of the model have to be initialize in the Simulation Settings JSON file which will be
|
||||
passed as a parameter to the simulation.
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"agent": ["SISaModel","ControlModelM2"],
|
||||
|
||||
"neutral_discontent_spon_prob": 0.04,
|
||||
"neutral_discontent_infected_prob": 0.04,
|
||||
"neutral_content_spon_prob": 0.18,
|
||||
"neutral_content_infected_prob": 0.02,
|
||||
|
||||
"discontent_neutral": 0.13,
|
||||
"discontent_content": 0.07,
|
||||
"variance_d_c": 0.02,
|
||||
|
||||
"content_discontent": 0.009,
|
||||
"variance_c_d": 0.003,
|
||||
"content_neutral": 0.088,
|
||||
|
||||
"standard_variance": 0.055,
|
||||
|
||||
|
||||
"prob_neutral_making_denier": 0.035,
|
||||
|
||||
"prob_infect": 0.075,
|
||||
|
||||
"prob_cured_healing_infected": 0.035,
|
||||
"prob_cured_vaccinate_neutral": 0.035,
|
||||
|
||||
"prob_vaccinated_healing_infected": 0.035,
|
||||
"prob_vaccinated_vaccinate_neutral": 0.035,
|
||||
"prob_generate_anti_rumor": 0.035
|
||||
}
|
||||
|
||||
In this file you will also define the models you are going to simulate. You can simulate as many models as you want.
|
||||
The simulation returns one result for each model, executing each model separately. For the usage, see :doc:`usage`.
|
||||
|
||||
Example Model
|
||||
=============
|
||||
|
||||
In this section, we will implement a Sentiment Correlation Model.
|
||||
|
||||
The class would look like this:
|
||||
|
||||
.. code:: python
|
||||
|
||||
from ..BaseBehaviour import *
|
||||
from .. import sentimentCorrelationNodeArray
|
||||
|
||||
class SentimentCorrelationModel(BaseBehaviour):
|
||||
|
||||
def __init__(self, environment=None, agent_id=0, state=()):
|
||||
super().__init__(environment=environment, agent_id=agent_id, state=state)
|
||||
self.outside_effects_prob = environment.environment_params['outside_effects_prob']
|
||||
self.anger_prob = environment.environment_params['anger_prob']
|
||||
self.joy_prob = environment.environment_params['joy_prob']
|
||||
self.sadness_prob = environment.environment_params['sadness_prob']
|
||||
self.disgust_prob = environment.environment_params['disgust_prob']
|
||||
self.time_awareness = []
|
||||
for i in range(4): # In this model we have 4 sentiments
|
||||
self.time_awareness.append(0) # 0-> Anger, 1-> joy, 2->sadness, 3 -> disgust
|
||||
sentimentCorrelationNodeArray[self.id][self.env.now] = 0
|
||||
|
||||
def step(self, now):
|
||||
self.behaviour() # Method which define the behaviour
|
||||
super().step(now)
|
||||
|
||||
The variables will be modified by the user, so you have to include them in the Simulation Settings JSON file.
|
99
docs/_build/html/_sources/usage.rst.txt
vendored
@@ -1,99 +0,0 @@
|
||||
Usage
|
||||
-----
|
||||
|
||||
First of all, you need to install the package. See :doc:`installation` for installation instructions.
|
||||
|
||||
Simulation Settings
|
||||
===================
|
||||
|
||||
Once installed, before running a simulation, you need to configure it.
|
||||
|
||||
* In the Settings JSON file you will find the configuration of the network.
|
||||
|
||||
.. code:: python
|
||||
|
||||
{
|
||||
"network_type": 1,
|
||||
"number_of_nodes": 1000,
|
||||
"max_time": 50,
|
||||
"num_trials": 1,
|
||||
"timeout": 2
|
||||
}
|
||||
|
||||
* In the Settings JSON file, you will also find the configuration of the models.
|
||||
|
||||
Network Types
|
||||
=============
|
||||
|
||||
There are three types of network implemented, but you could add more.
|
||||
|
||||
.. code:: python
|
||||
|
||||
if settings.network_type == 0:
|
||||
G = nx.complete_graph(settings.number_of_nodes)
|
||||
if settings.network_type == 1:
|
||||
G = nx.barabasi_albert_graph(settings.number_of_nodes, 10)
|
||||
if settings.network_type == 2:
|
||||
G = nx.margulis_gabber_galil_graph(settings.number_of_nodes, None)
|
||||
# More types of networks can be added here
|
||||
|
||||
Models Settings
|
||||
===============
|
||||
|
||||
After having configured the simulation, the next step is setting up the variables of the models.
|
||||
For this, you will need to modify the Settings JSON file again.
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"agent": ["SISaModel","ControlModelM2"],
|
||||
|
||||
"neutral_discontent_spon_prob": 0.04,
|
||||
"neutral_discontent_infected_prob": 0.04,
|
||||
"neutral_content_spon_prob": 0.18,
|
||||
"neutral_content_infected_prob": 0.02,
|
||||
|
||||
"discontent_neutral": 0.13,
|
||||
"discontent_content": 0.07,
|
||||
"variance_d_c": 0.02,
|
||||
|
||||
"content_discontent": 0.009,
|
||||
"variance_c_d": 0.003,
|
||||
"content_neutral": 0.088,
|
||||
|
||||
"standard_variance": 0.055,
|
||||
|
||||
|
||||
"prob_neutral_making_denier": 0.035,
|
||||
|
||||
"prob_infect": 0.075,
|
||||
|
||||
"prob_cured_healing_infected": 0.035,
|
||||
"prob_cured_vaccinate_neutral": 0.035,
|
||||
|
||||
"prob_vaccinated_healing_infected": 0.035,
|
||||
"prob_vaccinated_vaccinate_neutral": 0.035,
|
||||
"prob_generate_anti_rumor": 0.035
|
||||
}
|
||||
|
||||
In this file you will define the different models you are going to simulate. You can simulate as many models
|
||||
as you want. Each model will be simulated separately.
|
||||
|
||||
After setting up the models, you have to initialize the parameters of each one. You will find the parameters needed
|
||||
in the documentation of each model.
|
||||
|
||||
Parameter validation will fail if a required parameter without a default has not been provided.
|
||||
|
||||
Running the Simulation
|
||||
======================
|
||||
|
||||
After setting all the configuration, you will be able to run the simulation. All you need to do is execute:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
python3 soil.py
|
||||
|
||||
The simulation will return a dynamic graph .gexf file which could be visualized with
|
||||
`Gephi <https://gephi.org/users/download/>`__.
|
||||
|
||||
It will also return one .png picture for each model simulated.
|
BIN
docs/_build/html/_static/ajax-loader.gif
vendored
Before Width: | Height: | Size: 673 B |
693
docs/_build/html/_static/alabaster.css
vendored
@@ -1,693 +0,0 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@import url("basic.css");
|
||||
|
||||
/* -- page layout ----------------------------------------------------------- */
|
||||
|
||||
body {
|
||||
font-family: 'goudy old style', 'minion pro', 'bell mt', Georgia, 'Hiragino Mincho Pro', serif;
|
||||
font-size: 17px;
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
|
||||
div.document {
|
||||
width: 940px;
|
||||
margin: 30px auto 0 auto;
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
float: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin: 0 0 0 220px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
width: 220px;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 1px solid #B1B4B6;
|
||||
}
|
||||
|
||||
div.body {
|
||||
background-color: #fff;
|
||||
color: #3E4349;
|
||||
padding: 0 30px 0 30px;
|
||||
}
|
||||
|
||||
div.body > .section {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
div.footer {
|
||||
width: 940px;
|
||||
margin: 20px auto 30px auto;
|
||||
font-size: 14px;
|
||||
color: #888;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
div.footer a {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
p.caption {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
|
||||
div.relations {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
div.sphinxsidebar a {
|
||||
color: #444;
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dotted #999;
|
||||
}
|
||||
|
||||
div.sphinxsidebar a:hover {
|
||||
border-bottom: 1px solid #999;
|
||||
}
|
||||
|
||||
div.sphinxsidebarwrapper {
|
||||
padding: 18px 10px;
|
||||
}
|
||||
|
||||
div.sphinxsidebarwrapper p.logo {
|
||||
padding: 0;
|
||||
margin: -10px 0 0 0px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.sphinxsidebarwrapper h1.logo {
|
||||
margin-top: -10px;
|
||||
text-align: center;
|
||||
margin-bottom: 5px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
div.sphinxsidebarwrapper h1.logo-name {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
div.sphinxsidebarwrapper p.blurb {
|
||||
margin-top: 0;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3,
|
||||
div.sphinxsidebar h4 {
|
||||
font-family: 'Garamond', 'Georgia', serif;
|
||||
color: #444;
|
||||
font-size: 24px;
|
||||
font-weight: normal;
|
||||
margin: 0 0 5px 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h4 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3 a {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
div.sphinxsidebar p.logo a,
|
||||
div.sphinxsidebar h3 a,
|
||||
div.sphinxsidebar p.logo a:hover,
|
||||
div.sphinxsidebar h3 a:hover {
|
||||
border: none;
|
||||
}
|
||||
|
||||
div.sphinxsidebar p {
|
||||
color: #555;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul {
|
||||
margin: 10px 0;
|
||||
padding: 0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul li.toctree-l1 > a {
|
||||
font-size: 120%;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul li.toctree-l2 > a {
|
||||
font-size: 110%;
|
||||
}
|
||||
|
||||
div.sphinxsidebar input {
|
||||
border: 1px solid #CCC;
|
||||
font-family: 'goudy old style', 'minion pro', 'bell mt', Georgia, 'Hiragino Mincho Pro', serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
div.sphinxsidebar hr {
|
||||
border: none;
|
||||
height: 1px;
|
||||
color: #AAA;
|
||||
background: #AAA;
|
||||
|
||||
text-align: left;
|
||||
margin-left: 0;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
/* -- body styles ----------------------------------------------------------- */
|
||||
|
||||
a {
|
||||
color: #004B6B;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #6D4100;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div.body h1,
|
||||
div.body h2,
|
||||
div.body h3,
|
||||
div.body h4,
|
||||
div.body h5,
|
||||
div.body h6 {
|
||||
font-family: 'Garamond', 'Georgia', serif;
|
||||
font-weight: normal;
|
||||
margin: 30px 0px 10px 0px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
|
||||
div.body h2 { font-size: 180%; }
|
||||
div.body h3 { font-size: 150%; }
|
||||
div.body h4 { font-size: 130%; }
|
||||
div.body h5 { font-size: 100%; }
|
||||
div.body h6 { font-size: 100%; }
|
||||
|
||||
a.headerlink {
|
||||
color: #DDD;
|
||||
padding: 0 4px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a.headerlink:hover {
|
||||
color: #444;
|
||||
background: #EAEAEA;
|
||||
}
|
||||
|
||||
div.body p, div.body dd, div.body li {
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
div.admonition {
|
||||
margin: 20px 0px;
|
||||
padding: 10px 30px;
|
||||
background-color: #EEE;
|
||||
border: 1px solid #CCC;
|
||||
}
|
||||
|
||||
div.admonition tt.xref, div.admonition code.xref, div.admonition a tt {
|
||||
background-color: ;
|
||||
border-bottom: 1px solid #fafafa;
|
||||
}
|
||||
|
||||
dd div.admonition {
|
||||
margin-left: -60px;
|
||||
padding-left: 60px;
|
||||
}
|
||||
|
||||
div.admonition p.admonition-title {
|
||||
font-family: 'Garamond', 'Georgia', serif;
|
||||
font-weight: normal;
|
||||
font-size: 24px;
|
||||
margin: 0 0 10px 0;
|
||||
padding: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
div.admonition p.last {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.highlight {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
dt:target, .highlight {
|
||||
background: #FAF3E8;
|
||||
}
|
||||
|
||||
div.warning {
|
||||
background-color: #FCC;
|
||||
border: 1px solid #FAA;
|
||||
}
|
||||
|
||||
div.danger {
|
||||
background-color: #FCC;
|
||||
border: 1px solid #FAA;
|
||||
-moz-box-shadow: 2px 2px 4px #D52C2C;
|
||||
-webkit-box-shadow: 2px 2px 4px #D52C2C;
|
||||
box-shadow: 2px 2px 4px #D52C2C;
|
||||
}
|
||||
|
||||
div.error {
|
||||
background-color: #FCC;
|
||||
border: 1px solid #FAA;
|
||||
-moz-box-shadow: 2px 2px 4px #D52C2C;
|
||||
-webkit-box-shadow: 2px 2px 4px #D52C2C;
|
||||
box-shadow: 2px 2px 4px #D52C2C;
|
||||
}
|
||||
|
||||
div.caution {
|
||||
background-color: #FCC;
|
||||
border: 1px solid #FAA;
|
||||
}
|
||||
|
||||
div.attention {
|
||||
background-color: #FCC;
|
||||
border: 1px solid #FAA;
|
||||
}
|
||||
|
||||
div.important {
|
||||
background-color: #EEE;
|
||||
border: 1px solid #CCC;
|
||||
}
|
||||
|
||||
div.note {
|
||||
background-color: #EEE;
|
||||
border: 1px solid #CCC;
|
||||
}
|
||||
|
||||
div.tip {
|
||||
background-color: #EEE;
|
||||
border: 1px solid #CCC;
|
||||
}
|
||||
|
||||
div.hint {
|
||||
background-color: #EEE;
|
||||
border: 1px solid #CCC;
|
||||
}
|
||||
|
||||
div.seealso {
|
||||
background-color: #EEE;
|
||||
border: 1px solid #CCC;
|
||||
}
|
||||
|
||||
div.topic {
|
||||
background-color: #EEE;
|
||||
}
|
||||
|
||||
p.admonition-title {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
p.admonition-title:after {
|
||||
content: ":";
|
||||
}
|
||||
|
||||
pre, tt, code {
|
||||
font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.hll {
|
||||
background-color: #FFC;
|
||||
margin: 0 -12px;
|
||||
padding: 0 12px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
img.screenshot {
|
||||
}
|
||||
|
||||
tt.descname, tt.descclassname, code.descname, code.descclassname {
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
tt.descname, code.descname {
|
||||
padding-right: 0.08em;
|
||||
}
|
||||
|
||||
img.screenshot {
|
||||
-moz-box-shadow: 2px 2px 4px #EEE;
|
||||
-webkit-box-shadow: 2px 2px 4px #EEE;
|
||||
box-shadow: 2px 2px 4px #EEE;
|
||||
}
|
||||
|
||||
table.docutils {
|
||||
border: 1px solid #888;
|
||||
-moz-box-shadow: 2px 2px 4px #EEE;
|
||||
-webkit-box-shadow: 2px 2px 4px #EEE;
|
||||
box-shadow: 2px 2px 4px #EEE;
|
||||
}
|
||||
|
||||
table.docutils td, table.docutils th {
|
||||
border: 1px solid #888;
|
||||
padding: 0.25em 0.7em;
|
||||
}
|
||||
|
||||
table.field-list, table.footnote {
|
||||
border: none;
|
||||
-moz-box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
table.footnote {
|
||||
margin: 15px 0;
|
||||
width: 100%;
|
||||
border: 1px solid #EEE;
|
||||
background: #FDFDFD;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
table.footnote + table.footnote {
|
||||
margin-top: -15px;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
table.field-list th {
|
||||
padding: 0 0.8em 0 0;
|
||||
}
|
||||
|
||||
table.field-list td {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
table.field-list p {
|
||||
margin-bottom: 0.8em;
|
||||
}
|
||||
|
||||
table.footnote td.label {
|
||||
width: .1px;
|
||||
padding: 0.3em 0 0.3em 0.5em;
|
||||
}
|
||||
|
||||
table.footnote td {
|
||||
padding: 0.3em 0.5em;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
dl dd {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 0 30px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
/* Matches the 30px from the narrow-screen "li > ul" selector below */
|
||||
margin: 10px 0 10px 30px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: #EEE;
|
||||
padding: 7px 30px;
|
||||
margin: 15px 0px;
|
||||
line-height: 1.3em;
|
||||
}
|
||||
|
||||
div.viewcode-block:target {
|
||||
background: #ffd;
|
||||
}
|
||||
|
||||
dl pre, blockquote pre, li pre {
|
||||
margin-left: 0;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
dl dl pre {
|
||||
margin-left: -90px;
|
||||
padding-left: 90px;
|
||||
}
|
||||
|
||||
tt, code {
|
||||
background-color: #ecf0f3;
|
||||
color: #222;
|
||||
/* padding: 1px 2px; */
|
||||
}
|
||||
|
||||
tt.xref, code.xref, a tt {
|
||||
background-color: #FBFBFB;
|
||||
border-bottom: 1px solid #fff;
|
||||
}
|
||||
|
||||
a.reference {
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dotted #004B6B;
|
||||
}
|
||||
|
||||
/* Don't put an underline on images */
|
||||
a.image-reference, a.image-reference:hover {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
a.reference:hover {
|
||||
border-bottom: 1px solid #6D4100;
|
||||
}
|
||||
|
||||
a.footnote-reference {
|
||||
text-decoration: none;
|
||||
font-size: 0.7em;
|
||||
vertical-align: top;
|
||||
border-bottom: 1px dotted #004B6B;
|
||||
}
|
||||
|
||||
a.footnote-reference:hover {
|
||||
border-bottom: 1px solid #6D4100;
|
||||
}
|
||||
|
||||
a:hover tt, a:hover code {
|
||||
background: #EEE;
|
||||
}
|
||||
|
||||
|
||||
@media screen and (max-width: 870px) {
|
||||
|
||||
div.sphinxsidebar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.document {
|
||||
width: 100%;
|
||||
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
margin-left: 0;
|
||||
margin-top: 0;
|
||||
margin-right: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin-top: 0;
|
||||
margin-right: 0;
|
||||
margin-bottom: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
li > ul {
|
||||
/* Matches the 30px from the "ul, ol" selector above */
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
.document {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.bodywrapper {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.github {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@media screen and (max-width: 875px) {
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 20px 30px;
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
float: none;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
display: block;
|
||||
float: none;
|
||||
width: 102.5%;
|
||||
margin: 50px -30px -20px -30px;
|
||||
padding: 10px 20px;
|
||||
background: #333;
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,
|
||||
div.sphinxsidebar h3 a {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
div.sphinxsidebar a {
|
||||
color: #AAA;
|
||||
}
|
||||
|
||||
div.sphinxsidebar p.logo {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.document {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.body {
|
||||
min-height: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.rtd_doc_footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.document {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.github {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* misc. */
|
||||
|
||||
.revsys-inline {
|
||||
display: none!important;
|
||||
}
|
||||
|
||||
/* Make nested-list/multi-paragraph items look better in Releases changelog
|
||||
* pages. Without this, docutils' magical list fuckery causes inconsistent
|
||||
* formatting between different release sub-lists.
|
||||
*/
|
||||
div#changelog > div.section > ul > li > p:only-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Hide fugly table cell borders in ..bibliography:: directive output */
|
||||
table.docutils.citation, table.docutils.citation td, table.docutils.citation th {
|
||||
border: none;
|
||||
/* Below needed in some edge cases; if not applied, bottom shadows appear */
|
||||
-moz-box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
632
docs/_build/html/_static/basic.css
vendored
@@ -1,632 +0,0 @@
|
||||
/*
|
||||
* basic.css
|
||||
* ~~~~~~~~~
|
||||
*
|
||||
* Sphinx stylesheet -- basic theme.
|
||||
*
|
||||
* :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
/* -- main layout ----------------------------------------------------------- */
|
||||
|
||||
div.clearer {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* -- relbar ---------------------------------------------------------------- */
|
||||
|
||||
div.related {
|
||||
width: 100%;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
div.related h3 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.related ul {
|
||||
margin: 0;
|
||||
padding: 0 0 0 10px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
div.related li {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
div.related li.right {
|
||||
float: right;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
/* -- sidebar --------------------------------------------------------------- */
|
||||
|
||||
div.sphinxsidebarwrapper {
|
||||
padding: 10px 5px 0 10px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
float: left;
|
||||
width: 230px;
|
||||
margin-left: -100%;
|
||||
font-size: 90%;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap : break-word;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul ul,
|
||||
div.sphinxsidebar ul.want-points {
|
||||
margin-left: 20px;
|
||||
list-style: square;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul ul {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar form {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar input {
|
||||
border: 1px solid #98dbcc;
|
||||
font-family: sans-serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
div.sphinxsidebar #searchbox input[type="text"] {
|
||||
width: 170px;
|
||||
}
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* -- search page ----------------------------------------------------------- */
|
||||
|
||||
ul.search {
|
||||
margin: 10px 0 0 20px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul.search li {
|
||||
padding: 5px 0 5px 20px;
|
||||
background-image: url(file.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0 7px;
|
||||
}
|
||||
|
||||
ul.search li a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
ul.search li div.context {
|
||||
color: #888;
|
||||
margin: 2px 0 0 30px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
ul.keywordmatches li.goodmatch a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* -- index page ------------------------------------------------------------ */
|
||||
|
||||
table.contentstable {
|
||||
width: 90%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
table.contentstable p.biglink {
|
||||
line-height: 150%;
|
||||
}
|
||||
|
||||
a.biglink {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
span.linkdescr {
|
||||
font-style: italic;
|
||||
padding-top: 5px;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
/* -- general index --------------------------------------------------------- */
|
||||
|
||||
table.indextable {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table.indextable td {
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
table.indextable ul {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
table.indextable > tbody > tr > td > ul {
|
||||
padding-left: 0em;
|
||||
}
|
||||
|
||||
table.indextable tr.pcap {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
table.indextable tr.cap {
|
||||
margin-top: 10px;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
img.toggler {
|
||||
margin-right: 3px;
|
||||
margin-top: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.modindex-jumpbox {
|
||||
border-top: 1px solid #ddd;
|
||||
border-bottom: 1px solid #ddd;
|
||||
margin: 1em 0 1em 0;
|
||||
padding: 0.4em;
|
||||
}
|
||||
|
||||
div.genindex-jumpbox {
|
||||
border-top: 1px solid #ddd;
|
||||
border-bottom: 1px solid #ddd;
|
||||
margin: 1em 0 1em 0;
|
||||
padding: 0.4em;
|
||||
}
|
||||
|
||||
/* -- domain module index --------------------------------------------------- */
|
||||
|
||||
table.modindextable td {
|
||||
padding: 2px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
/* -- general body styles --------------------------------------------------- */
|
||||
|
||||
div.body p, div.body dd, div.body li, div.body blockquote {
|
||||
-moz-hyphens: auto;
|
||||
-ms-hyphens: auto;
|
||||
-webkit-hyphens: auto;
|
||||
hyphens: auto;
|
||||
}
|
||||
|
||||
a.headerlink {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
h1:hover > a.headerlink,
|
||||
h2:hover > a.headerlink,
|
||||
h3:hover > a.headerlink,
|
||||
h4:hover > a.headerlink,
|
||||
h5:hover > a.headerlink,
|
||||
h6:hover > a.headerlink,
|
||||
dt:hover > a.headerlink,
|
||||
caption:hover > a.headerlink,
|
||||
p.caption:hover > a.headerlink,
|
||||
div.code-block-caption:hover > a.headerlink {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
div.body p.caption {
|
||||
text-align: inherit;
|
||||
}
|
||||
|
||||
div.body td {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.first {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
p.rubric {
|
||||
margin-top: 30px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
img.align-left, .figure.align-left, object.align-left {
|
||||
clear: left;
|
||||
float: left;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
img.align-right, .figure.align-right, object.align-right {
|
||||
clear: right;
|
||||
float: right;
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
img.align-center, .figure.align-center, object.align-center {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.align-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.align-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.align-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* -- sidebars -------------------------------------------------------------- */
|
||||
|
||||
div.sidebar {
|
||||
margin: 0 0 0.5em 1em;
|
||||
border: 1px solid #ddb;
|
||||
padding: 7px 7px 0 7px;
|
||||
background-color: #ffe;
|
||||
width: 40%;
|
||||
float: right;
|
||||
}
|
||||
|
||||
p.sidebar-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* -- topics ---------------------------------------------------------------- */
|
||||
|
||||
div.topic {
|
||||
border: 1px solid #ccc;
|
||||
padding: 7px 7px 0 7px;
|
||||
margin: 10px 0 10px 0;
|
||||
}
|
||||
|
||||
p.topic-title {
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* -- admonitions ----------------------------------------------------------- */
|
||||
|
||||
div.admonition {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding: 7px;
|
||||
}
|
||||
|
||||
div.admonition dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.admonition dl {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
p.admonition-title {
|
||||
margin: 0px 10px 5px 0px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.body p.centered {
|
||||
text-align: center;
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
/* -- tables ---------------------------------------------------------------- */
|
||||
|
||||
table.docutils {
|
||||
border: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table caption span.caption-number {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
table caption span.caption-text {
|
||||
}
|
||||
|
||||
table.docutils td, table.docutils th {
|
||||
padding: 1px 8px 1px 5px;
|
||||
border-top: 0;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
border-bottom: 1px solid #aaa;
|
||||
}
|
||||
|
||||
table.footnote td, table.footnote th {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
table.citation {
|
||||
border-left: solid 1px gray;
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
table.citation td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* -- figures --------------------------------------------------------------- */
|
||||
|
||||
div.figure {
|
||||
margin: 0.5em;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
div.figure p.caption {
|
||||
padding: 0.3em;
|
||||
}
|
||||
|
||||
div.figure p.caption span.caption-number {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
div.figure p.caption span.caption-text {
|
||||
}
|
||||
|
||||
/* -- field list styles ----------------------------------------------------- */
|
||||
|
||||
table.field-list td, table.field-list th {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
.field-list ul {
|
||||
margin: 0;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.field-list p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* -- other body styles ----------------------------------------------------- */
|
||||
|
||||
ol.arabic {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
ol.loweralpha {
|
||||
list-style: lower-alpha;
|
||||
}
|
||||
|
||||
ol.upperalpha {
|
||||
list-style: upper-alpha;
|
||||
}
|
||||
|
||||
ol.lowerroman {
|
||||
list-style: lower-roman;
|
||||
}
|
||||
|
||||
ol.upperroman {
|
||||
list-style: upper-roman;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
dd p {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
dd ul, dd table {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-top: 3px;
|
||||
margin-bottom: 10px;
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
dt:target, .highlighted {
|
||||
background-color: #fbe54e;
|
||||
}
|
||||
|
||||
dl.glossary dt {
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.optional {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.sig-paren {
|
||||
font-size: larger;
|
||||
}
|
||||
|
||||
.versionmodified {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.system-message {
|
||||
background-color: #fda;
|
||||
padding: 5px;
|
||||
border: 3px solid red;
|
||||
}
|
||||
|
||||
.footnote:target {
|
||||
background-color: #ffa;
|
||||
}
|
||||
|
||||
.line-block {
|
||||
display: block;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.line-block .line-block {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
margin-left: 1.5em;
|
||||
}
|
||||
|
||||
.guilabel, .menuselection {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.accelerator {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.classifier {
|
||||
font-style: oblique;
|
||||
}
|
||||
|
||||
abbr, acronym {
|
||||
border-bottom: dotted 1px;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
/* -- code displays --------------------------------------------------------- */
|
||||
|
||||
pre {
|
||||
overflow: auto;
|
||||
overflow-y: hidden; /* fixes display issues on Chrome browsers */
|
||||
}
|
||||
|
||||
span.pre {
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
-webkit-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
td.linenos pre {
|
||||
padding: 5px 0px;
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
table.highlighttable {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
table.highlighttable td {
|
||||
padding: 0 0.5em 0 0.5em;
|
||||
}
|
||||
|
||||
div.code-block-caption {
|
||||
padding: 2px 5px;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
div.code-block-caption code {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
div.code-block-caption + div > div.highlight > pre {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
div.code-block-caption span.caption-number {
|
||||
padding: 0.1em 0.3em;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
div.code-block-caption span.caption-text {
|
||||
}
|
||||
|
||||
div.literal-block-wrapper {
|
||||
padding: 1em 1em 0;
|
||||
}
|
||||
|
||||
div.literal-block-wrapper div.highlight {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
code.descname {
|
||||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
code.descclassname {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
code.xref, a code {
|
||||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.viewcode-link {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.viewcode-back {
|
||||
float: right;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
div.viewcode-block:target {
|
||||
margin: -1px -10px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
/* -- math display ---------------------------------------------------------- */
|
||||
|
||||
img.math {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
div.body div.math p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
span.eqno {
|
||||
float: right;
|
||||
}
|
||||
|
||||
span.eqno a.headerlink {
|
||||
position: relative;
|
||||
left: 0px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
div.math:hover a.headerlink {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* -- printout stylesheet --------------------------------------------------- */
|
||||
|
||||
@media print {
|
||||
div.document,
|
||||
div.documentwrapper,
|
||||
div.bodywrapper {
|
||||
margin: 0 !important;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.sphinxsidebar,
|
||||
div.related,
|
||||
div.footer,
|
||||
#top-link {
|
||||
display: none;
|
||||
}
|
||||
}
|
BIN
docs/_build/html/_static/comment-bright.png
vendored
Before Width: | Height: | Size: 756 B |
BIN
docs/_build/html/_static/comment-close.png
vendored
Before Width: | Height: | Size: 829 B |
BIN
docs/_build/html/_static/comment.png
vendored
Before Width: | Height: | Size: 641 B |
1
docs/_build/html/_static/custom.css
vendored
@@ -1 +0,0 @@
|
||||
/* This file intentionally left blank. */
|
287
docs/_build/html/_static/doctools.js
vendored
@@ -1,287 +0,0 @@
|
||||
/*
|
||||
* doctools.js
|
||||
* ~~~~~~~~~~~
|
||||
*
|
||||
* Sphinx JavaScript utilities for all documentation.
|
||||
*
|
||||
* :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* select a different prefix for underscore
|
||||
*/
|
||||
$u = _.noConflict();
|
||||
|
||||
/**
|
||||
* make the code below compatible with browsers without
|
||||
* an installed firebug like debugger
|
||||
if (!window.console || !console.firebug) {
|
||||
var names = ["log", "debug", "info", "warn", "error", "assert", "dir",
|
||||
"dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace",
|
||||
"profile", "profileEnd"];
|
||||
window.console = {};
|
||||
for (var i = 0; i < names.length; ++i)
|
||||
window.console[names[i]] = function() {};
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* small helper function to urldecode strings
|
||||
*/
|
||||
jQuery.urldecode = function(x) {
|
||||
return decodeURIComponent(x).replace(/\+/g, ' ');
|
||||
};
|
||||
|
||||
/**
|
||||
* small helper function to urlencode strings
|
||||
*/
|
||||
jQuery.urlencode = encodeURIComponent;
|
||||
|
||||
/**
|
||||
* This function returns the parsed url parameters of the
|
||||
* current request. Multiple values per key are supported,
|
||||
* it will always return arrays of strings for the value parts.
|
||||
*/
|
||||
jQuery.getQueryParameters = function(s) {
|
||||
if (typeof s == 'undefined')
|
||||
s = document.location.search;
|
||||
var parts = s.substr(s.indexOf('?') + 1).split('&');
|
||||
var result = {};
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
var tmp = parts[i].split('=', 2);
|
||||
var key = jQuery.urldecode(tmp[0]);
|
||||
var value = jQuery.urldecode(tmp[1]);
|
||||
if (key in result)
|
||||
result[key].push(value);
|
||||
else
|
||||
result[key] = [value];
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* highlight a given string on a jquery object by wrapping it in
|
||||
* span elements with the given class name.
|
||||
*/
|
||||
jQuery.fn.highlightText = function(text, className) {
|
||||
function highlight(node) {
|
||||
if (node.nodeType == 3) {
|
||||
var val = node.nodeValue;
|
||||
var pos = val.toLowerCase().indexOf(text);
|
||||
if (pos >= 0 && !jQuery(node.parentNode).hasClass(className)) {
|
||||
var span = document.createElement("span");
|
||||
span.className = className;
|
||||
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
|
||||
node.parentNode.insertBefore(span, node.parentNode.insertBefore(
|
||||
document.createTextNode(val.substr(pos + text.length)),
|
||||
node.nextSibling));
|
||||
node.nodeValue = val.substr(0, pos);
|
||||
}
|
||||
}
|
||||
else if (!jQuery(node).is("button, select, textarea")) {
|
||||
jQuery.each(node.childNodes, function() {
|
||||
highlight(this);
|
||||
});
|
||||
}
|
||||
}
|
||||
return this.each(function() {
|
||||
highlight(this);
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
* backward compatibility for jQuery.browser
|
||||
* This will be supported until firefox bug is fixed.
|
||||
*/
|
||||
if (!jQuery.browser) {
|
||||
jQuery.uaMatch = function(ua) {
|
||||
ua = ua.toLowerCase();
|
||||
|
||||
var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
|
||||
/(webkit)[ \/]([\w.]+)/.exec(ua) ||
|
||||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
|
||||
/(msie) ([\w.]+)/.exec(ua) ||
|
||||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
|
||||
[];
|
||||
|
||||
return {
|
||||
browser: match[ 1 ] || "",
|
||||
version: match[ 2 ] || "0"
|
||||
};
|
||||
};
|
||||
jQuery.browser = {};
|
||||
jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Small JavaScript module for the documentation.
|
||||
*/
|
||||
var Documentation = {
|
||||
|
||||
init : function() {
|
||||
this.fixFirefoxAnchorBug();
|
||||
this.highlightSearchWords();
|
||||
this.initIndexTable();
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* i18n support
|
||||
*/
|
||||
TRANSLATIONS : {},
|
||||
PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; },
|
||||
LOCALE : 'unknown',
|
||||
|
||||
// gettext and ngettext don't access this so that the functions
|
||||
// can safely bound to a different name (_ = Documentation.gettext)
|
||||
gettext : function(string) {
|
||||
var translated = Documentation.TRANSLATIONS[string];
|
||||
if (typeof translated == 'undefined')
|
||||
return string;
|
||||
return (typeof translated == 'string') ? translated : translated[0];
|
||||
},
|
||||
|
||||
ngettext : function(singular, plural, n) {
|
||||
var translated = Documentation.TRANSLATIONS[singular];
|
||||
if (typeof translated == 'undefined')
|
||||
return (n == 1) ? singular : plural;
|
||||
return translated[Documentation.PLURALEXPR(n)];
|
||||
},
|
||||
|
||||
addTranslations : function(catalog) {
|
||||
for (var key in catalog.messages)
|
||||
this.TRANSLATIONS[key] = catalog.messages[key];
|
||||
this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')');
|
||||
this.LOCALE = catalog.locale;
|
||||
},
|
||||
|
||||
/**
|
||||
* add context elements like header anchor links
|
||||
*/
|
||||
addContextElements : function() {
|
||||
$('div[id] > :header:first').each(function() {
|
||||
$('<a class="headerlink">\u00B6</a>').
|
||||
attr('href', '#' + this.id).
|
||||
attr('title', _('Permalink to this headline')).
|
||||
appendTo(this);
|
||||
});
|
||||
$('dt[id]').each(function() {
|
||||
$('<a class="headerlink">\u00B6</a>').
|
||||
attr('href', '#' + this.id).
|
||||
attr('title', _('Permalink to this definition')).
|
||||
appendTo(this);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* workaround a firefox stupidity
|
||||
* see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075
|
||||
*/
|
||||
fixFirefoxAnchorBug : function() {
|
||||
if (document.location.hash)
|
||||
window.setTimeout(function() {
|
||||
document.location.href += '';
|
||||
}, 10);
|
||||
},
|
||||
|
||||
/**
|
||||
* highlight the search words provided in the url in the text
|
||||
*/
|
||||
highlightSearchWords : function() {
|
||||
var params = $.getQueryParameters();
|
||||
var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : [];
|
||||
if (terms.length) {
|
||||
var body = $('div.body');
|
||||
if (!body.length) {
|
||||
body = $('body');
|
||||
}
|
||||
window.setTimeout(function() {
|
||||
$.each(terms, function() {
|
||||
body.highlightText(this.toLowerCase(), 'highlighted');
|
||||
});
|
||||
}, 10);
|
||||
$('<p class="highlight-link"><a href="javascript:Documentation.' +
|
||||
'hideSearchWords()">' + _('Hide Search Matches') + '</a></p>')
|
||||
.appendTo($('#searchbox'));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* init the domain index toggle buttons
|
||||
*/
|
||||
initIndexTable : function() {
|
||||
var togglers = $('img.toggler').click(function() {
|
||||
var src = $(this).attr('src');
|
||||
var idnum = $(this).attr('id').substr(7);
|
||||
$('tr.cg-' + idnum).toggle();
|
||||
if (src.substr(-9) == 'minus.png')
|
||||
$(this).attr('src', src.substr(0, src.length-9) + 'plus.png');
|
||||
else
|
||||
$(this).attr('src', src.substr(0, src.length-8) + 'minus.png');
|
||||
}).css('display', '');
|
||||
if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) {
|
||||
togglers.click();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* helper function to hide the search marks again
|
||||
*/
|
||||
hideSearchWords : function() {
|
||||
$('#searchbox .highlight-link').fadeOut(300);
|
||||
$('span.highlighted').removeClass('highlighted');
|
||||
},
|
||||
|
||||
/**
|
||||
* make the url absolute
|
||||
*/
|
||||
makeURL : function(relativeURL) {
|
||||
return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL;
|
||||
},
|
||||
|
||||
/**
|
||||
* get the current relative url
|
||||
*/
|
||||
getCurrentURL : function() {
|
||||
var path = document.location.pathname;
|
||||
var parts = path.split(/\//);
|
||||
$.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() {
|
||||
if (this == '..')
|
||||
parts.pop();
|
||||
});
|
||||
var url = parts.join('/');
|
||||
return path.substring(url.lastIndexOf('/') + 1, path.length - 1);
|
||||
},
|
||||
|
||||
initOnKeyListeners: function() {
|
||||
$(document).keyup(function(event) {
|
||||
var activeElementType = document.activeElement.tagName;
|
||||
// don't navigate when in search box or textarea
|
||||
if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT') {
|
||||
switch (event.keyCode) {
|
||||
case 37: // left
|
||||
var prevHref = $('link[rel="prev"]').prop('href');
|
||||
if (prevHref) {
|
||||
window.location.href = prevHref;
|
||||
return false;
|
||||
}
|
||||
case 39: // right
|
||||
var nextHref = $('link[rel="next"]').prop('href');
|
||||
if (nextHref) {
|
||||
window.location.href = nextHref;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// quick alias for translations
|
||||
_ = Documentation.gettext;
|
||||
|
||||
$(document).ready(function() {
|
||||
Documentation.init();
|
||||
});
|
BIN
docs/_build/html/_static/down-pressed.png
vendored
Before Width: | Height: | Size: 222 B |
BIN
docs/_build/html/_static/down.png
vendored
Before Width: | Height: | Size: 202 B |
BIN
docs/_build/html/_static/file.png
vendored
Before Width: | Height: | Size: 286 B |
10074
docs/_build/html/_static/jquery-3.1.0.js
vendored
4
docs/_build/html/_static/jquery.js
vendored
BIN
docs/_build/html/_static/minus.png
vendored
Before Width: | Height: | Size: 90 B |
BIN
docs/_build/html/_static/plus.png
vendored
Before Width: | Height: | Size: 90 B |
65
docs/_build/html/_static/pygments.css
vendored
@@ -1,65 +0,0 @@
|
||||
.highlight .hll { background-color: #ffffcc }
|
||||
.highlight { background: #eeffcc; }
|
||||
.highlight .c { color: #408090; font-style: italic } /* Comment */
|
||||
.highlight .err { border: 1px solid #FF0000 } /* Error */
|
||||
.highlight .k { color: #007020; font-weight: bold } /* Keyword */
|
||||
.highlight .o { color: #666666 } /* Operator */
|
||||
.highlight .ch { color: #408090; font-style: italic } /* Comment.Hashbang */
|
||||
.highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */
|
||||
.highlight .cp { color: #007020 } /* Comment.Preproc */
|
||||
.highlight .cpf { color: #408090; font-style: italic } /* Comment.PreprocFile */
|
||||
.highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */
|
||||
.highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */
|
||||
.highlight .gd { color: #A00000 } /* Generic.Deleted */
|
||||
.highlight .ge { font-style: italic } /* Generic.Emph */
|
||||
.highlight .gr { color: #FF0000 } /* Generic.Error */
|
||||
.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
|
||||
.highlight .gi { color: #00A000 } /* Generic.Inserted */
|
||||
.highlight .go { color: #333333 } /* Generic.Output */
|
||||
.highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
|
||||
.highlight .gs { font-weight: bold } /* Generic.Strong */
|
||||
.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
|
||||
.highlight .gt { color: #0044DD } /* Generic.Traceback */
|
||||
.highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */
|
||||
.highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */
|
||||
.highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */
|
||||
.highlight .kp { color: #007020 } /* Keyword.Pseudo */
|
||||
.highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */
|
||||
.highlight .kt { color: #902000 } /* Keyword.Type */
|
||||
.highlight .m { color: #208050 } /* Literal.Number */
|
||||
.highlight .s { color: #4070a0 } /* Literal.String */
|
||||
.highlight .na { color: #4070a0 } /* Name.Attribute */
|
||||
.highlight .nb { color: #007020 } /* Name.Builtin */
|
||||
.highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */
|
||||
.highlight .no { color: #60add5 } /* Name.Constant */
|
||||
.highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */
|
||||
.highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */
|
||||
.highlight .ne { color: #007020 } /* Name.Exception */
|
||||
.highlight .nf { color: #06287e } /* Name.Function */
|
||||
.highlight .nl { color: #002070; font-weight: bold } /* Name.Label */
|
||||
.highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
|
||||
.highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */
|
||||
.highlight .nv { color: #bb60d5 } /* Name.Variable */
|
||||
.highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */
|
||||
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
|
||||
.highlight .mb { color: #208050 } /* Literal.Number.Bin */
|
||||
.highlight .mf { color: #208050 } /* Literal.Number.Float */
|
||||
.highlight .mh { color: #208050 } /* Literal.Number.Hex */
|
||||
.highlight .mi { color: #208050 } /* Literal.Number.Integer */
|
||||
.highlight .mo { color: #208050 } /* Literal.Number.Oct */
|
||||
.highlight .sb { color: #4070a0 } /* Literal.String.Backtick */
|
||||
.highlight .sc { color: #4070a0 } /* Literal.String.Char */
|
||||
.highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */
|
||||
.highlight .s2 { color: #4070a0 } /* Literal.String.Double */
|
||||
.highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */
|
||||
.highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */
|
||||
.highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */
|
||||
.highlight .sx { color: #c65d09 } /* Literal.String.Other */
|
||||
.highlight .sr { color: #235388 } /* Literal.String.Regex */
|
||||
.highlight .s1 { color: #4070a0 } /* Literal.String.Single */
|
||||
.highlight .ss { color: #517918 } /* Literal.String.Symbol */
|
||||
.highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */
|
||||
.highlight .vc { color: #bb60d5 } /* Name.Variable.Class */
|
||||
.highlight .vg { color: #bb60d5 } /* Name.Variable.Global */
|
||||
.highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */
|
||||
.highlight .il { color: #208050 } /* Literal.Number.Integer.Long */
|
758
docs/_build/html/_static/searchtools.js
vendored
@@ -1,758 +0,0 @@
|
||||
/*
|
||||
* searchtools.js_t
|
||||
* ~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* Sphinx JavaScript utilities for the full-text search.
|
||||
*
|
||||
* :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/* Non-minified version JS is _stemmer.js if file is provided */
|
||||
/**
|
||||
* Porter Stemmer
|
||||
*/
|
||||
var Stemmer = function() {
|
||||
|
||||
var step2list = {
|
||||
ational: 'ate',
|
||||
tional: 'tion',
|
||||
enci: 'ence',
|
||||
anci: 'ance',
|
||||
izer: 'ize',
|
||||
bli: 'ble',
|
||||
alli: 'al',
|
||||
entli: 'ent',
|
||||
eli: 'e',
|
||||
ousli: 'ous',
|
||||
ization: 'ize',
|
||||
ation: 'ate',
|
||||
ator: 'ate',
|
||||
alism: 'al',
|
||||
iveness: 'ive',
|
||||
fulness: 'ful',
|
||||
ousness: 'ous',
|
||||
aliti: 'al',
|
||||
iviti: 'ive',
|
||||
biliti: 'ble',
|
||||
logi: 'log'
|
||||
};
|
||||
|
||||
var step3list = {
|
||||
icate: 'ic',
|
||||
ative: '',
|
||||
alize: 'al',
|
||||
iciti: 'ic',
|
||||
ical: 'ic',
|
||||
ful: '',
|
||||
ness: ''
|
||||
};
|
||||
|
||||
var c = "[^aeiou]"; // consonant
|
||||
var v = "[aeiouy]"; // vowel
|
||||
var C = c + "[^aeiouy]*"; // consonant sequence
|
||||
var V = v + "[aeiou]*"; // vowel sequence
|
||||
|
||||
var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
|
||||
var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
|
||||
var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
|
||||
var s_v = "^(" + C + ")?" + v; // vowel in stem
|
||||
|
||||
this.stemWord = function (w) {
|
||||
var stem;
|
||||
var suffix;
|
||||
var firstch;
|
||||
var origword = w;
|
||||
|
||||
if (w.length < 3)
|
||||
return w;
|
||||
|
||||
var re;
|
||||
var re2;
|
||||
var re3;
|
||||
var re4;
|
||||
|
||||
firstch = w.substr(0,1);
|
||||
if (firstch == "y")
|
||||
w = firstch.toUpperCase() + w.substr(1);
|
||||
|
||||
// Step 1a
|
||||
re = /^(.+?)(ss|i)es$/;
|
||||
re2 = /^(.+?)([^s])s$/;
|
||||
|
||||
if (re.test(w))
|
||||
w = w.replace(re,"$1$2");
|
||||
else if (re2.test(w))
|
||||
w = w.replace(re2,"$1$2");
|
||||
|
||||
// Step 1b
|
||||
re = /^(.+?)eed$/;
|
||||
re2 = /^(.+?)(ed|ing)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(fp[1])) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
}
|
||||
else if (re2.test(w)) {
|
||||
var fp = re2.exec(w);
|
||||
stem = fp[1];
|
||||
re2 = new RegExp(s_v);
|
||||
if (re2.test(stem)) {
|
||||
w = stem;
|
||||
re2 = /(at|bl|iz)$/;
|
||||
re3 = new RegExp("([^aeiouylsz])\\1$");
|
||||
re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
|
||||
if (re2.test(w))
|
||||
w = w + "e";
|
||||
else if (re3.test(w)) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
else if (re4.test(w))
|
||||
w = w + "e";
|
||||
}
|
||||
}
|
||||
|
||||
// Step 1c
|
||||
re = /^(.+?)y$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(s_v);
|
||||
if (re.test(stem))
|
||||
w = stem + "i";
|
||||
}
|
||||
|
||||
// Step 2
|
||||
re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
suffix = fp[2];
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(stem))
|
||||
w = stem + step2list[suffix];
|
||||
}
|
||||
|
||||
// Step 3
|
||||
re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
suffix = fp[2];
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(stem))
|
||||
w = stem + step3list[suffix];
|
||||
}
|
||||
|
||||
// Step 4
|
||||
re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
|
||||
re2 = /^(.+?)(s|t)(ion)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(mgr1);
|
||||
if (re.test(stem))
|
||||
w = stem;
|
||||
}
|
||||
else if (re2.test(w)) {
|
||||
var fp = re2.exec(w);
|
||||
stem = fp[1] + fp[2];
|
||||
re2 = new RegExp(mgr1);
|
||||
if (re2.test(stem))
|
||||
w = stem;
|
||||
}
|
||||
|
||||
// Step 5
|
||||
re = /^(.+?)e$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(mgr1);
|
||||
re2 = new RegExp(meq1);
|
||||
re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
|
||||
if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
|
||||
w = stem;
|
||||
}
|
||||
re = /ll$/;
|
||||
re2 = new RegExp(mgr1);
|
||||
if (re.test(w) && re2.test(w)) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
|
||||
// and turn initial Y back to y
|
||||
if (firstch == "y")
|
||||
w = firstch.toLowerCase() + w.substr(1);
|
||||
return w;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Simple result scoring code.
|
||||
*/
|
||||
var Scorer = {
|
||||
// Implement the following function to further tweak the score for each result
|
||||
// The function takes a result array [filename, title, anchor, descr, score]
|
||||
// and returns the new score.
|
||||
/*
|
||||
score: function(result) {
|
||||
return result[4];
|
||||
},
|
||||
*/
|
||||
|
||||
// query matches the full name of an object
|
||||
objNameMatch: 11,
|
||||
// or matches in the last dotted part of the object name
|
||||
objPartialMatch: 6,
|
||||
// Additive scores depending on the priority of the object
|
||||
objPrio: {0: 15, // used to be importantResults
|
||||
1: 5, // used to be objectResults
|
||||
2: -5}, // used to be unimportantResults
|
||||
// Used when the priority is not in the mapping.
|
||||
objPrioDefault: 0,
|
||||
|
||||
// query found in title
|
||||
title: 15,
|
||||
// query found in terms
|
||||
term: 5
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var splitChars = (function() {
|
||||
var result = {};
|
||||
var singles = [96, 180, 187, 191, 215, 247, 749, 885, 903, 907, 909, 930, 1014, 1648,
|
||||
1748, 1809, 2416, 2473, 2481, 2526, 2601, 2609, 2612, 2615, 2653, 2702,
|
||||
2706, 2729, 2737, 2740, 2857, 2865, 2868, 2910, 2928, 2948, 2961, 2971,
|
||||
2973, 3085, 3089, 3113, 3124, 3213, 3217, 3241, 3252, 3295, 3341, 3345,
|
||||
3369, 3506, 3516, 3633, 3715, 3721, 3736, 3744, 3748, 3750, 3756, 3761,
|
||||
3781, 3912, 4239, 4347, 4681, 4695, 4697, 4745, 4785, 4799, 4801, 4823,
|
||||
4881, 5760, 5901, 5997, 6313, 7405, 8024, 8026, 8028, 8030, 8117, 8125,
|
||||
8133, 8181, 8468, 8485, 8487, 8489, 8494, 8527, 11311, 11359, 11687, 11695,
|
||||
11703, 11711, 11719, 11727, 11735, 12448, 12539, 43010, 43014, 43019, 43587,
|
||||
43696, 43713, 64286, 64297, 64311, 64317, 64319, 64322, 64325, 65141];
|
||||
var i, j, start, end;
|
||||
for (i = 0; i < singles.length; i++) {
|
||||
result[singles[i]] = true;
|
||||
}
|
||||
var ranges = [[0, 47], [58, 64], [91, 94], [123, 169], [171, 177], [182, 184], [706, 709],
|
||||
[722, 735], [741, 747], [751, 879], [888, 889], [894, 901], [1154, 1161],
|
||||
[1318, 1328], [1367, 1368], [1370, 1376], [1416, 1487], [1515, 1519], [1523, 1568],
|
||||
[1611, 1631], [1642, 1645], [1750, 1764], [1767, 1773], [1789, 1790], [1792, 1807],
|
||||
[1840, 1868], [1958, 1968], [1970, 1983], [2027, 2035], [2038, 2041], [2043, 2047],
|
||||
[2070, 2073], [2075, 2083], [2085, 2087], [2089, 2307], [2362, 2364], [2366, 2383],
|
||||
[2385, 2391], [2402, 2405], [2419, 2424], [2432, 2436], [2445, 2446], [2449, 2450],
|
||||
[2483, 2485], [2490, 2492], [2494, 2509], [2511, 2523], [2530, 2533], [2546, 2547],
|
||||
[2554, 2564], [2571, 2574], [2577, 2578], [2618, 2648], [2655, 2661], [2672, 2673],
|
||||
[2677, 2692], [2746, 2748], [2750, 2767], [2769, 2783], [2786, 2789], [2800, 2820],
|
||||
[2829, 2830], [2833, 2834], [2874, 2876], [2878, 2907], [2914, 2917], [2930, 2946],
|
||||
[2955, 2957], [2966, 2968], [2976, 2978], [2981, 2983], [2987, 2989], [3002, 3023],
|
||||
[3025, 3045], [3059, 3076], [3130, 3132], [3134, 3159], [3162, 3167], [3170, 3173],
|
||||
[3184, 3191], [3199, 3204], [3258, 3260], [3262, 3293], [3298, 3301], [3312, 3332],
|
||||
[3386, 3388], [3390, 3423], [3426, 3429], [3446, 3449], [3456, 3460], [3479, 3481],
|
||||
[3518, 3519], [3527, 3584], [3636, 3647], [3655, 3663], [3674, 3712], [3717, 3718],
|
||||
[3723, 3724], [3726, 3731], [3752, 3753], [3764, 3772], [3774, 3775], [3783, 3791],
|
||||
[3802, 3803], [3806, 3839], [3841, 3871], [3892, 3903], [3949, 3975], [3980, 4095],
|
||||
[4139, 4158], [4170, 4175], [4182, 4185], [4190, 4192], [4194, 4196], [4199, 4205],
|
||||
[4209, 4212], [4226, 4237], [4250, 4255], [4294, 4303], [4349, 4351], [4686, 4687],
|
||||
[4702, 4703], [4750, 4751], [4790, 4791], [4806, 4807], [4886, 4887], [4955, 4968],
|
||||
[4989, 4991], [5008, 5023], [5109, 5120], [5741, 5742], [5787, 5791], [5867, 5869],
|
||||
[5873, 5887], [5906, 5919], [5938, 5951], [5970, 5983], [6001, 6015], [6068, 6102],
|
||||
[6104, 6107], [6109, 6111], [6122, 6127], [6138, 6159], [6170, 6175], [6264, 6271],
|
||||
[6315, 6319], [6390, 6399], [6429, 6469], [6510, 6511], [6517, 6527], [6572, 6592],
|
||||
[6600, 6607], [6619, 6655], [6679, 6687], [6741, 6783], [6794, 6799], [6810, 6822],
|
||||
[6824, 6916], [6964, 6980], [6988, 6991], [7002, 7042], [7073, 7085], [7098, 7167],
|
||||
[7204, 7231], [7242, 7244], [7294, 7400], [7410, 7423], [7616, 7679], [7958, 7959],
|
||||
[7966, 7967], [8006, 8007], [8014, 8015], [8062, 8063], [8127, 8129], [8141, 8143],
|
||||
[8148, 8149], [8156, 8159], [8173, 8177], [8189, 8303], [8306, 8307], [8314, 8318],
|
||||
[8330, 8335], [8341, 8449], [8451, 8454], [8456, 8457], [8470, 8472], [8478, 8483],
|
||||
[8506, 8507], [8512, 8516], [8522, 8525], [8586, 9311], [9372, 9449], [9472, 10101],
|
||||
[10132, 11263], [11493, 11498], [11503, 11516], [11518, 11519], [11558, 11567],
|
||||
[11622, 11630], [11632, 11647], [11671, 11679], [11743, 11822], [11824, 12292],
|
||||
[12296, 12320], [12330, 12336], [12342, 12343], [12349, 12352], [12439, 12444],
|
||||
[12544, 12548], [12590, 12592], [12687, 12689], [12694, 12703], [12728, 12783],
|
||||
[12800, 12831], [12842, 12880], [12896, 12927], [12938, 12976], [12992, 13311],
|
||||
[19894, 19967], [40908, 40959], [42125, 42191], [42238, 42239], [42509, 42511],
|
||||
[42540, 42559], [42592, 42593], [42607, 42622], [42648, 42655], [42736, 42774],
|
||||
[42784, 42785], [42889, 42890], [42893, 43002], [43043, 43055], [43062, 43071],
|
||||
[43124, 43137], [43188, 43215], [43226, 43249], [43256, 43258], [43260, 43263],
|
||||
[43302, 43311], [43335, 43359], [43389, 43395], [43443, 43470], [43482, 43519],
|
||||
[43561, 43583], [43596, 43599], [43610, 43615], [43639, 43641], [43643, 43647],
|
||||
[43698, 43700], [43703, 43704], [43710, 43711], [43715, 43738], [43742, 43967],
|
||||
[44003, 44015], [44026, 44031], [55204, 55215], [55239, 55242], [55292, 55295],
|
||||
[57344, 63743], [64046, 64047], [64110, 64111], [64218, 64255], [64263, 64274],
|
||||
[64280, 64284], [64434, 64466], [64830, 64847], [64912, 64913], [64968, 65007],
|
||||
[65020, 65135], [65277, 65295], [65306, 65312], [65339, 65344], [65371, 65381],
|
||||
[65471, 65473], [65480, 65481], [65488, 65489], [65496, 65497]];
|
||||
for (i = 0; i < ranges.length; i++) {
|
||||
start = ranges[i][0];
|
||||
end = ranges[i][1];
|
||||
for (j = start; j <= end; j++) {
|
||||
result[j] = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
})();
|
||||
|
||||
function splitQuery(query) {
|
||||
var result = [];
|
||||
var start = -1;
|
||||
for (var i = 0; i < query.length; i++) {
|
||||
if (splitChars[query.charCodeAt(i)]) {
|
||||
if (start !== -1) {
|
||||
result.push(query.slice(start, i));
|
||||
start = -1;
|
||||
}
|
||||
} else if (start === -1) {
|
||||
start = i;
|
||||
}
|
||||
}
|
||||
if (start !== -1) {
|
||||
result.push(query.slice(start));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Search Module
|
||||
*/
|
||||
var Search = {
|
||||
|
||||
_index : null,
|
||||
_queued_query : null,
|
||||
_pulse_status : -1,
|
||||
|
||||
init : function() {
|
||||
var params = $.getQueryParameters();
|
||||
if (params.q) {
|
||||
var query = params.q[0];
|
||||
$('input[name="q"]')[0].value = query;
|
||||
this.performSearch(query);
|
||||
}
|
||||
},
|
||||
|
||||
loadIndex : function(url) {
|
||||
$.ajax({type: "GET", url: url, data: null,
|
||||
dataType: "script", cache: true,
|
||||
complete: function(jqxhr, textstatus) {
|
||||
if (textstatus != "success") {
|
||||
document.getElementById("searchindexloader").src = url;
|
||||
}
|
||||
}});
|
||||
},
|
||||
|
||||
setIndex : function(index) {
|
||||
var q;
|
||||
this._index = index;
|
||||
if ((q = this._queued_query) !== null) {
|
||||
this._queued_query = null;
|
||||
Search.query(q);
|
||||
}
|
||||
},
|
||||
|
||||
hasIndex : function() {
|
||||
return this._index !== null;
|
||||
},
|
||||
|
||||
deferQuery : function(query) {
|
||||
this._queued_query = query;
|
||||
},
|
||||
|
||||
stopPulse : function() {
|
||||
this._pulse_status = 0;
|
||||
},
|
||||
|
||||
startPulse : function() {
|
||||
if (this._pulse_status >= 0)
|
||||
return;
|
||||
function pulse() {
|
||||
var i;
|
||||
Search._pulse_status = (Search._pulse_status + 1) % 4;
|
||||
var dotString = '';
|
||||
for (i = 0; i < Search._pulse_status; i++)
|
||||
dotString += '.';
|
||||
Search.dots.text(dotString);
|
||||
if (Search._pulse_status > -1)
|
||||
window.setTimeout(pulse, 500);
|
||||
}
|
||||
pulse();
|
||||
},
|
||||
|
||||
/**
|
||||
* perform a search for something (or wait until index is loaded)
|
||||
*/
|
||||
performSearch : function(query) {
|
||||
// create the required interface elements
|
||||
this.out = $('#search-results');
|
||||
this.title = $('<h2>' + _('Searching') + '</h2>').appendTo(this.out);
|
||||
this.dots = $('<span></span>').appendTo(this.title);
|
||||
this.status = $('<p style="display: none"></p>').appendTo(this.out);
|
||||
this.output = $('<ul class="search"/>').appendTo(this.out);
|
||||
|
||||
$('#search-progress').text(_('Preparing search...'));
|
||||
this.startPulse();
|
||||
|
||||
// index already loaded, the browser was quick!
|
||||
if (this.hasIndex())
|
||||
this.query(query);
|
||||
else
|
||||
this.deferQuery(query);
|
||||
},
|
||||
|
||||
/**
|
||||
* execute search (requires search index to be loaded)
|
||||
*/
|
||||
query : function(query) {
|
||||
var i;
|
||||
var stopwords = ["a","and","are","as","at","be","but","by","for","if","in","into","is","it","near","no","not","of","on","or","such","that","the","their","then","there","these","they","this","to","was","will","with"];
|
||||
|
||||
// stem the searchterms and add them to the correct list
|
||||
var stemmer = new Stemmer();
|
||||
var searchterms = [];
|
||||
var excluded = [];
|
||||
var hlterms = [];
|
||||
var tmp = splitQuery(query);
|
||||
var objectterms = [];
|
||||
for (i = 0; i < tmp.length; i++) {
|
||||
if (tmp[i] !== "") {
|
||||
objectterms.push(tmp[i].toLowerCase());
|
||||
}
|
||||
|
||||
if ($u.indexOf(stopwords, tmp[i].toLowerCase()) != -1 || tmp[i].match(/^\d+$/) ||
|
||||
tmp[i] === "") {
|
||||
// skip this "word"
|
||||
continue;
|
||||
}
|
||||
// stem the word
|
||||
var word = stemmer.stemWord(tmp[i].toLowerCase());
|
||||
// prevent stemmer from cutting word smaller than two chars
|
||||
if(word.length < 3 && tmp[i].length >= 3) {
|
||||
word = tmp[i];
|
||||
}
|
||||
var toAppend;
|
||||
// select the correct list
|
||||
if (word[0] == '-') {
|
||||
toAppend = excluded;
|
||||
word = word.substr(1);
|
||||
}
|
||||
else {
|
||||
toAppend = searchterms;
|
||||
hlterms.push(tmp[i].toLowerCase());
|
||||
}
|
||||
// only add if not already in the list
|
||||
if (!$u.contains(toAppend, word))
|
||||
toAppend.push(word);
|
||||
}
|
||||
var highlightstring = '?highlight=' + $.urlencode(hlterms.join(" "));
|
||||
|
||||
// console.debug('SEARCH: searching for:');
|
||||
// console.info('required: ', searchterms);
|
||||
// console.info('excluded: ', excluded);
|
||||
|
||||
// prepare search
|
||||
var terms = this._index.terms;
|
||||
var titleterms = this._index.titleterms;
|
||||
|
||||
// array of [filename, title, anchor, descr, score]
|
||||
var results = [];
|
||||
$('#search-progress').empty();
|
||||
|
||||
// lookup as object
|
||||
for (i = 0; i < objectterms.length; i++) {
|
||||
var others = [].concat(objectterms.slice(0, i),
|
||||
objectterms.slice(i+1, objectterms.length));
|
||||
results = results.concat(this.performObjectSearch(objectterms[i], others));
|
||||
}
|
||||
|
||||
// lookup as search terms in fulltext
|
||||
results = results.concat(this.performTermsSearch(searchterms, excluded, terms, titleterms));
|
||||
|
||||
// let the scorer override scores with a custom scoring function
|
||||
if (Scorer.score) {
|
||||
for (i = 0; i < results.length; i++)
|
||||
results[i][4] = Scorer.score(results[i]);
|
||||
}
|
||||
|
||||
// now sort the results by score (in opposite order of appearance, since the
|
||||
// display function below uses pop() to retrieve items) and then
|
||||
// alphabetically
|
||||
results.sort(function(a, b) {
|
||||
var left = a[4];
|
||||
var right = b[4];
|
||||
if (left > right) {
|
||||
return 1;
|
||||
} else if (left < right) {
|
||||
return -1;
|
||||
} else {
|
||||
// same score: sort alphabetically
|
||||
left = a[1].toLowerCase();
|
||||
right = b[1].toLowerCase();
|
||||
return (left > right) ? -1 : ((left < right) ? 1 : 0);
|
||||
}
|
||||
});
|
||||
|
||||
// for debugging
|
||||
//Search.lastresults = results.slice(); // a copy
|
||||
//console.info('search results:', Search.lastresults);
|
||||
|
||||
// print the results
|
||||
var resultCount = results.length;
|
||||
function displayNextItem() {
|
||||
// results left, load the summary and display it
|
||||
if (results.length) {
|
||||
var item = results.pop();
|
||||
var listItem = $('<li style="display:none"></li>');
|
||||
if (DOCUMENTATION_OPTIONS.FILE_SUFFIX === '') {
|
||||
// dirhtml builder
|
||||
var dirname = item[0] + '/';
|
||||
if (dirname.match(/\/index\/$/)) {
|
||||
dirname = dirname.substring(0, dirname.length-6);
|
||||
} else if (dirname == 'index/') {
|
||||
dirname = '';
|
||||
}
|
||||
listItem.append($('<a/>').attr('href',
|
||||
DOCUMENTATION_OPTIONS.URL_ROOT + dirname +
|
||||
highlightstring + item[2]).html(item[1]));
|
||||
} else {
|
||||
// normal html builders
|
||||
listItem.append($('<a/>').attr('href',
|
||||
item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX +
|
||||
highlightstring + item[2]).html(item[1]));
|
||||
}
|
||||
if (item[3]) {
|
||||
listItem.append($('<span> (' + item[3] + ')</span>'));
|
||||
Search.output.append(listItem);
|
||||
listItem.slideDown(5, function() {
|
||||
displayNextItem();
|
||||
});
|
||||
} else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) {
|
||||
var suffix = DOCUMENTATION_OPTIONS.SOURCELINK_SUFFIX;
|
||||
$.ajax({url: DOCUMENTATION_OPTIONS.URL_ROOT + '_sources/' + item[5] + (item[5].slice(-suffix.length) === suffix ? '' : suffix),
|
||||
dataType: "text",
|
||||
complete: function(jqxhr, textstatus) {
|
||||
var data = jqxhr.responseText;
|
||||
if (data !== '' && data !== undefined) {
|
||||
listItem.append(Search.makeSearchSummary(data, searchterms, hlterms));
|
||||
}
|
||||
Search.output.append(listItem);
|
||||
listItem.slideDown(5, function() {
|
||||
displayNextItem();
|
||||
});
|
||||
}});
|
||||
} else {
|
||||
// no source available, just display title
|
||||
Search.output.append(listItem);
|
||||
listItem.slideDown(5, function() {
|
||||
displayNextItem();
|
||||
});
|
||||
}
|
||||
}
|
||||
// search finished, update title and status message
|
||||
else {
|
||||
Search.stopPulse();
|
||||
Search.title.text(_('Search Results'));
|
||||
if (!resultCount)
|
||||
Search.status.text(_('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.'));
|
||||
else
|
||||
Search.status.text(_('Search finished, found %s page(s) matching the search query.').replace('%s', resultCount));
|
||||
Search.status.fadeIn(500);
|
||||
}
|
||||
}
|
||||
displayNextItem();
|
||||
},
|
||||
|
||||
/**
|
||||
* search for object names
|
||||
*/
|
||||
performObjectSearch : function(object, otherterms) {
|
||||
var filenames = this._index.filenames;
|
||||
var docnames = this._index.docnames;
|
||||
var objects = this._index.objects;
|
||||
var objnames = this._index.objnames;
|
||||
var titles = this._index.titles;
|
||||
|
||||
var i;
|
||||
var results = [];
|
||||
|
||||
for (var prefix in objects) {
|
||||
for (var name in objects[prefix]) {
|
||||
var fullname = (prefix ? prefix + '.' : '') + name;
|
||||
if (fullname.toLowerCase().indexOf(object) > -1) {
|
||||
var score = 0;
|
||||
var parts = fullname.split('.');
|
||||
// check for different match types: exact matches of full name or
|
||||
// "last name" (i.e. last dotted part)
|
||||
if (fullname == object || parts[parts.length - 1] == object) {
|
||||
score += Scorer.objNameMatch;
|
||||
// matches in last name
|
||||
} else if (parts[parts.length - 1].indexOf(object) > -1) {
|
||||
score += Scorer.objPartialMatch;
|
||||
}
|
||||
var match = objects[prefix][name];
|
||||
var objname = objnames[match[1]][2];
|
||||
var title = titles[match[0]];
|
||||
// If more than one term searched for, we require other words to be
|
||||
// found in the name/title/description
|
||||
if (otherterms.length > 0) {
|
||||
var haystack = (prefix + ' ' + name + ' ' +
|
||||
objname + ' ' + title).toLowerCase();
|
||||
var allfound = true;
|
||||
for (i = 0; i < otherterms.length; i++) {
|
||||
if (haystack.indexOf(otherterms[i]) == -1) {
|
||||
allfound = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!allfound) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
var descr = objname + _(', in ') + title;
|
||||
|
||||
var anchor = match[3];
|
||||
if (anchor === '')
|
||||
anchor = fullname;
|
||||
else if (anchor == '-')
|
||||
anchor = objnames[match[1]][1] + '-' + fullname;
|
||||
// add custom score for some objects according to scorer
|
||||
if (Scorer.objPrio.hasOwnProperty(match[2])) {
|
||||
score += Scorer.objPrio[match[2]];
|
||||
} else {
|
||||
score += Scorer.objPrioDefault;
|
||||
}
|
||||
results.push([docnames[match[0]], fullname, '#'+anchor, descr, score, filenames[match[0]]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
},
|
||||
|
||||
/**
|
||||
* search for full-text terms in the index
|
||||
*/
|
||||
performTermsSearch : function(searchterms, excluded, terms, titleterms) {
|
||||
var docnames = this._index.docnames;
|
||||
var filenames = this._index.filenames;
|
||||
var titles = this._index.titles;
|
||||
|
||||
var i, j, file;
|
||||
var fileMap = {};
|
||||
var scoreMap = {};
|
||||
var results = [];
|
||||
|
||||
// perform the search on the required terms
|
||||
for (i = 0; i < searchterms.length; i++) {
|
||||
var word = searchterms[i];
|
||||
var files = [];
|
||||
var _o = [
|
||||
{files: terms[word], score: Scorer.term},
|
||||
{files: titleterms[word], score: Scorer.title}
|
||||
];
|
||||
|
||||
// no match but word was a required one
|
||||
if ($u.every(_o, function(o){return o.files === undefined;})) {
|
||||
break;
|
||||
}
|
||||
// found search word in contents
|
||||
$u.each(_o, function(o) {
|
||||
var _files = o.files;
|
||||
if (_files === undefined)
|
||||
return
|
||||
|
||||
if (_files.length === undefined)
|
||||
_files = [_files];
|
||||
files = files.concat(_files);
|
||||
|
||||
// set score for the word in each file to Scorer.term
|
||||
for (j = 0; j < _files.length; j++) {
|
||||
file = _files[j];
|
||||
if (!(file in scoreMap))
|
||||
scoreMap[file] = {}
|
||||
scoreMap[file][word] = o.score;
|
||||
}
|
||||
});
|
||||
|
||||
// create the mapping
|
||||
for (j = 0; j < files.length; j++) {
|
||||
file = files[j];
|
||||
if (file in fileMap)
|
||||
fileMap[file].push(word);
|
||||
else
|
||||
fileMap[file] = [word];
|
||||
}
|
||||
}
|
||||
|
||||
// now check if the files don't contain excluded terms
|
||||
for (file in fileMap) {
|
||||
var valid = true;
|
||||
|
||||
// check if all requirements are matched
|
||||
if (fileMap[file].length != searchterms.length)
|
||||
continue;
|
||||
|
||||
// ensure that none of the excluded terms is in the search result
|
||||
for (i = 0; i < excluded.length; i++) {
|
||||
if (terms[excluded[i]] == file ||
|
||||
titleterms[excluded[i]] == file ||
|
||||
$u.contains(terms[excluded[i]] || [], file) ||
|
||||
$u.contains(titleterms[excluded[i]] || [], file)) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if we have still a valid result we can add it to the result list
|
||||
if (valid) {
|
||||
// select one (max) score for the file.
|
||||
// for better ranking, we should calculate ranking by using words statistics like basic tf-idf...
|
||||
var score = $u.max($u.map(fileMap[file], function(w){return scoreMap[file][w]}));
|
||||
results.push([docnames[file], titles[file], '', null, score, filenames[file]]);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
},
|
||||
|
||||
/**
|
||||
* helper function to return a node containing the
|
||||
* search summary for a given text. keywords is a list
|
||||
* of stemmed words, hlwords is the list of normal, unstemmed
|
||||
* words. the first one is used to find the occurrence, the
|
||||
* latter for highlighting it.
|
||||
*/
|
||||
makeSearchSummary : function(text, keywords, hlwords) {
|
||||
var textLower = text.toLowerCase();
|
||||
var start = 0;
|
||||
$.each(keywords, function() {
|
||||
var i = textLower.indexOf(this.toLowerCase());
|
||||
if (i > -1)
|
||||
start = i;
|
||||
});
|
||||
start = Math.max(start - 120, 0);
|
||||
var excerpt = ((start > 0) ? '...' : '') +
|
||||
$.trim(text.substr(start, 240)) +
|
||||
((start + 240 - text.length) ? '...' : '');
|
||||
var rv = $('<div class="context"></div>').text(excerpt);
|
||||
$.each(hlwords, function() {
|
||||
rv = rv.highlightText(this, 'highlighted');
|
||||
});
|
||||
return rv;
|
||||
}
|
||||
};
|
||||
|
||||
$(document).ready(function() {
|
||||
Search.init();
|
||||
});
|
999
docs/_build/html/_static/underscore-1.3.1.js
vendored
@@ -1,999 +0,0 @@
|
||||
// Underscore.js 1.3.1
|
||||
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
|
||||
// Underscore is freely distributable under the MIT license.
|
||||
// Portions of Underscore are inspired or borrowed from Prototype,
|
||||
// Oliver Steele's Functional, and John Resig's Micro-Templating.
|
||||
// For all details and documentation:
|
||||
// http://documentcloud.github.com/underscore
|
||||
|
||||
(function() {
|
||||
|
||||
// Baseline setup
|
||||
// --------------
|
||||
|
||||
// Establish the root object, `window` in the browser, or `global` on the server.
|
||||
var root = this;
|
||||
|
||||
// Save the previous value of the `_` variable.
|
||||
var previousUnderscore = root._;
|
||||
|
||||
// Establish the object that gets returned to break out of a loop iteration.
|
||||
var breaker = {};
|
||||
|
||||
// Save bytes in the minified (but not gzipped) version:
|
||||
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
|
||||
|
||||
// Create quick reference variables for speed access to core prototypes.
|
||||
var slice = ArrayProto.slice,
|
||||
unshift = ArrayProto.unshift,
|
||||
toString = ObjProto.toString,
|
||||
hasOwnProperty = ObjProto.hasOwnProperty;
|
||||
|
||||
// All **ECMAScript 5** native function implementations that we hope to use
|
||||
// are declared here.
|
||||
var
|
||||
nativeForEach = ArrayProto.forEach,
|
||||
nativeMap = ArrayProto.map,
|
||||
nativeReduce = ArrayProto.reduce,
|
||||
nativeReduceRight = ArrayProto.reduceRight,
|
||||
nativeFilter = ArrayProto.filter,
|
||||
nativeEvery = ArrayProto.every,
|
||||
nativeSome = ArrayProto.some,
|
||||
nativeIndexOf = ArrayProto.indexOf,
|
||||
nativeLastIndexOf = ArrayProto.lastIndexOf,
|
||||
nativeIsArray = Array.isArray,
|
||||
nativeKeys = Object.keys,
|
||||
nativeBind = FuncProto.bind;
|
||||
|
||||
// Create a safe reference to the Underscore object for use below.
|
||||
var _ = function(obj) { return new wrapper(obj); };
|
||||
|
||||
// Export the Underscore object for **Node.js**, with
|
||||
// backwards-compatibility for the old `require()` API. If we're in
|
||||
// the browser, add `_` as a global object via a string identifier,
|
||||
// for Closure Compiler "advanced" mode.
|
||||
if (typeof exports !== 'undefined') {
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
exports = module.exports = _;
|
||||
}
|
||||
exports._ = _;
|
||||
} else {
|
||||
root['_'] = _;
|
||||
}
|
||||
|
||||
// Current version.
|
||||
_.VERSION = '1.3.1';
|
||||
|
||||
// Collection Functions
|
||||
// --------------------
|
||||
|
||||
// The cornerstone, an `each` implementation, aka `forEach`.
|
||||
// Handles objects with the built-in `forEach`, arrays, and raw objects.
|
||||
// Delegates to **ECMAScript 5**'s native `forEach` if available.
|
||||
var each = _.each = _.forEach = function(obj, iterator, context) {
|
||||
if (obj == null) return;
|
||||
if (nativeForEach && obj.forEach === nativeForEach) {
|
||||
obj.forEach(iterator, context);
|
||||
} else if (obj.length === +obj.length) {
|
||||
for (var i = 0, l = obj.length; i < l; i++) {
|
||||
if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
|
||||
}
|
||||
} else {
|
||||
for (var key in obj) {
|
||||
if (_.has(obj, key)) {
|
||||
if (iterator.call(context, obj[key], key, obj) === breaker) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Return the results of applying the iterator to each element.
|
||||
// Delegates to **ECMAScript 5**'s native `map` if available.
|
||||
_.map = _.collect = function(obj, iterator, context) {
|
||||
var results = [];
|
||||
if (obj == null) return results;
|
||||
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
|
||||
each(obj, function(value, index, list) {
|
||||
results[results.length] = iterator.call(context, value, index, list);
|
||||
});
|
||||
if (obj.length === +obj.length) results.length = obj.length;
|
||||
return results;
|
||||
};
|
||||
|
||||
// **Reduce** builds up a single result from a list of values, aka `inject`,
|
||||
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
|
||||
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
|
||||
var initial = arguments.length > 2;
|
||||
if (obj == null) obj = [];
|
||||
if (nativeReduce && obj.reduce === nativeReduce) {
|
||||
if (context) iterator = _.bind(iterator, context);
|
||||
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
|
||||
}
|
||||
each(obj, function(value, index, list) {
|
||||
if (!initial) {
|
||||
memo = value;
|
||||
initial = true;
|
||||
} else {
|
||||
memo = iterator.call(context, memo, value, index, list);
|
||||
}
|
||||
});
|
||||
if (!initial) throw new TypeError('Reduce of empty array with no initial value');
|
||||
return memo;
|
||||
};
|
||||
|
||||
// The right-associative version of reduce, also known as `foldr`.
|
||||
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
|
||||
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
|
||||
var initial = arguments.length > 2;
|
||||
if (obj == null) obj = [];
|
||||
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
|
||||
if (context) iterator = _.bind(iterator, context);
|
||||
return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
|
||||
}
|
||||
var reversed = _.toArray(obj).reverse();
|
||||
if (context && !initial) iterator = _.bind(iterator, context);
|
||||
return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);
|
||||
};
|
||||
|
||||
// Return the first value which passes a truth test. Aliased as `detect`.
|
||||
_.find = _.detect = function(obj, iterator, context) {
|
||||
var result;
|
||||
any(obj, function(value, index, list) {
|
||||
if (iterator.call(context, value, index, list)) {
|
||||
result = value;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
// Return all the elements that pass a truth test.
|
||||
// Delegates to **ECMAScript 5**'s native `filter` if available.
|
||||
// Aliased as `select`.
|
||||
_.filter = _.select = function(obj, iterator, context) {
|
||||
var results = [];
|
||||
if (obj == null) return results;
|
||||
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
|
||||
each(obj, function(value, index, list) {
|
||||
if (iterator.call(context, value, index, list)) results[results.length] = value;
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
// Return all the elements for which a truth test fails.
|
||||
_.reject = function(obj, iterator, context) {
|
||||
var results = [];
|
||||
if (obj == null) return results;
|
||||
each(obj, function(value, index, list) {
|
||||
if (!iterator.call(context, value, index, list)) results[results.length] = value;
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
// Determine whether all of the elements match a truth test.
|
||||
// Delegates to **ECMAScript 5**'s native `every` if available.
|
||||
// Aliased as `all`.
|
||||
_.every = _.all = function(obj, iterator, context) {
|
||||
var result = true;
|
||||
if (obj == null) return result;
|
||||
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
|
||||
each(obj, function(value, index, list) {
|
||||
if (!(result = result && iterator.call(context, value, index, list))) return breaker;
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
// Determine if at least one element in the object matches a truth test.
|
||||
// Delegates to **ECMAScript 5**'s native `some` if available.
|
||||
// Aliased as `any`.
|
||||
var any = _.some = _.any = function(obj, iterator, context) {
|
||||
iterator || (iterator = _.identity);
|
||||
var result = false;
|
||||
if (obj == null) return result;
|
||||
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
|
||||
each(obj, function(value, index, list) {
|
||||
if (result || (result = iterator.call(context, value, index, list))) return breaker;
|
||||
});
|
||||
return !!result;
|
||||
};
|
||||
|
||||
// Determine if a given value is included in the array or object using `===`.
|
||||
// Aliased as `contains`.
|
||||
_.include = _.contains = function(obj, target) {
|
||||
var found = false;
|
||||
if (obj == null) return found;
|
||||
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
|
||||
found = any(obj, function(value) {
|
||||
return value === target;
|
||||
});
|
||||
return found;
|
||||
};
|
||||
|
||||
// Invoke a method (with arguments) on every item in a collection.
|
||||
_.invoke = function(obj, method) {
|
||||
var args = slice.call(arguments, 2);
|
||||
return _.map(obj, function(value) {
|
||||
return (_.isFunction(method) ? method || value : value[method]).apply(value, args);
|
||||
});
|
||||
};
|
||||
|
||||
// Convenience version of a common use case of `map`: fetching a property.
|
||||
_.pluck = function(obj, key) {
|
||||
return _.map(obj, function(value){ return value[key]; });
|
||||
};
|
||||
|
||||
// Return the maximum element or (element-based computation).
|
||||
_.max = function(obj, iterator, context) {
|
||||
if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
|
||||
if (!iterator && _.isEmpty(obj)) return -Infinity;
|
||||
var result = {computed : -Infinity};
|
||||
each(obj, function(value, index, list) {
|
||||
var computed = iterator ? iterator.call(context, value, index, list) : value;
|
||||
computed >= result.computed && (result = {value : value, computed : computed});
|
||||
});
|
||||
return result.value;
|
||||
};
|
||||
|
||||
// Return the minimum element (or element-based computation).
|
||||
_.min = function(obj, iterator, context) {
|
||||
if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
|
||||
if (!iterator && _.isEmpty(obj)) return Infinity;
|
||||
var result = {computed : Infinity};
|
||||
each(obj, function(value, index, list) {
|
||||
var computed = iterator ? iterator.call(context, value, index, list) : value;
|
||||
computed < result.computed && (result = {value : value, computed : computed});
|
||||
});
|
||||
return result.value;
|
||||
};
|
||||
|
||||
// Shuffle an array.
|
||||
_.shuffle = function(obj) {
|
||||
var shuffled = [], rand;
|
||||
each(obj, function(value, index, list) {
|
||||
if (index == 0) {
|
||||
shuffled[0] = value;
|
||||
} else {
|
||||
rand = Math.floor(Math.random() * (index + 1));
|
||||
shuffled[index] = shuffled[rand];
|
||||
shuffled[rand] = value;
|
||||
}
|
||||
});
|
||||
return shuffled;
|
||||
};
|
||||
|
||||
// Sort the object's values by a criterion produced by an iterator.
|
||||
_.sortBy = function(obj, iterator, context) {
|
||||
return _.pluck(_.map(obj, function(value, index, list) {
|
||||
return {
|
||||
value : value,
|
||||
criteria : iterator.call(context, value, index, list)
|
||||
};
|
||||
}).sort(function(left, right) {
|
||||
var a = left.criteria, b = right.criteria;
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
}), 'value');
|
||||
};
|
||||
|
||||
// Groups the object's values by a criterion. Pass either a string attribute
|
||||
// to group by, or a function that returns the criterion.
|
||||
_.groupBy = function(obj, val) {
|
||||
var result = {};
|
||||
var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
|
||||
each(obj, function(value, index) {
|
||||
var key = iterator(value, index);
|
||||
(result[key] || (result[key] = [])).push(value);
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
// Use a comparator function to figure out at what index an object should
|
||||
// be inserted so as to maintain order. Uses binary search.
|
||||
_.sortedIndex = function(array, obj, iterator) {
|
||||
iterator || (iterator = _.identity);
|
||||
var low = 0, high = array.length;
|
||||
while (low < high) {
|
||||
var mid = (low + high) >> 1;
|
||||
iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
|
||||
}
|
||||
return low;
|
||||
};
|
||||
|
||||
// Safely convert anything iterable into a real, live array.
|
||||
_.toArray = function(iterable) {
|
||||
if (!iterable) return [];
|
||||
if (iterable.toArray) return iterable.toArray();
|
||||
if (_.isArray(iterable)) return slice.call(iterable);
|
||||
if (_.isArguments(iterable)) return slice.call(iterable);
|
||||
return _.values(iterable);
|
||||
};
|
||||
|
||||
// Return the number of elements in an object.
|
||||
_.size = function(obj) {
|
||||
return _.toArray(obj).length;
|
||||
};
|
||||
|
||||
// Array Functions
|
||||
// ---------------
|
||||
|
||||
// Get the first element of an array. Passing **n** will return the first N
|
||||
// values in the array. Aliased as `head`. The **guard** check allows it to work
|
||||
// with `_.map`.
|
||||
_.first = _.head = function(array, n, guard) {
|
||||
return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
|
||||
};
|
||||
|
||||
// Returns everything but the last entry of the array. Especcialy useful on
|
||||
// the arguments object. Passing **n** will return all the values in
|
||||
// the array, excluding the last N. The **guard** check allows it to work with
|
||||
// `_.map`.
|
||||
_.initial = function(array, n, guard) {
|
||||
return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
|
||||
};
|
||||
|
||||
// Get the last element of an array. Passing **n** will return the last N
|
||||
// values in the array. The **guard** check allows it to work with `_.map`.
|
||||
_.last = function(array, n, guard) {
|
||||
if ((n != null) && !guard) {
|
||||
return slice.call(array, Math.max(array.length - n, 0));
|
||||
} else {
|
||||
return array[array.length - 1];
|
||||
}
|
||||
};
|
||||
|
||||
// Returns everything but the first entry of the array. Aliased as `tail`.
|
||||
// Especially useful on the arguments object. Passing an **index** will return
|
||||
// the rest of the values in the array from that index onward. The **guard**
|
||||
// check allows it to work with `_.map`.
|
||||
_.rest = _.tail = function(array, index, guard) {
|
||||
return slice.call(array, (index == null) || guard ? 1 : index);
|
||||
};
|
||||
|
||||
// Trim out all falsy values from an array.
|
||||
_.compact = function(array) {
|
||||
return _.filter(array, function(value){ return !!value; });
|
||||
};
|
||||
|
||||
// Return a completely flattened version of an array.
|
||||
_.flatten = function(array, shallow) {
|
||||
return _.reduce(array, function(memo, value) {
|
||||
if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value));
|
||||
memo[memo.length] = value;
|
||||
return memo;
|
||||
}, []);
|
||||
};
|
||||
|
||||
// Return a version of the array that does not contain the specified value(s).
|
||||
_.without = function(array) {
|
||||
return _.difference(array, slice.call(arguments, 1));
|
||||
};
|
||||
|
||||
// Produce a duplicate-free version of the array. If the array has already
|
||||
// been sorted, you have the option of using a faster algorithm.
|
||||
// Aliased as `unique`.
|
||||
_.uniq = _.unique = function(array, isSorted, iterator) {
|
||||
var initial = iterator ? _.map(array, iterator) : array;
|
||||
var result = [];
|
||||
_.reduce(initial, function(memo, el, i) {
|
||||
if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) {
|
||||
memo[memo.length] = el;
|
||||
result[result.length] = array[i];
|
||||
}
|
||||
return memo;
|
||||
}, []);
|
||||
return result;
|
||||
};
|
||||
|
||||
// Produce an array that contains the union: each distinct element from all of
|
||||
// the passed-in arrays.
|
||||
_.union = function() {
|
||||
return _.uniq(_.flatten(arguments, true));
|
||||
};
|
||||
|
||||
// Produce an array that contains every item shared between all the
|
||||
// passed-in arrays. (Aliased as "intersect" for back-compat.)
|
||||
_.intersection = _.intersect = function(array) {
|
||||
var rest = slice.call(arguments, 1);
|
||||
return _.filter(_.uniq(array), function(item) {
|
||||
return _.every(rest, function(other) {
|
||||
return _.indexOf(other, item) >= 0;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Take the difference between one array and a number of other arrays.
|
||||
// Only the elements present in just the first array will remain.
|
||||
_.difference = function(array) {
|
||||
var rest = _.flatten(slice.call(arguments, 1));
|
||||
return _.filter(array, function(value){ return !_.include(rest, value); });
|
||||
};
|
||||
|
||||
// Zip together multiple lists into a single array -- elements that share
|
||||
// an index go together.
|
||||
_.zip = function() {
|
||||
var args = slice.call(arguments);
|
||||
var length = _.max(_.pluck(args, 'length'));
|
||||
var results = new Array(length);
|
||||
for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
|
||||
return results;
|
||||
};
|
||||
|
||||
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
|
||||
// we need this function. Return the position of the first occurrence of an
|
||||
// item in an array, or -1 if the item is not included in the array.
|
||||
// Delegates to **ECMAScript 5**'s native `indexOf` if available.
|
||||
// If the array is large and already in sort order, pass `true`
|
||||
// for **isSorted** to use binary search.
|
||||
_.indexOf = function(array, item, isSorted) {
|
||||
if (array == null) return -1;
|
||||
var i, l;
|
||||
if (isSorted) {
|
||||
i = _.sortedIndex(array, item);
|
||||
return array[i] === item ? i : -1;
|
||||
}
|
||||
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
|
||||
for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i;
|
||||
return -1;
|
||||
};
|
||||
|
||||
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
|
||||
_.lastIndexOf = function(array, item) {
|
||||
if (array == null) return -1;
|
||||
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
|
||||
var i = array.length;
|
||||
while (i--) if (i in array && array[i] === item) return i;
|
||||
return -1;
|
||||
};
|
||||
|
||||
// Generate an integer Array containing an arithmetic progression. A port of
|
||||
// the native Python `range()` function. See
|
||||
// [the Python documentation](http://docs.python.org/library/functions.html#range).
|
||||
_.range = function(start, stop, step) {
|
||||
if (arguments.length <= 1) {
|
||||
stop = start || 0;
|
||||
start = 0;
|
||||
}
|
||||
step = arguments[2] || 1;
|
||||
|
||||
var len = Math.max(Math.ceil((stop - start) / step), 0);
|
||||
var idx = 0;
|
||||
var range = new Array(len);
|
||||
|
||||
while(idx < len) {
|
||||
range[idx++] = start;
|
||||
start += step;
|
||||
}
|
||||
|
||||
return range;
|
||||
};
|
||||
|
||||
// Function (ahem) Functions
|
||||
// ------------------
|
||||
|
||||
// Reusable constructor function for prototype setting.
|
||||
var ctor = function(){};
|
||||
|
||||
// Create a function bound to a given object (assigning `this`, and arguments,
|
||||
// optionally). Binding with arguments is also known as `curry`.
|
||||
// Delegates to **ECMAScript 5**'s native `Function.bind` if available.
|
||||
// We check for `func.bind` first, to fail fast when `func` is undefined.
|
||||
_.bind = function bind(func, context) {
|
||||
var bound, args;
|
||||
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
|
||||
if (!_.isFunction(func)) throw new TypeError;
|
||||
args = slice.call(arguments, 2);
|
||||
return bound = function() {
|
||||
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
|
||||
ctor.prototype = func.prototype;
|
||||
var self = new ctor;
|
||||
var result = func.apply(self, args.concat(slice.call(arguments)));
|
||||
if (Object(result) === result) return result;
|
||||
return self;
|
||||
};
|
||||
};
|
||||
|
||||
// Bind all of an object's methods to that object. Useful for ensuring that
|
||||
// all callbacks defined on an object belong to it.
|
||||
_.bindAll = function(obj) {
|
||||
var funcs = slice.call(arguments, 1);
|
||||
if (funcs.length == 0) funcs = _.functions(obj);
|
||||
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
|
||||
return obj;
|
||||
};
|
||||
|
||||
// Memoize an expensive function by storing its results.
|
||||
_.memoize = function(func, hasher) {
|
||||
var memo = {};
|
||||
hasher || (hasher = _.identity);
|
||||
return function() {
|
||||
var key = hasher.apply(this, arguments);
|
||||
return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
|
||||
};
|
||||
};
|
||||
|
||||
// Delays a function for the given number of milliseconds, and then calls
|
||||
// it with the arguments supplied.
|
||||
_.delay = function(func, wait) {
|
||||
var args = slice.call(arguments, 2);
|
||||
return setTimeout(function(){ return func.apply(func, args); }, wait);
|
||||
};
|
||||
|
||||
// Defers a function, scheduling it to run after the current call stack has
|
||||
// cleared.
|
||||
_.defer = function(func) {
|
||||
return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
|
||||
};
|
||||
|
||||
// Returns a function, that, when invoked, will only be triggered at most once
|
||||
// during a given window of time.
|
||||
_.throttle = function(func, wait) {
|
||||
var context, args, timeout, throttling, more;
|
||||
var whenDone = _.debounce(function(){ more = throttling = false; }, wait);
|
||||
return function() {
|
||||
context = this; args = arguments;
|
||||
var later = function() {
|
||||
timeout = null;
|
||||
if (more) func.apply(context, args);
|
||||
whenDone();
|
||||
};
|
||||
if (!timeout) timeout = setTimeout(later, wait);
|
||||
if (throttling) {
|
||||
more = true;
|
||||
} else {
|
||||
func.apply(context, args);
|
||||
}
|
||||
whenDone();
|
||||
throttling = true;
|
||||
};
|
||||
};
|
||||
|
||||
// Returns a function, that, as long as it continues to be invoked, will not
|
||||
// be triggered. The function will be called after it stops being called for
|
||||
// N milliseconds.
|
||||
_.debounce = function(func, wait) {
|
||||
var timeout;
|
||||
return function() {
|
||||
var context = this, args = arguments;
|
||||
var later = function() {
|
||||
timeout = null;
|
||||
func.apply(context, args);
|
||||
};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
};
|
||||
};
|
||||
|
||||
// Returns a function that will be executed at most one time, no matter how
|
||||
// often you call it. Useful for lazy initialization.
|
||||
_.once = function(func) {
|
||||
var ran = false, memo;
|
||||
return function() {
|
||||
if (ran) return memo;
|
||||
ran = true;
|
||||
return memo = func.apply(this, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
// Returns the first function passed as an argument to the second,
|
||||
// allowing you to adjust arguments, run code before and after, and
|
||||
// conditionally execute the original function.
|
||||
_.wrap = function(func, wrapper) {
|
||||
return function() {
|
||||
var args = [func].concat(slice.call(arguments, 0));
|
||||
return wrapper.apply(this, args);
|
||||
};
|
||||
};
|
||||
|
||||
// Returns a function that is the composition of a list of functions, each
|
||||
// consuming the return value of the function that follows.
|
||||
_.compose = function() {
|
||||
var funcs = arguments;
|
||||
return function() {
|
||||
var args = arguments;
|
||||
for (var i = funcs.length - 1; i >= 0; i--) {
|
||||
args = [funcs[i].apply(this, args)];
|
||||
}
|
||||
return args[0];
|
||||
};
|
||||
};
|
||||
|
||||
// Returns a function that will only be executed after being called N times.
|
||||
_.after = function(times, func) {
|
||||
if (times <= 0) return func();
|
||||
return function() {
|
||||
if (--times < 1) { return func.apply(this, arguments); }
|
||||
};
|
||||
};
|
||||
|
||||
// Object Functions
|
||||
// ----------------
|
||||
|
||||
// Retrieve the names of an object's properties.
|
||||
// Delegates to **ECMAScript 5**'s native `Object.keys`
|
||||
_.keys = nativeKeys || function(obj) {
|
||||
if (obj !== Object(obj)) throw new TypeError('Invalid object');
|
||||
var keys = [];
|
||||
for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
|
||||
return keys;
|
||||
};
|
||||
|
||||
// Retrieve the values of an object's properties.
|
||||
_.values = function(obj) {
|
||||
return _.map(obj, _.identity);
|
||||
};
|
||||
|
||||
// Return a sorted list of the function names available on the object.
|
||||
// Aliased as `methods`
|
||||
_.functions = _.methods = function(obj) {
|
||||
var names = [];
|
||||
for (var key in obj) {
|
||||
if (_.isFunction(obj[key])) names.push(key);
|
||||
}
|
||||
return names.sort();
|
||||
};
|
||||
|
||||
// Extend a given object with all the properties in passed-in object(s).
|
||||
_.extend = function(obj) {
|
||||
each(slice.call(arguments, 1), function(source) {
|
||||
for (var prop in source) {
|
||||
obj[prop] = source[prop];
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
};
|
||||
|
||||
// Fill in a given object with default properties.
|
||||
_.defaults = function(obj) {
|
||||
each(slice.call(arguments, 1), function(source) {
|
||||
for (var prop in source) {
|
||||
if (obj[prop] == null) obj[prop] = source[prop];
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
};
|
||||
|
||||
// Create a (shallow-cloned) duplicate of an object.
|
||||
_.clone = function(obj) {
|
||||
if (!_.isObject(obj)) return obj;
|
||||
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
|
||||
};
|
||||
|
||||
// Invokes interceptor with the obj, and then returns obj.
|
||||
// The primary purpose of this method is to "tap into" a method chain, in
|
||||
// order to perform operations on intermediate results within the chain.
|
||||
_.tap = function(obj, interceptor) {
|
||||
interceptor(obj);
|
||||
return obj;
|
||||
};
|
||||
|
||||
// Internal recursive comparison function.
|
||||
function eq(a, b, stack) {
|
||||
// Identical objects are equal. `0 === -0`, but they aren't identical.
|
||||
// See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
|
||||
if (a === b) return a !== 0 || 1 / a == 1 / b;
|
||||
// A strict comparison is necessary because `null == undefined`.
|
||||
if (a == null || b == null) return a === b;
|
||||
// Unwrap any wrapped objects.
|
||||
if (a._chain) a = a._wrapped;
|
||||
if (b._chain) b = b._wrapped;
|
||||
// Invoke a custom `isEqual` method if one is provided.
|
||||
if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b);
|
||||
if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a);
|
||||
// Compare `[[Class]]` names.
|
||||
var className = toString.call(a);
|
||||
if (className != toString.call(b)) return false;
|
||||
switch (className) {
|
||||
// Strings, numbers, dates, and booleans are compared by value.
|
||||
case '[object String]':
|
||||
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
|
||||
// equivalent to `new String("5")`.
|
||||
return a == String(b);
|
||||
case '[object Number]':
|
||||
// `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
|
||||
// other numeric values.
|
||||
return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
|
||||
case '[object Date]':
|
||||
case '[object Boolean]':
|
||||
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
|
||||
// millisecond representations. Note that invalid dates with millisecond representations
|
||||
// of `NaN` are not equivalent.
|
||||
return +a == +b;
|
||||
// RegExps are compared by their source patterns and flags.
|
||||
case '[object RegExp]':
|
||||
return a.source == b.source &&
|
||||
a.global == b.global &&
|
||||
a.multiline == b.multiline &&
|
||||
a.ignoreCase == b.ignoreCase;
|
||||
}
|
||||
if (typeof a != 'object' || typeof b != 'object') return false;
|
||||
// Assume equality for cyclic structures. The algorithm for detecting cyclic
|
||||
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
|
||||
var length = stack.length;
|
||||
while (length--) {
|
||||
// Linear search. Performance is inversely proportional to the number of
|
||||
// unique nested structures.
|
||||
if (stack[length] == a) return true;
|
||||
}
|
||||
// Add the first object to the stack of traversed objects.
|
||||
stack.push(a);
|
||||
var size = 0, result = true;
|
||||
// Recursively compare objects and arrays.
|
||||
if (className == '[object Array]') {
|
||||
// Compare array lengths to determine if a deep comparison is necessary.
|
||||
size = a.length;
|
||||
result = size == b.length;
|
||||
if (result) {
|
||||
// Deep compare the contents, ignoring non-numeric properties.
|
||||
while (size--) {
|
||||
// Ensure commutative equality for sparse arrays.
|
||||
if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Objects with different constructors are not equivalent.
|
||||
if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false;
|
||||
// Deep compare objects.
|
||||
for (var key in a) {
|
||||
if (_.has(a, key)) {
|
||||
// Count the expected number of properties.
|
||||
size++;
|
||||
// Deep compare each member.
|
||||
if (!(result = _.has(b, key) && eq(a[key], b[key], stack))) break;
|
||||
}
|
||||
}
|
||||
// Ensure that both objects contain the same number of properties.
|
||||
if (result) {
|
||||
for (key in b) {
|
||||
if (_.has(b, key) && !(size--)) break;
|
||||
}
|
||||
result = !size;
|
||||
}
|
||||
}
|
||||
// Remove the first object from the stack of traversed objects.
|
||||
stack.pop();
|
||||
return result;
|
||||
}
|
||||
|
||||
// Perform a deep comparison to check if two objects are equal.
|
||||
_.isEqual = function(a, b) {
|
||||
return eq(a, b, []);
|
||||
};
|
||||
|
||||
// Is a given array, string, or object empty?
|
||||
// An "empty" object has no enumerable own-properties.
|
||||
_.isEmpty = function(obj) {
|
||||
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
|
||||
for (var key in obj) if (_.has(obj, key)) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
// Is a given value a DOM element?
|
||||
_.isElement = function(obj) {
|
||||
return !!(obj && obj.nodeType == 1);
|
||||
};
|
||||
|
||||
// Is a given value an array?
|
||||
// Delegates to ECMA5's native Array.isArray
|
||||
_.isArray = nativeIsArray || function(obj) {
|
||||
return toString.call(obj) == '[object Array]';
|
||||
};
|
||||
|
||||
// Is a given variable an object?
|
||||
_.isObject = function(obj) {
|
||||
return obj === Object(obj);
|
||||
};
|
||||
|
||||
// Is a given variable an arguments object?
|
||||
_.isArguments = function(obj) {
|
||||
return toString.call(obj) == '[object Arguments]';
|
||||
};
|
||||
if (!_.isArguments(arguments)) {
|
||||
_.isArguments = function(obj) {
|
||||
return !!(obj && _.has(obj, 'callee'));
|
||||
};
|
||||
}
|
||||
|
||||
// Is a given value a function?
|
||||
_.isFunction = function(obj) {
|
||||
return toString.call(obj) == '[object Function]';
|
||||
};
|
||||
|
||||
// Is a given value a string?
|
||||
_.isString = function(obj) {
|
||||
return toString.call(obj) == '[object String]';
|
||||
};
|
||||
|
||||
// Is a given value a number?
|
||||
_.isNumber = function(obj) {
|
||||
return toString.call(obj) == '[object Number]';
|
||||
};
|
||||
|
||||
// Is the given value `NaN`?
|
||||
_.isNaN = function(obj) {
|
||||
// `NaN` is the only value for which `===` is not reflexive.
|
||||
return obj !== obj;
|
||||
};
|
||||
|
||||
// Is a given value a boolean?
|
||||
_.isBoolean = function(obj) {
|
||||
return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
|
||||
};
|
||||
|
||||
// Is a given value a date?
|
||||
_.isDate = function(obj) {
|
||||
return toString.call(obj) == '[object Date]';
|
||||
};
|
||||
|
||||
// Is the given value a regular expression?
|
||||
_.isRegExp = function(obj) {
|
||||
return toString.call(obj) == '[object RegExp]';
|
||||
};
|
||||
|
||||
// Is a given value equal to null?
|
||||
_.isNull = function(obj) {
|
||||
return obj === null;
|
||||
};
|
||||
|
||||
// Is a given variable undefined?
|
||||
_.isUndefined = function(obj) {
|
||||
return obj === void 0;
|
||||
};
|
||||
|
||||
// Has own property?
|
||||
_.has = function(obj, key) {
|
||||
return hasOwnProperty.call(obj, key);
|
||||
};
|
||||
|
||||
// Utility Functions
|
||||
// -----------------
|
||||
|
||||
// Run Underscore.js in *noConflict* mode, returning the `_` variable to its
|
||||
// previous owner. Returns a reference to the Underscore object.
|
||||
_.noConflict = function() {
|
||||
root._ = previousUnderscore;
|
||||
return this;
|
||||
};
|
||||
|
||||
// Keep the identity function around for default iterators.
|
||||
_.identity = function(value) {
|
||||
return value;
|
||||
};
|
||||
|
||||
// Run a function **n** times.
|
||||
_.times = function (n, iterator, context) {
|
||||
for (var i = 0; i < n; i++) iterator.call(context, i);
|
||||
};
|
||||
|
||||
// Escape a string for HTML interpolation.
|
||||
_.escape = function(string) {
|
||||
return (''+string).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g,'/');
|
||||
};
|
||||
|
||||
// Add your own custom functions to the Underscore object, ensuring that
|
||||
// they're correctly added to the OOP wrapper as well.
|
||||
_.mixin = function(obj) {
|
||||
each(_.functions(obj), function(name){
|
||||
addToWrapper(name, _[name] = obj[name]);
|
||||
});
|
||||
};
|
||||
|
||||
// Generate a unique integer id (unique within the entire client session).
|
||||
// Useful for temporary DOM ids.
|
||||
var idCounter = 0;
|
||||
_.uniqueId = function(prefix) {
|
||||
var id = idCounter++;
|
||||
return prefix ? prefix + id : id;
|
||||
};
|
||||
|
||||
// By default, Underscore uses ERB-style template delimiters, change the
|
||||
// following template settings to use alternative delimiters.
|
||||
_.templateSettings = {
|
||||
evaluate : /<%([\s\S]+?)%>/g,
|
||||
interpolate : /<%=([\s\S]+?)%>/g,
|
||||
escape : /<%-([\s\S]+?)%>/g
|
||||
};
|
||||
|
||||
// When customizing `templateSettings`, if you don't want to define an
|
||||
// interpolation, evaluation or escaping regex, we need one that is
|
||||
// guaranteed not to match.
|
||||
var noMatch = /.^/;
|
||||
|
||||
// Within an interpolation, evaluation, or escaping, remove HTML escaping
|
||||
// that had been previously added.
|
||||
var unescape = function(code) {
|
||||
return code.replace(/\\\\/g, '\\').replace(/\\'/g, "'");
|
||||
};
|
||||
|
||||
// JavaScript micro-templating, similar to John Resig's implementation.
|
||||
// Underscore templating handles arbitrary delimiters, preserves whitespace,
|
||||
// and correctly escapes quotes within interpolated code.
|
||||
_.template = function(str, data) {
|
||||
var c = _.templateSettings;
|
||||
var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
|
||||
'with(obj||{}){__p.push(\'' +
|
||||
str.replace(/\\/g, '\\\\')
|
||||
.replace(/'/g, "\\'")
|
||||
.replace(c.escape || noMatch, function(match, code) {
|
||||
return "',_.escape(" + unescape(code) + "),'";
|
||||
})
|
||||
.replace(c.interpolate || noMatch, function(match, code) {
|
||||
return "'," + unescape(code) + ",'";
|
||||
})
|
||||
.replace(c.evaluate || noMatch, function(match, code) {
|
||||
return "');" + unescape(code).replace(/[\r\n\t]/g, ' ') + ";__p.push('";
|
||||
})
|
||||
.replace(/\r/g, '\\r')
|
||||
.replace(/\n/g, '\\n')
|
||||
.replace(/\t/g, '\\t')
|
||||
+ "');}return __p.join('');";
|
||||
var func = new Function('obj', '_', tmpl);
|
||||
if (data) return func(data, _);
|
||||
return function(data) {
|
||||
return func.call(this, data, _);
|
||||
};
|
||||
};
|
||||
|
||||
// Add a "chain" function, which will delegate to the wrapper.
|
||||
_.chain = function(obj) {
|
||||
return _(obj).chain();
|
||||
};
|
||||
|
||||
// The OOP Wrapper
|
||||
// ---------------
|
||||
|
||||
// If Underscore is called as a function, it returns a wrapped object that
|
||||
// can be used OO-style. This wrapper holds altered versions of all the
|
||||
// underscore functions. Wrapped objects may be chained.
|
||||
var wrapper = function(obj) { this._wrapped = obj; };
|
||||
|
||||
// Expose `wrapper.prototype` as `_.prototype`
|
||||
_.prototype = wrapper.prototype;
|
||||
|
||||
// Helper function to continue chaining intermediate results.
|
||||
var result = function(obj, chain) {
|
||||
return chain ? _(obj).chain() : obj;
|
||||
};
|
||||
|
||||
// A method to easily add functions to the OOP wrapper.
|
||||
var addToWrapper = function(name, func) {
|
||||
wrapper.prototype[name] = function() {
|
||||
var args = slice.call(arguments);
|
||||
unshift.call(args, this._wrapped);
|
||||
return result(func.apply(_, args), this._chain);
|
||||
};
|
||||
};
|
||||
|
||||
// Add all of the Underscore functions to the wrapper object.
|
||||
_.mixin(_);
|
||||
|
||||
// Add all mutator Array functions to the wrapper.
|
||||
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
|
||||
var method = ArrayProto[name];
|
||||
wrapper.prototype[name] = function() {
|
||||
var wrapped = this._wrapped;
|
||||
method.apply(wrapped, arguments);
|
||||
var length = wrapped.length;
|
||||
if ((name == 'shift' || name == 'splice') && length === 0) delete wrapped[0];
|
||||
return result(wrapped, this._chain);
|
||||
};
|
||||
});
|
||||
|
||||
// Add all accessor Array functions to the wrapper.
|
||||
each(['concat', 'join', 'slice'], function(name) {
|
||||
var method = ArrayProto[name];
|
||||
wrapper.prototype[name] = function() {
|
||||
return result(method.apply(this._wrapped, arguments), this._chain);
|
||||
};
|
||||
});
|
||||
|
||||
// Start chaining a wrapped Underscore object.
|
||||
wrapper.prototype.chain = function() {
|
||||
this._chain = true;
|
||||
return this;
|
||||
};
|
||||
|
||||
// Extracts the result from a wrapped and chained object.
|
||||
wrapper.prototype.value = function() {
|
||||
return this._wrapped;
|
||||
};
|
||||
|
||||
}).call(this);
|
31
docs/_build/html/_static/underscore.js
vendored
@@ -1,31 +0,0 @@
|
||||
// Underscore.js 1.3.1
|
||||
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
|
||||
// Underscore is freely distributable under the MIT license.
|
||||
// Portions of Underscore are inspired or borrowed from Prototype,
|
||||
// Oliver Steele's Functional, and John Resig's Micro-Templating.
|
||||
// For all details and documentation:
|
||||
// http://documentcloud.github.com/underscore
|
||||
(function(){function q(a,c,d){if(a===c)return a!==0||1/a==1/c;if(a==null||c==null)return a===c;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return false;switch(e){case "[object String]":return a==String(c);case "[object Number]":return a!=+a?c!=+c:a==0?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source==
|
||||
c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){if(f=a.length,g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&q(a[f],c[f],d)))break}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return false;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&q(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c,
|
||||
h)&&!f--)break;g=!f}}d.pop();return g}var r=this,G=r._,n={},k=Array.prototype,o=Object.prototype,i=k.slice,H=k.unshift,l=o.toString,I=o.hasOwnProperty,w=k.forEach,x=k.map,y=k.reduce,z=k.reduceRight,A=k.filter,B=k.every,C=k.some,p=k.indexOf,D=k.lastIndexOf,o=Array.isArray,J=Object.keys,s=Function.prototype.bind,b=function(a){return new m(a)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=b;exports._=b}else r._=b;b.VERSION="1.3.1";var j=b.each=
|
||||
b.forEach=function(a,c,d){if(a!=null)if(w&&a.forEach===w)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(e in a&&c.call(d,a[e],e,a)===n)break}else for(e in a)if(b.has(a,e)&&c.call(d,a[e],e,a)===n)break};b.map=b.collect=function(a,c,b){var e=[];if(a==null)return e;if(x&&a.map===x)return a.map(c,b);j(a,function(a,g,h){e[e.length]=c.call(b,a,g,h)});if(a.length===+a.length)e.length=a.length;return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){var f=arguments.length>2;a==
|
||||
null&&(a=[]);if(y&&a.reduce===y)return e&&(c=b.bind(c,e)),f?a.reduce(c,d):a.reduce(c);j(a,function(a,b,i){f?d=c.call(e,d,a,b,i):(d=a,f=true)});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(z&&a.reduceRight===z)return e&&(c=b.bind(c,e)),f?a.reduceRight(c,d):a.reduceRight(c);var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect=
|
||||
function(a,c,b){var e;E(a,function(a,g,h){if(c.call(b,a,g,h))return e=a,true});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(A&&a.filter===A)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(B&&a.every===B)return a.every(c,b);j(a,function(a,g,h){if(!(e=
|
||||
e&&c.call(b,a,g,h)))return n});return e};var E=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(C&&a.some===C)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return n});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;return p&&a.indexOf===p?a.indexOf(c)!=-1:b=E(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck=
|
||||
function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b<e.computed&&(e={value:a,computed:b})});
|
||||
return e.value};b.shuffle=function(a){var b=[],d;j(a,function(a,f){f==0?b[0]=a:(d=Math.floor(Math.random()*(f+1)),b[f]=b[d],b[d]=a)});return b};b.sortBy=function(a,c,d){return b.pluck(b.map(a,function(a,b,g){return{value:a,criteria:c.call(d,a,b,g)}}).sort(function(a,b){var c=a.criteria,d=b.criteria;return c<d?-1:c>d?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a,
|
||||
c,d){d||(d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:a.toArray?a.toArray():b.isArray(a)?i.call(a):b.isArguments(a)?i.call(a):b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=b.head=function(a,b,d){return b!=null&&!d?i.call(a,0,b):a[0]};b.initial=function(a,b,d){return i.call(a,0,a.length-(b==null||d?1:b))};b.last=function(a,b,d){return b!=null&&!d?i.call(a,Math.max(a.length-b,0)):a[a.length-1]};b.rest=
|
||||
b.tail=function(a,b,d){return i.call(a,b==null||d?1:b)};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a,c){return b.reduce(a,function(a,e){if(b.isArray(e))return a.concat(c?e:b.flatten(e));a[a.length]=e;return a},[])};b.without=function(a){return b.difference(a,i.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a,e=[];b.reduce(d,function(d,g,h){if(0==h||(c===true?b.last(d)!=g:!b.include(d,g)))d[d.length]=g,e[e.length]=a[h];return d},[]);
|
||||
return e};b.union=function(){return b.uniq(b.flatten(arguments,true))};b.intersection=b.intersect=function(a){var c=i.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1));return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,c,
|
||||
d){if(a==null)return-1;var e;if(d)return d=b.sortedIndex(a,c),a[d]===c?d:-1;if(p&&a.indexOf===p)return a.indexOf(c);for(d=0,e=a.length;d<e;d++)if(d in a&&a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(D&&a.lastIndexOf===D)return a.lastIndexOf(b);for(var d=a.length;d--;)if(d in a&&a[d]===b)return d;return-1};b.range=function(a,b,d){arguments.length<=1&&(b=a||0,a=0);for(var d=arguments[2]||1,e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;)g[f++]=a,a+=d;return g};
|
||||
var F=function(){};b.bind=function(a,c){var d,e;if(a.bind===s&&s)return s.apply(a,i.call(arguments,1));if(!b.isFunction(a))throw new TypeError;e=i.call(arguments,2);return d=function(){if(!(this instanceof d))return a.apply(c,e.concat(i.call(arguments)));F.prototype=a.prototype;var b=new F,g=a.apply(b,e.concat(i.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c=i.call(arguments,1);c.length==0&&(c=b.functions(a));j(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,
|
||||
c){var d={};c||(c=b.identity);return function(){var e=c.apply(this,arguments);return b.has(d,e)?d[e]:d[e]=a.apply(this,arguments)}};b.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(a,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(i.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,h,i=b.debounce(function(){h=g=false},c);return function(){d=this;e=arguments;var b;f||(f=setTimeout(function(){f=null;h&&a.apply(d,e);i()},c));g?h=true:
|
||||
a.apply(d,e);i();g=true}};b.debounce=function(a,b){var d;return function(){var e=this,f=arguments;clearTimeout(d);d=setTimeout(function(){d=null;a.apply(e,f)},b)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=[a].concat(i.call(arguments,0));return b.apply(this,d)}};b.compose=function(){var a=arguments;return function(){for(var b=arguments,d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};
|
||||
b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=J||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.defaults=function(a){j(i.call(arguments,
|
||||
1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return q(a,b,[])};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=o||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)};
|
||||
b.isArguments=function(a){return l.call(a)=="[object Arguments]"};if(!b.isArguments(arguments))b.isArguments=function(a){return!(!a||!b.has(a,"callee"))};b.isFunction=function(a){return l.call(a)=="[object Function]"};b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"};
|
||||
b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,b){return I.call(a,b)};b.noConflict=function(){r._=G;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.escape=function(a){return(""+a).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};b.mixin=function(a){j(b.functions(a),
|
||||
function(c){K(c,b[c]=a[c])})};var L=0;b.uniqueId=function(a){var b=L++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var t=/.^/,u=function(a){return a.replace(/\\\\/g,"\\").replace(/\\'/g,"'")};b.template=function(a,c){var d=b.templateSettings,d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.escape||t,function(a,b){return"',_.escape("+
|
||||
u(b)+"),'"}).replace(d.interpolate||t,function(a,b){return"',"+u(b)+",'"}).replace(d.evaluate||t,function(a,b){return"');"+u(b).replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",e=new Function("obj","_",d);return c?e(c,b):function(a){return e.call(this,a,b)}};b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var v=function(a,c){return c?b(a).chain():a},K=function(a,c){m.prototype[a]=
|
||||
function(){var a=i.call(arguments);H.call(a,this._wrapped);return v(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return v(d,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return v(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain=
|
||||
true;return this};m.prototype.value=function(){return this._wrapped}}).call(this);
|
BIN
docs/_build/html/_static/up-pressed.png
vendored
Before Width: | Height: | Size: 214 B |
BIN
docs/_build/html/_static/up.png
vendored
Before Width: | Height: | Size: 203 B |
808
docs/_build/html/_static/websupport.js
vendored
@@ -1,808 +0,0 @@
|
||||
/*
|
||||
* websupport.js
|
||||
* ~~~~~~~~~~~~~
|
||||
*
|
||||
* sphinx.websupport utilities for all documentation.
|
||||
*
|
||||
* :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
$.fn.autogrow = function() {
|
||||
return this.each(function() {
|
||||
var textarea = this;
|
||||
|
||||
$.fn.autogrow.resize(textarea);
|
||||
|
||||
$(textarea)
|
||||
.focus(function() {
|
||||
textarea.interval = setInterval(function() {
|
||||
$.fn.autogrow.resize(textarea);
|
||||
}, 500);
|
||||
})
|
||||
.blur(function() {
|
||||
clearInterval(textarea.interval);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$.fn.autogrow.resize = function(textarea) {
|
||||
var lineHeight = parseInt($(textarea).css('line-height'), 10);
|
||||
var lines = textarea.value.split('\n');
|
||||
var columns = textarea.cols;
|
||||
var lineCount = 0;
|
||||
$.each(lines, function() {
|
||||
lineCount += Math.ceil(this.length / columns) || 1;
|
||||
});
|
||||
var height = lineHeight * (lineCount + 1);
|
||||
$(textarea).css('height', height);
|
||||
};
|
||||
})(jQuery);
|
||||
|
||||
(function($) {
|
||||
var comp, by;
|
||||
|
||||
function init() {
|
||||
initEvents();
|
||||
initComparator();
|
||||
}
|
||||
|
||||
function initEvents() {
|
||||
$(document).on("click", 'a.comment-close', function(event) {
|
||||
event.preventDefault();
|
||||
hide($(this).attr('id').substring(2));
|
||||
});
|
||||
$(document).on("click", 'a.vote', function(event) {
|
||||
event.preventDefault();
|
||||
handleVote($(this));
|
||||
});
|
||||
$(document).on("click", 'a.reply', function(event) {
|
||||
event.preventDefault();
|
||||
openReply($(this).attr('id').substring(2));
|
||||
});
|
||||
$(document).on("click", 'a.close-reply', function(event) {
|
||||
event.preventDefault();
|
||||
closeReply($(this).attr('id').substring(2));
|
||||
});
|
||||
$(document).on("click", 'a.sort-option', function(event) {
|
||||
event.preventDefault();
|
||||
handleReSort($(this));
|
||||
});
|
||||
$(document).on("click", 'a.show-proposal', function(event) {
|
||||
event.preventDefault();
|
||||
showProposal($(this).attr('id').substring(2));
|
||||
});
|
||||
$(document).on("click", 'a.hide-proposal', function(event) {
|
||||
event.preventDefault();
|
||||
hideProposal($(this).attr('id').substring(2));
|
||||
});
|
||||
$(document).on("click", 'a.show-propose-change', function(event) {
|
||||
event.preventDefault();
|
||||
showProposeChange($(this).attr('id').substring(2));
|
||||
});
|
||||
$(document).on("click", 'a.hide-propose-change', function(event) {
|
||||
event.preventDefault();
|
||||
hideProposeChange($(this).attr('id').substring(2));
|
||||
});
|
||||
$(document).on("click", 'a.accept-comment', function(event) {
|
||||
event.preventDefault();
|
||||
acceptComment($(this).attr('id').substring(2));
|
||||
});
|
||||
$(document).on("click", 'a.delete-comment', function(event) {
|
||||
event.preventDefault();
|
||||
deleteComment($(this).attr('id').substring(2));
|
||||
});
|
||||
$(document).on("click", 'a.comment-markup', function(event) {
|
||||
event.preventDefault();
|
||||
toggleCommentMarkupBox($(this).attr('id').substring(2));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set comp, which is a comparator function used for sorting and
|
||||
* inserting comments into the list.
|
||||
*/
|
||||
function setComparator() {
|
||||
// If the first three letters are "asc", sort in ascending order
|
||||
// and remove the prefix.
|
||||
if (by.substring(0,3) == 'asc') {
|
||||
var i = by.substring(3);
|
||||
comp = function(a, b) { return a[i] - b[i]; };
|
||||
} else {
|
||||
// Otherwise sort in descending order.
|
||||
comp = function(a, b) { return b[by] - a[by]; };
|
||||
}
|
||||
|
||||
// Reset link styles and format the selected sort option.
|
||||
$('a.sel').attr('href', '#').removeClass('sel');
|
||||
$('a.by' + by).removeAttr('href').addClass('sel');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a comp function. If the user has preferences stored in
|
||||
* the sortBy cookie, use those, otherwise use the default.
|
||||
*/
|
||||
function initComparator() {
|
||||
by = 'rating'; // Default to sort by rating.
|
||||
// If the sortBy cookie is set, use that instead.
|
||||
if (document.cookie.length > 0) {
|
||||
var start = document.cookie.indexOf('sortBy=');
|
||||
if (start != -1) {
|
||||
start = start + 7;
|
||||
var end = document.cookie.indexOf(";", start);
|
||||
if (end == -1) {
|
||||
end = document.cookie.length;
|
||||
by = unescape(document.cookie.substring(start, end));
|
||||
}
|
||||
}
|
||||
}
|
||||
setComparator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a comment div.
|
||||
*/
|
||||
function show(id) {
|
||||
$('#ao' + id).hide();
|
||||
$('#ah' + id).show();
|
||||
var context = $.extend({id: id}, opts);
|
||||
var popup = $(renderTemplate(popupTemplate, context)).hide();
|
||||
popup.find('textarea[name="proposal"]').hide();
|
||||
popup.find('a.by' + by).addClass('sel');
|
||||
var form = popup.find('#cf' + id);
|
||||
form.submit(function(event) {
|
||||
event.preventDefault();
|
||||
addComment(form);
|
||||
});
|
||||
$('#s' + id).after(popup);
|
||||
popup.slideDown('fast', function() {
|
||||
getComments(id);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide a comment div.
|
||||
*/
|
||||
function hide(id) {
|
||||
$('#ah' + id).hide();
|
||||
$('#ao' + id).show();
|
||||
var div = $('#sc' + id);
|
||||
div.slideUp('fast', function() {
|
||||
div.remove();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an ajax request to get comments for a node
|
||||
* and insert the comments into the comments tree.
|
||||
*/
|
||||
function getComments(id) {
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: opts.getCommentsURL,
|
||||
data: {node: id},
|
||||
success: function(data, textStatus, request) {
|
||||
var ul = $('#cl' + id);
|
||||
var speed = 100;
|
||||
$('#cf' + id)
|
||||
.find('textarea[name="proposal"]')
|
||||
.data('source', data.source);
|
||||
|
||||
if (data.comments.length === 0) {
|
||||
ul.html('<li>No comments yet.</li>');
|
||||
ul.data('empty', true);
|
||||
} else {
|
||||
// If there are comments, sort them and put them in the list.
|
||||
var comments = sortComments(data.comments);
|
||||
speed = data.comments.length * 100;
|
||||
appendComments(comments, ul);
|
||||
ul.data('empty', false);
|
||||
}
|
||||
$('#cn' + id).slideUp(speed + 200);
|
||||
ul.slideDown(speed);
|
||||
},
|
||||
error: function(request, textStatus, error) {
|
||||
showError('Oops, there was a problem retrieving the comments.');
|
||||
},
|
||||
dataType: 'json'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a comment via ajax and insert the comment into the comment tree.
|
||||
*/
|
||||
function addComment(form) {
|
||||
var node_id = form.find('input[name="node"]').val();
|
||||
var parent_id = form.find('input[name="parent"]').val();
|
||||
var text = form.find('textarea[name="comment"]').val();
|
||||
var proposal = form.find('textarea[name="proposal"]').val();
|
||||
|
||||
if (text == '') {
|
||||
showError('Please enter a comment.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable the form that is being submitted.
|
||||
form.find('textarea,input').attr('disabled', 'disabled');
|
||||
|
||||
// Send the comment to the server.
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: opts.addCommentURL,
|
||||
dataType: 'json',
|
||||
data: {
|
||||
node: node_id,
|
||||
parent: parent_id,
|
||||
text: text,
|
||||
proposal: proposal
|
||||
},
|
||||
success: function(data, textStatus, error) {
|
||||
// Reset the form.
|
||||
if (node_id) {
|
||||
hideProposeChange(node_id);
|
||||
}
|
||||
form.find('textarea')
|
||||
.val('')
|
||||
.add(form.find('input'))
|
||||
.removeAttr('disabled');
|
||||
var ul = $('#cl' + (node_id || parent_id));
|
||||
if (ul.data('empty')) {
|
||||
$(ul).empty();
|
||||
ul.data('empty', false);
|
||||
}
|
||||
insertComment(data.comment);
|
||||
var ao = $('#ao' + node_id);
|
||||
ao.find('img').attr({'src': opts.commentBrightImage});
|
||||
if (node_id) {
|
||||
// if this was a "root" comment, remove the commenting box
|
||||
// (the user can get it back by reopening the comment popup)
|
||||
$('#ca' + node_id).slideUp();
|
||||
}
|
||||
},
|
||||
error: function(request, textStatus, error) {
|
||||
form.find('textarea,input').removeAttr('disabled');
|
||||
showError('Oops, there was a problem adding the comment.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively append comments to the main comment list and children
|
||||
* lists, creating the comment tree.
|
||||
*/
|
||||
function appendComments(comments, ul) {
|
||||
$.each(comments, function() {
|
||||
var div = createCommentDiv(this);
|
||||
ul.append($(document.createElement('li')).html(div));
|
||||
appendComments(this.children, div.find('ul.comment-children'));
|
||||
// To avoid stagnating data, don't store the comments children in data.
|
||||
this.children = null;
|
||||
div.data('comment', this);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* After adding a new comment, it must be inserted in the correct
|
||||
* location in the comment tree.
|
||||
*/
|
||||
function insertComment(comment) {
|
||||
var div = createCommentDiv(comment);
|
||||
|
||||
// To avoid stagnating data, don't store the comments children in data.
|
||||
comment.children = null;
|
||||
div.data('comment', comment);
|
||||
|
||||
var ul = $('#cl' + (comment.node || comment.parent));
|
||||
var siblings = getChildren(ul);
|
||||
|
||||
var li = $(document.createElement('li'));
|
||||
li.hide();
|
||||
|
||||
// Determine where in the parents children list to insert this comment.
|
||||
for(i=0; i < siblings.length; i++) {
|
||||
if (comp(comment, siblings[i]) <= 0) {
|
||||
$('#cd' + siblings[i].id)
|
||||
.parent()
|
||||
.before(li.html(div));
|
||||
li.slideDown('fast');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here, this comment rates lower than all the others,
|
||||
// or it is the only comment in the list.
|
||||
ul.append(li.html(div));
|
||||
li.slideDown('fast');
|
||||
}
|
||||
|
||||
function acceptComment(id) {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: opts.acceptCommentURL,
|
||||
data: {id: id},
|
||||
success: function(data, textStatus, request) {
|
||||
$('#cm' + id).fadeOut('fast');
|
||||
$('#cd' + id).removeClass('moderate');
|
||||
},
|
||||
error: function(request, textStatus, error) {
|
||||
showError('Oops, there was a problem accepting the comment.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deleteComment(id) {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: opts.deleteCommentURL,
|
||||
data: {id: id},
|
||||
success: function(data, textStatus, request) {
|
||||
var div = $('#cd' + id);
|
||||
if (data == 'delete') {
|
||||
// Moderator mode: remove the comment and all children immediately
|
||||
div.slideUp('fast', function() {
|
||||
div.remove();
|
||||
});
|
||||
return;
|
||||
}
|
||||
// User mode: only mark the comment as deleted
|
||||
div
|
||||
.find('span.user-id:first')
|
||||
.text('[deleted]').end()
|
||||
.find('div.comment-text:first')
|
||||
.text('[deleted]').end()
|
||||
.find('#cm' + id + ', #dc' + id + ', #ac' + id + ', #rc' + id +
|
||||
', #sp' + id + ', #hp' + id + ', #cr' + id + ', #rl' + id)
|
||||
.remove();
|
||||
var comment = div.data('comment');
|
||||
comment.username = '[deleted]';
|
||||
comment.text = '[deleted]';
|
||||
div.data('comment', comment);
|
||||
},
|
||||
error: function(request, textStatus, error) {
|
||||
showError('Oops, there was a problem deleting the comment.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showProposal(id) {
|
||||
$('#sp' + id).hide();
|
||||
$('#hp' + id).show();
|
||||
$('#pr' + id).slideDown('fast');
|
||||
}
|
||||
|
||||
function hideProposal(id) {
|
||||
$('#hp' + id).hide();
|
||||
$('#sp' + id).show();
|
||||
$('#pr' + id).slideUp('fast');
|
||||
}
|
||||
|
||||
function showProposeChange(id) {
|
||||
$('#pc' + id).hide();
|
||||
$('#hc' + id).show();
|
||||
var textarea = $('#pt' + id);
|
||||
textarea.val(textarea.data('source'));
|
||||
$.fn.autogrow.resize(textarea[0]);
|
||||
textarea.slideDown('fast');
|
||||
}
|
||||
|
||||
function hideProposeChange(id) {
|
||||
$('#hc' + id).hide();
|
||||
$('#pc' + id).show();
|
||||
var textarea = $('#pt' + id);
|
||||
textarea.val('').removeAttr('disabled');
|
||||
textarea.slideUp('fast');
|
||||
}
|
||||
|
||||
function toggleCommentMarkupBox(id) {
|
||||
$('#mb' + id).toggle();
|
||||
}
|
||||
|
||||
/** Handle when the user clicks on a sort by link. */
|
||||
function handleReSort(link) {
|
||||
var classes = link.attr('class').split(/\s+/);
|
||||
for (var i=0; i<classes.length; i++) {
|
||||
if (classes[i] != 'sort-option') {
|
||||
by = classes[i].substring(2);
|
||||
}
|
||||
}
|
||||
setComparator();
|
||||
// Save/update the sortBy cookie.
|
||||
var expiration = new Date();
|
||||
expiration.setDate(expiration.getDate() + 365);
|
||||
document.cookie= 'sortBy=' + escape(by) +
|
||||
';expires=' + expiration.toUTCString();
|
||||
$('ul.comment-ul').each(function(index, ul) {
|
||||
var comments = getChildren($(ul), true);
|
||||
comments = sortComments(comments);
|
||||
appendComments(comments, $(ul).empty());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to process a vote when a user clicks an arrow.
|
||||
*/
|
||||
function handleVote(link) {
|
||||
if (!opts.voting) {
|
||||
showError("You'll need to login to vote.");
|
||||
return;
|
||||
}
|
||||
|
||||
var id = link.attr('id');
|
||||
if (!id) {
|
||||
// Didn't click on one of the voting arrows.
|
||||
return;
|
||||
}
|
||||
// If it is an unvote, the new vote value is 0,
|
||||
// Otherwise it's 1 for an upvote, or -1 for a downvote.
|
||||
var value = 0;
|
||||
if (id.charAt(1) != 'u') {
|
||||
value = id.charAt(0) == 'u' ? 1 : -1;
|
||||
}
|
||||
// The data to be sent to the server.
|
||||
var d = {
|
||||
comment_id: id.substring(2),
|
||||
value: value
|
||||
};
|
||||
|
||||
// Swap the vote and unvote links.
|
||||
link.hide();
|
||||
$('#' + id.charAt(0) + (id.charAt(1) == 'u' ? 'v' : 'u') + d.comment_id)
|
||||
.show();
|
||||
|
||||
// The div the comment is displayed in.
|
||||
var div = $('div#cd' + d.comment_id);
|
||||
var data = div.data('comment');
|
||||
|
||||
// If this is not an unvote, and the other vote arrow has
|
||||
// already been pressed, unpress it.
|
||||
if ((d.value !== 0) && (data.vote === d.value * -1)) {
|
||||
$('#' + (d.value == 1 ? 'd' : 'u') + 'u' + d.comment_id).hide();
|
||||
$('#' + (d.value == 1 ? 'd' : 'u') + 'v' + d.comment_id).show();
|
||||
}
|
||||
|
||||
// Update the comments rating in the local data.
|
||||
data.rating += (data.vote === 0) ? d.value : (d.value - data.vote);
|
||||
data.vote = d.value;
|
||||
div.data('comment', data);
|
||||
|
||||
// Change the rating text.
|
||||
div.find('.rating:first')
|
||||
.text(data.rating + ' point' + (data.rating == 1 ? '' : 's'));
|
||||
|
||||
// Send the vote information to the server.
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: opts.processVoteURL,
|
||||
data: d,
|
||||
error: function(request, textStatus, error) {
|
||||
showError('Oops, there was a problem casting that vote.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a reply form used to reply to an existing comment.
|
||||
*/
|
||||
function openReply(id) {
|
||||
// Swap out the reply link for the hide link
|
||||
$('#rl' + id).hide();
|
||||
$('#cr' + id).show();
|
||||
|
||||
// Add the reply li to the children ul.
|
||||
var div = $(renderTemplate(replyTemplate, {id: id})).hide();
|
||||
$('#cl' + id)
|
||||
.prepend(div)
|
||||
// Setup the submit handler for the reply form.
|
||||
.find('#rf' + id)
|
||||
.submit(function(event) {
|
||||
event.preventDefault();
|
||||
addComment($('#rf' + id));
|
||||
closeReply(id);
|
||||
})
|
||||
.find('input[type=button]')
|
||||
.click(function() {
|
||||
closeReply(id);
|
||||
});
|
||||
div.slideDown('fast', function() {
|
||||
$('#rf' + id).find('textarea').focus();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the reply form opened with openReply.
|
||||
*/
|
||||
function closeReply(id) {
|
||||
// Remove the reply div from the DOM.
|
||||
$('#rd' + id).slideUp('fast', function() {
|
||||
$(this).remove();
|
||||
});
|
||||
|
||||
// Swap out the hide link for the reply link
|
||||
$('#cr' + id).hide();
|
||||
$('#rl' + id).show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively sort a tree of comments using the comp comparator.
|
||||
*/
|
||||
function sortComments(comments) {
|
||||
comments.sort(comp);
|
||||
$.each(comments, function() {
|
||||
this.children = sortComments(this.children);
|
||||
});
|
||||
return comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the children comments from a ul. If recursive is true,
|
||||
* recursively include childrens' children.
|
||||
*/
|
||||
function getChildren(ul, recursive) {
|
||||
var children = [];
|
||||
ul.children().children("[id^='cd']")
|
||||
.each(function() {
|
||||
var comment = $(this).data('comment');
|
||||
if (recursive)
|
||||
comment.children = getChildren($(this).find('#cl' + comment.id), true);
|
||||
children.push(comment);
|
||||
});
|
||||
return children;
|
||||
}
|
||||
|
||||
/** Create a div to display a comment in. */
|
||||
function createCommentDiv(comment) {
|
||||
if (!comment.displayed && !opts.moderator) {
|
||||
return $('<div class="moderate">Thank you! Your comment will show up '
|
||||
+ 'once it is has been approved by a moderator.</div>');
|
||||
}
|
||||
// Prettify the comment rating.
|
||||
comment.pretty_rating = comment.rating + ' point' +
|
||||
(comment.rating == 1 ? '' : 's');
|
||||
// Make a class (for displaying not yet moderated comments differently)
|
||||
comment.css_class = comment.displayed ? '' : ' moderate';
|
||||
// Create a div for this comment.
|
||||
var context = $.extend({}, opts, comment);
|
||||
var div = $(renderTemplate(commentTemplate, context));
|
||||
|
||||
// If the user has voted on this comment, highlight the correct arrow.
|
||||
if (comment.vote) {
|
||||
var direction = (comment.vote == 1) ? 'u' : 'd';
|
||||
div.find('#' + direction + 'v' + comment.id).hide();
|
||||
div.find('#' + direction + 'u' + comment.id).show();
|
||||
}
|
||||
|
||||
if (opts.moderator || comment.text != '[deleted]') {
|
||||
div.find('a.reply').show();
|
||||
if (comment.proposal_diff)
|
||||
div.find('#sp' + comment.id).show();
|
||||
if (opts.moderator && !comment.displayed)
|
||||
div.find('#cm' + comment.id).show();
|
||||
if (opts.moderator || (opts.username == comment.username))
|
||||
div.find('#dc' + comment.id).show();
|
||||
}
|
||||
return div;
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple template renderer. Placeholders such as <%id%> are replaced
|
||||
* by context['id'] with items being escaped. Placeholders such as <#id#>
|
||||
* are not escaped.
|
||||
*/
|
||||
function renderTemplate(template, context) {
|
||||
var esc = $(document.createElement('div'));
|
||||
|
||||
function handle(ph, escape) {
|
||||
var cur = context;
|
||||
$.each(ph.split('.'), function() {
|
||||
cur = cur[this];
|
||||
});
|
||||
return escape ? esc.text(cur || "").html() : cur;
|
||||
}
|
||||
|
||||
return template.replace(/<([%#])([\w\.]*)\1>/g, function() {
|
||||
return handle(arguments[2], arguments[1] == '%' ? true : false);
|
||||
});
|
||||
}
|
||||
|
||||
/** Flash an error message briefly. */
|
||||
function showError(message) {
|
||||
$(document.createElement('div')).attr({'class': 'popup-error'})
|
||||
.append($(document.createElement('div'))
|
||||
.attr({'class': 'error-message'}).text(message))
|
||||
.appendTo('body')
|
||||
.fadeIn("slow")
|
||||
.delay(2000)
|
||||
.fadeOut("slow");
|
||||
}
|
||||
|
||||
/** Add a link the user uses to open the comments popup. */
|
||||
$.fn.comment = function() {
|
||||
return this.each(function() {
|
||||
var id = $(this).attr('id').substring(1);
|
||||
var count = COMMENT_METADATA[id];
|
||||
var title = count + ' comment' + (count == 1 ? '' : 's');
|
||||
var image = count > 0 ? opts.commentBrightImage : opts.commentImage;
|
||||
var addcls = count == 0 ? ' nocomment' : '';
|
||||
$(this)
|
||||
.append(
|
||||
$(document.createElement('a')).attr({
|
||||
href: '#',
|
||||
'class': 'sphinx-comment-open' + addcls,
|
||||
id: 'ao' + id
|
||||
})
|
||||
.append($(document.createElement('img')).attr({
|
||||
src: image,
|
||||
alt: 'comment',
|
||||
title: title
|
||||
}))
|
||||
.click(function(event) {
|
||||
event.preventDefault();
|
||||
show($(this).attr('id').substring(2));
|
||||
})
|
||||
)
|
||||
.append(
|
||||
$(document.createElement('a')).attr({
|
||||
href: '#',
|
||||
'class': 'sphinx-comment-close hidden',
|
||||
id: 'ah' + id
|
||||
})
|
||||
.append($(document.createElement('img')).attr({
|
||||
src: opts.closeCommentImage,
|
||||
alt: 'close',
|
||||
title: 'close'
|
||||
}))
|
||||
.click(function(event) {
|
||||
event.preventDefault();
|
||||
hide($(this).attr('id').substring(2));
|
||||
})
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
var opts = {
|
||||
processVoteURL: '/_process_vote',
|
||||
addCommentURL: '/_add_comment',
|
||||
getCommentsURL: '/_get_comments',
|
||||
acceptCommentURL: '/_accept_comment',
|
||||
deleteCommentURL: '/_delete_comment',
|
||||
commentImage: '/static/_static/comment.png',
|
||||
closeCommentImage: '/static/_static/comment-close.png',
|
||||
loadingImage: '/static/_static/ajax-loader.gif',
|
||||
commentBrightImage: '/static/_static/comment-bright.png',
|
||||
upArrow: '/static/_static/up.png',
|
||||
downArrow: '/static/_static/down.png',
|
||||
upArrowPressed: '/static/_static/up-pressed.png',
|
||||
downArrowPressed: '/static/_static/down-pressed.png',
|
||||
voting: false,
|
||||
moderator: false
|
||||
};
|
||||
|
||||
if (typeof COMMENT_OPTIONS != "undefined") {
|
||||
opts = jQuery.extend(opts, COMMENT_OPTIONS);
|
||||
}
|
||||
|
||||
var popupTemplate = '\
|
||||
<div class="sphinx-comments" id="sc<%id%>">\
|
||||
<p class="sort-options">\
|
||||
Sort by:\
|
||||
<a href="#" class="sort-option byrating">best rated</a>\
|
||||
<a href="#" class="sort-option byascage">newest</a>\
|
||||
<a href="#" class="sort-option byage">oldest</a>\
|
||||
</p>\
|
||||
<div class="comment-header">Comments</div>\
|
||||
<div class="comment-loading" id="cn<%id%>">\
|
||||
loading comments... <img src="<%loadingImage%>" alt="" /></div>\
|
||||
<ul id="cl<%id%>" class="comment-ul"></ul>\
|
||||
<div id="ca<%id%>">\
|
||||
<p class="add-a-comment">Add a comment\
|
||||
(<a href="#" class="comment-markup" id="ab<%id%>">markup</a>):</p>\
|
||||
<div class="comment-markup-box" id="mb<%id%>">\
|
||||
reStructured text markup: <i>*emph*</i>, <b>**strong**</b>, \
|
||||
<code>``code``</code>, \
|
||||
code blocks: <code>::</code> and an indented block after blank line</div>\
|
||||
<form method="post" id="cf<%id%>" class="comment-form" action="">\
|
||||
<textarea name="comment" cols="80"></textarea>\
|
||||
<p class="propose-button">\
|
||||
<a href="#" id="pc<%id%>" class="show-propose-change">\
|
||||
Propose a change ▹\
|
||||
</a>\
|
||||
<a href="#" id="hc<%id%>" class="hide-propose-change">\
|
||||
Propose a change ▿\
|
||||
</a>\
|
||||
</p>\
|
||||
<textarea name="proposal" id="pt<%id%>" cols="80"\
|
||||
spellcheck="false"></textarea>\
|
||||
<input type="submit" value="Add comment" />\
|
||||
<input type="hidden" name="node" value="<%id%>" />\
|
||||
<input type="hidden" name="parent" value="" />\
|
||||
</form>\
|
||||
</div>\
|
||||
</div>';
|
||||
|
||||
var commentTemplate = '\
|
||||
<div id="cd<%id%>" class="sphinx-comment<%css_class%>">\
|
||||
<div class="vote">\
|
||||
<div class="arrow">\
|
||||
<a href="#" id="uv<%id%>" class="vote" title="vote up">\
|
||||
<img src="<%upArrow%>" />\
|
||||
</a>\
|
||||
<a href="#" id="uu<%id%>" class="un vote" title="vote up">\
|
||||
<img src="<%upArrowPressed%>" />\
|
||||
</a>\
|
||||
</div>\
|
||||
<div class="arrow">\
|
||||
<a href="#" id="dv<%id%>" class="vote" title="vote down">\
|
||||
<img src="<%downArrow%>" id="da<%id%>" />\
|
||||
</a>\
|
||||
<a href="#" id="du<%id%>" class="un vote" title="vote down">\
|
||||
<img src="<%downArrowPressed%>" />\
|
||||
</a>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class="comment-content">\
|
||||
<p class="tagline comment">\
|
||||
<span class="user-id"><%username%></span>\
|
||||
<span class="rating"><%pretty_rating%></span>\
|
||||
<span class="delta"><%time.delta%></span>\
|
||||
</p>\
|
||||
<div class="comment-text comment"><#text#></div>\
|
||||
<p class="comment-opts comment">\
|
||||
<a href="#" class="reply hidden" id="rl<%id%>">reply ▹</a>\
|
||||
<a href="#" class="close-reply" id="cr<%id%>">reply ▿</a>\
|
||||
<a href="#" id="sp<%id%>" class="show-proposal">proposal ▹</a>\
|
||||
<a href="#" id="hp<%id%>" class="hide-proposal">proposal ▿</a>\
|
||||
<a href="#" id="dc<%id%>" class="delete-comment hidden">delete</a>\
|
||||
<span id="cm<%id%>" class="moderation hidden">\
|
||||
<a href="#" id="ac<%id%>" class="accept-comment">accept</a>\
|
||||
</span>\
|
||||
</p>\
|
||||
<pre class="proposal" id="pr<%id%>">\
|
||||
<#proposal_diff#>\
|
||||
</pre>\
|
||||
<ul class="comment-children" id="cl<%id%>"></ul>\
|
||||
</div>\
|
||||
<div class="clearleft"></div>\
|
||||
</div>\
|
||||
</div>';
|
||||
|
||||
var replyTemplate = '\
|
||||
<li>\
|
||||
<div class="reply-div" id="rd<%id%>">\
|
||||
<form id="rf<%id%>">\
|
||||
<textarea name="comment" cols="80"></textarea>\
|
||||
<input type="submit" value="Add reply" />\
|
||||
<input type="button" value="Cancel" />\
|
||||
<input type="hidden" name="parent" value="<%id%>" />\
|
||||
<input type="hidden" name="node" value="" />\
|
||||
</form>\
|
||||
</div>\
|
||||
</li>';
|
||||
|
||||
$(document).ready(function() {
|
||||
init();
|
||||
});
|
||||
})(jQuery);
|
||||
|
||||
$(document).ready(function() {
|
||||
// add comment anchors for all paragraphs that are commentable
|
||||
$('.sphinx-has-comment').comment();
|
||||
|
||||
// highlight search words in search results
|
||||
$("div.context").each(function() {
|
||||
var params = $.getQueryParameters();
|
||||
var terms = (params.q) ? params.q[0].split(/\s+/) : [];
|
||||
var result = $(this);
|
||||
$.each(terms, function() {
|
||||
result.highlightText(this.toLowerCase(), 'highlighted');
|
||||
});
|
||||
});
|
||||
|
||||
// directly open comment window if requested
|
||||
var anchor = document.location.hash;
|
||||
if (anchor.substring(0, 9) == '#comment-') {
|
||||
$('#ao' + anchor.substring(9)).click();
|
||||
document.location.hash = '#s' + anchor.substring(9);
|
||||
}
|
||||
});
|
96
docs/_build/html/demo.html
vendored
@@ -1,96 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title><no title> — Soil 0.1 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: './',
|
||||
VERSION: '0.1',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true,
|
||||
SOURCELINK_SUFFIX: '.txt'
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<link rel="prev" title="Models" href="models.html" />
|
||||
|
||||
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
|
||||
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
|
||||
|
||||
</head>
|
||||
<body role="document">
|
||||
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper"><div class="relations">
|
||||
<h3>Related Topics</h3>
|
||||
<ul>
|
||||
<li><a href="index.html">Documentation overview</a><ul>
|
||||
<li>Previous: <a href="models.html" title="previous chapter">Models</a></li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div role="note" aria-label="source link">
|
||||
<h3>This Page</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="_sources/demo.rst.txt"
|
||||
rel="nofollow">Show Source</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3>Quick search</h3>
|
||||
<form class="search" action="search.html" method="get">
|
||||
<div><input type="text" name="q" /></div>
|
||||
<div><input type="submit" value="Go" /></div>
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
©2017, GSI.
|
||||
|
||||
|
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 1.5.1</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.9</a>
|
||||
|
||||
|
|
||||
<a href="_sources/demo.rst.txt"
|
||||
rel="nofollow">Page source</a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
94
docs/_build/html/genindex.html
vendored
@@ -1,94 +0,0 @@
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>Index — Soil 0.1 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: './',
|
||||
VERSION: '0.1',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true,
|
||||
SOURCELINK_SUFFIX: '.txt'
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<link rel="index" title="Index" href="#" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
|
||||
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
|
||||
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
|
||||
|
||||
</head>
|
||||
<body role="document">
|
||||
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
|
||||
<h1 id="index">Index</h1>
|
||||
|
||||
<div class="genindex-jumpbox">
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
|
||||
<div class="relations">
|
||||
<h3>Related Topics</h3>
|
||||
<ul>
|
||||
<li><a href="index.html">Documentation overview</a><ul>
|
||||
</ul></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3>Quick search</h3>
|
||||
<form class="search" action="search.html" method="get">
|
||||
<div><input type="text" name="q" /></div>
|
||||
<div><input type="submit" value="Go" /></div>
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
©2017, GSI.
|
||||
|
||||
|
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 1.5.1</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.9</a>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
120
docs/_build/html/index.html
vendored
@@ -1,120 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>Welcome to Soil’s documentation! — Soil 0.1 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: './',
|
||||
VERSION: '0.1',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true,
|
||||
SOURCELINK_SUFFIX: '.txt'
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<link rel="next" title="Installation" href="installation.html" />
|
||||
|
||||
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
|
||||
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
|
||||
|
||||
</head>
|
||||
<body role="document">
|
||||
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<div class="section" id="welcome-to-soil-s-documentation">
|
||||
<h1>Welcome to Soil’s documentation!<a class="headerlink" href="#welcome-to-soil-s-documentation" title="Permalink to this headline">¶</a></h1>
|
||||
<p>Soil is an Agent-based Social Simulator in Python for modelling and simulation of Social Networks.</p>
|
||||
<div class="toctree-wrapper compound">
|
||||
<p class="caption"><span class="caption-text">Learn more about soil:</span></p>
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="usage.html">Usage</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="usage.html#simulation-settings">Simulation Settings</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="usage.html#network-types">Network Types</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="usage.html#models-settings">Models Settings</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="usage.html#running-the-simulation">Running the Simulation</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="models.html">Developing new models</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="models.html#what-is-a-model">What is a model?</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="models.html#models-code">Models Code</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="models.html#variable-initialization">Variable Initialization</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="models.html#example-model">Example Model</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper"><div class="relations">
|
||||
<h3>Related Topics</h3>
|
||||
<ul>
|
||||
<li><a href="#">Documentation overview</a><ul>
|
||||
<li>Next: <a href="installation.html" title="next chapter">Installation</a></li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div role="note" aria-label="source link">
|
||||
<h3>This Page</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="_sources/index.rst.txt"
|
||||
rel="nofollow">Show Source</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3>Quick search</h3>
|
||||
<form class="search" action="search.html" method="get">
|
||||
<div><input type="text" name="q" /></div>
|
||||
<div><input type="submit" value="Go" /></div>
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
©2017, GSI.
|
||||
|
||||
|
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 1.5.1</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.9</a>
|
||||
|
||||
|
|
||||
<a href="_sources/index.rst.txt"
|
||||
rel="nofollow">Page source</a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
103
docs/_build/html/installation.html
vendored
@@ -1,103 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>Installation — Soil 0.1 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: './',
|
||||
VERSION: '0.1',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true,
|
||||
SOURCELINK_SUFFIX: '.txt'
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<link rel="prev" title="Welcome to Soil’s documentation!" href="index.html" />
|
||||
|
||||
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
|
||||
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
|
||||
|
||||
</head>
|
||||
<body role="document">
|
||||
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<div class="section" id="installation">
|
||||
<h1>Installation<a class="headerlink" href="#installation" title="Permalink to this headline">¶</a></h1>
|
||||
<p>The latest version can be installed through GitLab.</p>
|
||||
<div class="code bash highlight-default"><div class="highlight"><pre><span></span><span class="n">git</span> <span class="n">clone</span> <span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">lab</span><span class="o">.</span><span class="n">cluster</span><span class="o">.</span><span class="n">gsi</span><span class="o">.</span><span class="n">dit</span><span class="o">.</span><span class="n">upm</span><span class="o">.</span><span class="n">es</span><span class="o">/</span><span class="n">soil</span><span class="o">/</span><span class="n">soil</span><span class="o">.</span><span class="n">git</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper"><div class="relations">
|
||||
<h3>Related Topics</h3>
|
||||
<ul>
|
||||
<li><a href="index.html">Documentation overview</a><ul>
|
||||
<li>Previous: <a href="index.html" title="previous chapter">Welcome to Soil’s documentation!</a></li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div role="note" aria-label="source link">
|
||||
<h3>This Page</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="_sources/installation.rst.txt"
|
||||
rel="nofollow">Show Source</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3>Quick search</h3>
|
||||
<form class="search" action="search.html" method="get">
|
||||
<div><input type="text" name="q" /></div>
|
||||
<div><input type="submit" value="Go" /></div>
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
©2017, GSI.
|
||||
|
||||
|
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 1.5.1</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.9</a>
|
||||
|
||||
|
|
||||
<a href="_sources/installation.rst.txt"
|
||||
rel="nofollow">Page source</a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
210
docs/_build/html/models.html
vendored
@@ -1,210 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>Developing new models — Soil 0.1 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: './',
|
||||
VERSION: '0.1',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true,
|
||||
SOURCELINK_SUFFIX: '.txt'
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<link rel="prev" title="Usage" href="usage.html" />
|
||||
|
||||
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
|
||||
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
|
||||
|
||||
</head>
|
||||
<body role="document">
|
||||
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<div class="section" id="developing-new-models">
|
||||
<h1>Developing new models<a class="headerlink" href="#developing-new-models" title="Permalink to this headline">¶</a></h1>
|
||||
<p>This document describes how to develop a new analysis model.</p>
|
||||
<div class="section" id="what-is-a-model">
|
||||
<h2>What is a model?<a class="headerlink" href="#what-is-a-model" title="Permalink to this headline">¶</a></h2>
|
||||
<p>A model defines the behaviour of the agents with a view to assessing their effects on the system as a whole.
|
||||
In practice, a model consists of at least two parts:</p>
|
||||
<ul class="simple">
|
||||
<li>Python module: the actual code that describes the behaviour.</li>
|
||||
<li>Setting up the variables in the Settings JSON file.</li>
|
||||
</ul>
|
||||
<p>This separation allows us to run the simulation with different agents.</p>
|
||||
</div>
|
||||
<div class="section" id="models-code">
|
||||
<h2>Models Code<a class="headerlink" href="#models-code" title="Permalink to this headline">¶</a></h2>
|
||||
<p>All the models are imported to the main file. The initialization look like this:</p>
|
||||
<div class="code python highlight-default"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">settings</span>
|
||||
|
||||
<span class="n">networkStatus</span> <span class="o">=</span> <span class="p">{}</span> <span class="c1"># Dict that will contain the status of every agent in the network</span>
|
||||
|
||||
<span class="n">sentimentCorrelationNodeArray</span> <span class="o">=</span> <span class="p">[]</span>
|
||||
<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">settings</span><span class="o">.</span><span class="n">network_params</span><span class="p">[</span><span class="s2">"number_of_nodes"</span><span class="p">]):</span>
|
||||
<span class="n">sentimentCorrelationNodeArray</span><span class="o">.</span><span class="n">append</span><span class="p">({</span><span class="s1">'id'</span><span class="p">:</span> <span class="n">x</span><span class="p">})</span>
|
||||
<span class="c1"># Initialize agent states. Let's assume everyone is normal.</span>
|
||||
<span class="n">init_states</span> <span class="o">=</span> <span class="p">[{</span><span class="s1">'id'</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="p">}</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">settings</span><span class="o">.</span><span class="n">network_params</span><span class="p">[</span><span class="s2">"number_of_nodes"</span><span class="p">])]</span>
|
||||
<span class="c1"># add keys as as necessary, but "id" must always refer to that state category</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>A new model have to inherit the BaseBehaviour class which is in the same module.
|
||||
There are two basics methods:</p>
|
||||
<ul class="simple">
|
||||
<li>__init__</li>
|
||||
<li>step: used to define the behaviour over time.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="variable-initialization">
|
||||
<h2>Variable Initialization<a class="headerlink" href="#variable-initialization" title="Permalink to this headline">¶</a></h2>
|
||||
<p>The different parameters of the model have to be initialize in the Simulation Settings JSON file which will be
|
||||
passed as a parameter to the simulation.</p>
|
||||
<div class="code json highlight-default"><div class="highlight"><pre><span></span><span class="p">{</span>
|
||||
<span class="s2">"agent"</span><span class="p">:</span> <span class="p">[</span><span class="s2">"SISaModel"</span><span class="p">,</span><span class="s2">"ControlModelM2"</span><span class="p">],</span>
|
||||
|
||||
<span class="s2">"neutral_discontent_spon_prob"</span><span class="p">:</span> <span class="mf">0.04</span><span class="p">,</span>
|
||||
<span class="s2">"neutral_discontent_infected_prob"</span><span class="p">:</span> <span class="mf">0.04</span><span class="p">,</span>
|
||||
<span class="s2">"neutral_content_spon_prob"</span><span class="p">:</span> <span class="mf">0.18</span><span class="p">,</span>
|
||||
<span class="s2">"neutral_content_infected_prob"</span><span class="p">:</span> <span class="mf">0.02</span><span class="p">,</span>
|
||||
|
||||
<span class="s2">"discontent_neutral"</span><span class="p">:</span> <span class="mf">0.13</span><span class="p">,</span>
|
||||
<span class="s2">"discontent_content"</span><span class="p">:</span> <span class="mf">0.07</span><span class="p">,</span>
|
||||
<span class="s2">"variance_d_c"</span><span class="p">:</span> <span class="mf">0.02</span><span class="p">,</span>
|
||||
|
||||
<span class="s2">"content_discontent"</span><span class="p">:</span> <span class="mf">0.009</span><span class="p">,</span>
|
||||
<span class="s2">"variance_c_d"</span><span class="p">:</span> <span class="mf">0.003</span><span class="p">,</span>
|
||||
<span class="s2">"content_neutral"</span><span class="p">:</span> <span class="mf">0.088</span><span class="p">,</span>
|
||||
|
||||
<span class="s2">"standard_variance"</span><span class="p">:</span> <span class="mf">0.055</span><span class="p">,</span>
|
||||
|
||||
|
||||
<span class="s2">"prob_neutral_making_denier"</span><span class="p">:</span> <span class="mf">0.035</span><span class="p">,</span>
|
||||
|
||||
<span class="s2">"prob_infect"</span><span class="p">:</span> <span class="mf">0.075</span><span class="p">,</span>
|
||||
|
||||
<span class="s2">"prob_cured_healing_infected"</span><span class="p">:</span> <span class="mf">0.035</span><span class="p">,</span>
|
||||
<span class="s2">"prob_cured_vaccinate_neutral"</span><span class="p">:</span> <span class="mf">0.035</span><span class="p">,</span>
|
||||
|
||||
<span class="s2">"prob_vaccinated_healing_infected"</span><span class="p">:</span> <span class="mf">0.035</span><span class="p">,</span>
|
||||
<span class="s2">"prob_vaccinated_vaccinate_neutral"</span><span class="p">:</span> <span class="mf">0.035</span><span class="p">,</span>
|
||||
<span class="s2">"prob_generate_anti_rumor"</span><span class="p">:</span> <span class="mf">0.035</span>
|
||||
<span class="p">}</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>In this file you will also define the models you are going to simulate. You can simulate as many models as you want.
|
||||
The simulation returns one result for each model, executing each model separately. For the usage, see <a class="reference internal" href="usage.html"><span class="doc">Usage</span></a>.</p>
|
||||
</div>
|
||||
<div class="section" id="example-model">
|
||||
<h2>Example Model<a class="headerlink" href="#example-model" title="Permalink to this headline">¶</a></h2>
|
||||
<p>In this section, we will implement a Sentiment Correlation Model.</p>
|
||||
<p>The class would look like this:</p>
|
||||
<div class="code python highlight-default"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">..BaseBehaviour</span> <span class="k">import</span> <span class="o">*</span>
|
||||
<span class="kn">from</span> <span class="nn">..</span> <span class="k">import</span> <span class="n">sentimentCorrelationNodeArray</span>
|
||||
|
||||
<span class="k">class</span> <span class="nc">SentimentCorrelationModel</span><span class="p">(</span><span class="n">BaseBehaviour</span><span class="p">):</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">environment</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">agent_id</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">state</span><span class="o">=</span><span class="p">()):</span>
|
||||
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">__init__</span><span class="p">(</span><span class="n">environment</span><span class="o">=</span><span class="n">environment</span><span class="p">,</span> <span class="n">agent_id</span><span class="o">=</span><span class="n">agent_id</span><span class="p">,</span> <span class="n">state</span><span class="o">=</span><span class="n">state</span><span class="p">)</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">outside_effects_prob</span> <span class="o">=</span> <span class="n">environment</span><span class="o">.</span><span class="n">environment_params</span><span class="p">[</span><span class="s1">'outside_effects_prob'</span><span class="p">]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">anger_prob</span> <span class="o">=</span> <span class="n">environment</span><span class="o">.</span><span class="n">environment_params</span><span class="p">[</span><span class="s1">'anger_prob'</span><span class="p">]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">joy_prob</span> <span class="o">=</span> <span class="n">environment</span><span class="o">.</span><span class="n">environment_params</span><span class="p">[</span><span class="s1">'joy_prob'</span><span class="p">]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">sadness_prob</span> <span class="o">=</span> <span class="n">environment</span><span class="o">.</span><span class="n">environment_params</span><span class="p">[</span><span class="s1">'sadness_prob'</span><span class="p">]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">disgust_prob</span> <span class="o">=</span> <span class="n">environment</span><span class="o">.</span><span class="n">environment_params</span><span class="p">[</span><span class="s1">'disgust_prob'</span><span class="p">]</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">time_awareness</span> <span class="o">=</span> <span class="p">[]</span>
|
||||
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">4</span><span class="p">):</span> <span class="c1"># In this model we have 4 sentiments</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">time_awareness</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="c1"># 0-> Anger, 1-> joy, 2->sadness, 3 -> disgust</span>
|
||||
<span class="n">sentimentCorrelationNodeArray</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">id</span><span class="p">][</span><span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">now</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">step</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">now</span><span class="p">):</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">behaviour</span><span class="p">()</span> <span class="c1"># Method which define the behaviour</span>
|
||||
<span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">step</span><span class="p">(</span><span class="n">now</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The variables will be modified by the user, so you have to include them in the Simulation Settings JSON file.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<h3><a href="index.html">Table Of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">Developing new models</a><ul>
|
||||
<li><a class="reference internal" href="#what-is-a-model">What is a model?</a></li>
|
||||
<li><a class="reference internal" href="#models-code">Models Code</a></li>
|
||||
<li><a class="reference internal" href="#variable-initialization">Variable Initialization</a></li>
|
||||
<li><a class="reference internal" href="#example-model">Example Model</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="relations">
|
||||
<h3>Related Topics</h3>
|
||||
<ul>
|
||||
<li><a href="index.html">Documentation overview</a><ul>
|
||||
<li>Previous: <a href="usage.html" title="previous chapter">Usage</a></li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div role="note" aria-label="source link">
|
||||
<h3>This Page</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="_sources/models.rst.txt"
|
||||
rel="nofollow">Show Source</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3>Quick search</h3>
|
||||
<form class="search" action="search.html" method="get">
|
||||
<div><input type="text" name="q" /></div>
|
||||
<div><input type="submit" value="Go" /></div>
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
©2017, GSI.
|
||||
|
||||
|
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 1.5.1</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.9</a>
|
||||
|
||||
|
|
||||
<a href="_sources/models.rst.txt"
|
||||
rel="nofollow">Page source</a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
6
docs/_build/html/objects.inv
vendored
@@ -1,6 +0,0 @@
|
||||
# Sphinx inventory version 2
|
||||
# Project: Soil
|
||||
# Version: 0.1
|
||||
# The remainder of this file is compressed using zlib.
|
||||
x<EFBFBD>m<EFBFBD><EFBFBD>
|
||||
<EFBFBD>0<0C><>}<7D>x<EFBFBD>4<EFBFBD><34>g/a<>纆<EFBFBD><E7BA86>6öS<C3B6><53>u<EFBFBD><75>^J<><4A><EFBFBD><EFBFBD><EFBFBD>bp<62><70>b2'<27>O$<24><>`'zh<7A>'<27><>R-<2D><><EFBFBD>H+<2B><>GH5;<3B>G<18><>1$<24><1C><><10>I<13><>팅Ϗ-Dy6Hq"<22>{$<24>\<5C><10>=<3D>J<EFBFBD><4A>Fݷ<46><12><>Ʉ<EFBFBD>n<11><>i<EFBFBD> a"<22>ҿi<D2BF>*<2A><>\i<04><>K<EFBFBD>~my+
|
104
docs/_build/html/search.html
vendored
@@ -1,104 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>Search — Soil 0.1 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: './',
|
||||
VERSION: '0.1',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true,
|
||||
SOURCELINK_SUFFIX: '.txt'
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<script type="text/javascript" src="_static/searchtools.js"></script>
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="#" />
|
||||
<script type="text/javascript">
|
||||
jQuery(function() { Search.loadIndex("searchindex.js"); });
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" id="searchindexloader"></script>
|
||||
|
||||
|
||||
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
|
||||
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
|
||||
|
||||
|
||||
</head>
|
||||
<body role="document">
|
||||
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<h1 id="search-documentation">Search</h1>
|
||||
<div id="fallback" class="admonition warning">
|
||||
<script type="text/javascript">$('#fallback').hide();</script>
|
||||
<p>
|
||||
Please activate JavaScript to enable the search
|
||||
functionality.
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
From here you can search these documents. Enter your search
|
||||
words into the box below and click "search". Note that the search
|
||||
function will automatically search for all of the words. Pages
|
||||
containing fewer words won't appear in the result list.
|
||||
</p>
|
||||
<form action="" method="get">
|
||||
<input type="text" name="q" value="" />
|
||||
<input type="submit" value="search" />
|
||||
<span id="search-progress" style="padding-left: 10px"></span>
|
||||
</form>
|
||||
|
||||
<div id="search-results">
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper"><div class="relations">
|
||||
<h3>Related Topics</h3>
|
||||
<ul>
|
||||
<li><a href="index.html">Documentation overview</a><ul>
|
||||
</ul></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
©2017, GSI.
|
||||
|
||||
|
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 1.5.1</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.9</a>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
1
docs/_build/html/searchindex.js
vendored
@@ -1 +0,0 @@
|
||||
Search.setIndex({docnames:["index","models","usage"],envversion:51,filenames:["index.rst","models.rst","usage.rst"],objects:{},objnames:{},objtypes:{},terms:{"class":1,"default":2,"import":1,"new":0,"return":[1,2],"super":1,For:[1,2],The:[1,2],There:[1,2],__init__:1,abl:2,about:0,actual:1,add:[1,2],added:2,after:2,again:2,agent:[0,1,2],agent_id:1,all:[1,2],allow:1,also:[1,2],alwai:1,analysi:1,anger:1,anger_prob:1,append:1,assess:1,assum:1,barabasi_albert_graph:2,base:0,basebehaviour:1,basic:1,been:2,befor:2,behaviour:1,can:[1,2],categori:1,clone:[],cluster:[],code:0,complete_graph:2,configur:2,consist:1,contain:1,content:[],content_discont:[1,2],content_neutr:[1,2],controlmodelm2:[1,2],correl:1,could:2,def:1,defin:[1,2],describ:1,develop:0,dict:1,differ:[1,2],discontent_cont:[1,2],discontent_neutr:[1,2],disgust:1,disgust_prob:1,dit:[],doc:[],document:[1,2],dynam:2,each:[1,2],effect:1,env:1,environ:1,environment_param:1,everi:1,everyon:1,exampl:0,execut:[1,2],fail:2,file:[1,2],fill:[],find:2,first:2,from:1,gephi:2,gexf:2,git:[],gitlab:[],going:[1,2],graph:2,gsi:[],has:2,have:[1,2],here:2,how:1,http:[],implement:[1,2],includ:1,index:[],inherit:1,init_st:1,initi:[0,2],instal:[0,2],instruct:2,joi:1,joy_prob:1,json:[1,2],kei:1,lab:[],latest:[],learn:0,least:1,let:1,like:1,look:1,main:1,mani:[1,2],margulis_gabber_galil_graph:2,max_tim:2,method:1,model:0,modifi:[1,2],modul:1,more:[0,2],must:1,necessari:1,need:2,network:[0,1],network_param:1,network_typ:2,networkstatu:1,neutral_content_infected_prob:[1,2],neutral_content_spon_prob:[1,2],neutral_discontent_infected_prob:[1,2],neutral_discontent_spon_prob:[1,2],next:2,none:[1,2],normal:1,now:1,num_trial:2,number_of_nod:[1,2],onc:2,one:[1,2],outside_effects_prob:1,over:1,packag:2,page:[],paramet:[1,2],part:1,pass:1,pictur:2,plugin:[],png:2,practic:1,prob_cured_healing_infect:[1,2],prob_cured_vaccinate_neutr:[1,2],prob_generate_anti_rumor:[1,2],prob_infect:[1,2],prob_neutral_making_deni:[1,2],prob_vaccinated_healing_infect:[1,2],prob_vaccinated_vaccinate_neutr:[1,2],provid:2,python3:2,python:[0,1],rang:1,refer:1,requir:2,result:1,run:[0,1],sad:1,sadness_prob:1,same:1,search:[],section:1,see:[1,2],self:1,sentiment:1,sentimentcorrelationmodel:1,sentimentcorrelationnodearrai:1,separ:[1,2],set:[0,1],simul:[0,1],sisamodel:[1,2],social:0,soil:2,standard_vari:[1,2],state:1,statu:1,step:[1,2],system:1,them:1,thi:[1,2],three:2,through:[],time:1,time_awar:1,timeout:2,two:1,type:0,upm:[],usag:[0,1],used:1,user:1,valid:2,variabl:[0,2],variance_c_d:[1,2],variance_d_c:[1,2],version:[],view:1,visual:2,want:[1,2],what:0,which:[1,2],whole:1,without:2,would:1,you:[1,2]},titles:["Welcome to Soil’s documentation!","Developing new models","Usage"],titleterms:{"new":1,code:1,develop:1,document:0,exampl:1,indic:[],initi:1,instal:[],model:[1,2],network:2,run:2,set:2,simul:2,soil:0,tabl:[],type:2,usag:2,variabl:1,welcom:0,what:1}})
|
197
docs/_build/html/usage.html
vendored
@@ -1,197 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>Usage — Soil 0.1 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: './',
|
||||
VERSION: '0.1',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true,
|
||||
SOURCELINK_SUFFIX: '.txt'
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<link rel="next" title="Developing new models" href="models.html" />
|
||||
<link rel="prev" title="Installation" href="installation.html" />
|
||||
|
||||
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
|
||||
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
|
||||
|
||||
</head>
|
||||
<body role="document">
|
||||
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body" role="main">
|
||||
|
||||
<div class="section" id="usage">
|
||||
<h1>Usage<a class="headerlink" href="#usage" title="Permalink to this headline">¶</a></h1>
|
||||
<p>First of all, you need to install the package. See <a class="reference internal" href="installation.html"><span class="doc">Installation</span></a> for installation instructions.</p>
|
||||
<div class="section" id="simulation-settings">
|
||||
<h2>Simulation Settings<a class="headerlink" href="#simulation-settings" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Once installed, before running a simulation, you need to configure it.</p>
|
||||
<ul>
|
||||
<li><p class="first">In the Settings JSON file you will find the configuration of the network.</p>
|
||||
<div class="code python highlight-default"><div class="highlight"><pre><span></span><span class="p">{</span>
|
||||
<span class="s2">"network_type"</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
|
||||
<span class="s2">"number_of_nodes"</span><span class="p">:</span> <span class="mi">1000</span><span class="p">,</span>
|
||||
<span class="s2">"max_time"</span><span class="p">:</span> <span class="mi">50</span><span class="p">,</span>
|
||||
<span class="s2">"num_trials"</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
|
||||
<span class="s2">"timeout"</span><span class="p">:</span> <span class="mi">2</span>
|
||||
<span class="p">}</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</li>
|
||||
<li><p class="first">In the Settings JSON file, you will also find the configuration of the models.</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="network-types">
|
||||
<h2>Network Types<a class="headerlink" href="#network-types" title="Permalink to this headline">¶</a></h2>
|
||||
<p>There are three types of network implemented, but you could add more.</p>
|
||||
<div class="code python highlight-default"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">settings</span><span class="o">.</span><span class="n">network_type</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
|
||||
<span class="n">G</span> <span class="o">=</span> <span class="n">nx</span><span class="o">.</span><span class="n">complete_graph</span><span class="p">(</span><span class="n">settings</span><span class="o">.</span><span class="n">number_of_nodes</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">settings</span><span class="o">.</span><span class="n">network_type</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
|
||||
<span class="n">G</span> <span class="o">=</span> <span class="n">nx</span><span class="o">.</span><span class="n">barabasi_albert_graph</span><span class="p">(</span><span class="n">settings</span><span class="o">.</span><span class="n">number_of_nodes</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
|
||||
<span class="k">if</span> <span class="n">settings</span><span class="o">.</span><span class="n">network_type</span> <span class="o">==</span> <span class="mi">2</span><span class="p">:</span>
|
||||
<span class="n">G</span> <span class="o">=</span> <span class="n">nx</span><span class="o">.</span><span class="n">margulis_gabber_galil_graph</span><span class="p">(</span><span class="n">settings</span><span class="o">.</span><span class="n">number_of_nodes</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
|
||||
<span class="c1"># More types of networks can be added here</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="models-settings">
|
||||
<h2>Models Settings<a class="headerlink" href="#models-settings" title="Permalink to this headline">¶</a></h2>
|
||||
<p>After having configured the simulation, the next step is setting up the variables of the models.
|
||||
For this, you will need to modify the Settings JSON file again.</p>
|
||||
<div class="code json highlight-default"><div class="highlight"><pre><span></span><span class="p">{</span>
|
||||
<span class="s2">"agent"</span><span class="p">:</span> <span class="p">[</span><span class="s2">"SISaModel"</span><span class="p">,</span><span class="s2">"ControlModelM2"</span><span class="p">],</span>
|
||||
|
||||
<span class="s2">"neutral_discontent_spon_prob"</span><span class="p">:</span> <span class="mf">0.04</span><span class="p">,</span>
|
||||
<span class="s2">"neutral_discontent_infected_prob"</span><span class="p">:</span> <span class="mf">0.04</span><span class="p">,</span>
|
||||
<span class="s2">"neutral_content_spon_prob"</span><span class="p">:</span> <span class="mf">0.18</span><span class="p">,</span>
|
||||
<span class="s2">"neutral_content_infected_prob"</span><span class="p">:</span> <span class="mf">0.02</span><span class="p">,</span>
|
||||
|
||||
<span class="s2">"discontent_neutral"</span><span class="p">:</span> <span class="mf">0.13</span><span class="p">,</span>
|
||||
<span class="s2">"discontent_content"</span><span class="p">:</span> <span class="mf">0.07</span><span class="p">,</span>
|
||||
<span class="s2">"variance_d_c"</span><span class="p">:</span> <span class="mf">0.02</span><span class="p">,</span>
|
||||
|
||||
<span class="s2">"content_discontent"</span><span class="p">:</span> <span class="mf">0.009</span><span class="p">,</span>
|
||||
<span class="s2">"variance_c_d"</span><span class="p">:</span> <span class="mf">0.003</span><span class="p">,</span>
|
||||
<span class="s2">"content_neutral"</span><span class="p">:</span> <span class="mf">0.088</span><span class="p">,</span>
|
||||
|
||||
<span class="s2">"standard_variance"</span><span class="p">:</span> <span class="mf">0.055</span><span class="p">,</span>
|
||||
|
||||
|
||||
<span class="s2">"prob_neutral_making_denier"</span><span class="p">:</span> <span class="mf">0.035</span><span class="p">,</span>
|
||||
|
||||
<span class="s2">"prob_infect"</span><span class="p">:</span> <span class="mf">0.075</span><span class="p">,</span>
|
||||
|
||||
<span class="s2">"prob_cured_healing_infected"</span><span class="p">:</span> <span class="mf">0.035</span><span class="p">,</span>
|
||||
<span class="s2">"prob_cured_vaccinate_neutral"</span><span class="p">:</span> <span class="mf">0.035</span><span class="p">,</span>
|
||||
|
||||
<span class="s2">"prob_vaccinated_healing_infected"</span><span class="p">:</span> <span class="mf">0.035</span><span class="p">,</span>
|
||||
<span class="s2">"prob_vaccinated_vaccinate_neutral"</span><span class="p">:</span> <span class="mf">0.035</span><span class="p">,</span>
|
||||
<span class="s2">"prob_generate_anti_rumor"</span><span class="p">:</span> <span class="mf">0.035</span>
|
||||
<span class="p">}</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>In this file you will define the different models you are going to simulate. You can simulate as many models
|
||||
as you want. Each model will be simulated separately.</p>
|
||||
<p>After setting up the models, you have to initialize the parameters of each one. You will find the parameters needed
|
||||
in the documentation of each model.</p>
|
||||
<p>Parameter validation will fail if a required parameter without a default has not been provided.</p>
|
||||
</div>
|
||||
<div class="section" id="running-the-simulation">
|
||||
<h2>Running the Simulation<a class="headerlink" href="#running-the-simulation" title="Permalink to this headline">¶</a></h2>
|
||||
<p>After setting all the configuration, you will be able to run the simulation. All you need to do is execute:</p>
|
||||
<div class="code bash highlight-default"><div class="highlight"><pre><span></span><span class="n">python3</span> <span class="n">soil</span><span class="o">.</span><span class="n">py</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The simulation will return a dynamic graph .gexf file which could be visualized with
|
||||
<a class="reference external" href="https://gephi.org/users/download/">Gephi</a>.</p>
|
||||
<p>It will also return one .png picture for each model simulated.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<h3><a href="index.html">Table Of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">Usage</a><ul>
|
||||
<li><a class="reference internal" href="#simulation-settings">Simulation Settings</a></li>
|
||||
<li><a class="reference internal" href="#network-types">Network Types</a></li>
|
||||
<li><a class="reference internal" href="#models-settings">Models Settings</a></li>
|
||||
<li><a class="reference internal" href="#running-the-simulation">Running the Simulation</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="relations">
|
||||
<h3>Related Topics</h3>
|
||||
<ul>
|
||||
<li><a href="index.html">Documentation overview</a><ul>
|
||||
<li>Previous: <a href="installation.html" title="previous chapter">Installation</a></li>
|
||||
<li>Next: <a href="models.html" title="next chapter">Developing new models</a></li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div role="note" aria-label="source link">
|
||||
<h3>This Page</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="_sources/usage.rst.txt"
|
||||
rel="nofollow">Show Source</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3>Quick search</h3>
|
||||
<form class="search" action="search.html" method="get">
|
||||
<div><input type="text" name="q" /></div>
|
||||
<div><input type="submit" value="Go" /></div>
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
</div>
|
||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
©2017, GSI.
|
||||
|
||||
|
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 1.5.1</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.9</a>
|
||||
|
||||
|
|
||||
<a href="_sources/usage.rst.txt"
|
||||
rel="nofollow">Page source</a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
12
docs/conf.py
@@ -31,7 +31,10 @@
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = []
|
||||
extensions = [
|
||||
"IPython.sphinxext.ipython_console_highlighting",
|
||||
"nbsphinx",
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
@@ -64,12 +67,12 @@ release = '0.1'
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# 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
|
||||
# directories to ignore when looking for source files.
|
||||
# This patterns also effect to html_static_path and html_extra_path
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', '**.ipynb_checkpoints']
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
@@ -152,6 +155,3 @@ texinfo_documents = [
|
||||
author, 'Soil', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
@@ -1,21 +1,57 @@
|
||||
.. Soil documentation master file, created by
|
||||
sphinx-quickstart on Tue Apr 25 12:48:56 2017.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to Soil's documentation!
|
||||
================================
|
||||
|
||||
Soil is an Agent-based Social Simulator in Python for modelling and simulation of 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 <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
|
||||
:width: 80%
|
||||
:align: center
|
||||
|
||||
|
||||
.. code:: bash
|
||||
|
||||
pip install soil
|
||||
|
||||
|
||||
|
||||
If you use Soil in your research, do not forget to cite this paper:
|
||||
|
||||
.. code:: bibtex
|
||||
|
||||
@inbook{soil-gsi-conference-2017,
|
||||
author = "S{\'a}nchez, Jes{\'u}s M. and Iglesias, Carlos A. and S{\'a}nchez-Rada, J. Fernando",
|
||||
booktitle = "Advances in Practical Applications of Cyber-Physical Multi-Agent Systems: The PAAMS Collection",
|
||||
doi = "10.1007/978-3-319-59930-4_19",
|
||||
editor = "Demazeau Y., Davidsson P., Bajo J., Vale Z.",
|
||||
isbn = "978-3-319-59929-8",
|
||||
keywords = "soil;social networks;agent based social simulation;python",
|
||||
month = "June",
|
||||
organization = "PAAMS 2017",
|
||||
pages = "234-245",
|
||||
publisher = "Springer Verlag",
|
||||
series = "LNAI",
|
||||
title = "{S}oil: {A}n {A}gent-{B}ased {S}ocial {S}imulator in {P}ython for {M}odelling and {S}imulation of {S}ocial {N}etworks",
|
||||
url = "https://link.springer.com/chapter/10.1007/978-3-319-59930-4_19",
|
||||
volume = "10349",
|
||||
year = "2017",
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:maxdepth: 0
|
||||
:caption: Learn more about soil:
|
||||
|
||||
installation
|
||||
usage
|
||||
models
|
||||
Tutorial <tutorial/soil_tutorial>
|
||||
notes_v1.0
|
||||
soil-vs
|
||||
|
||||
..
|
||||
|
||||
|
||||
.. Indices and tables
|
||||
|
@@ -1,7 +1,65 @@
|
||||
Installation
|
||||
------------
|
||||
The latest version can be installed through GitLab.
|
||||
|
||||
Through pip
|
||||
===========
|
||||
|
||||
The easiest way to install Soil is through pip, with Python >= 3.8:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
git clone https://lab.cluster.gsi.dit.upm.es/soil/soil.git
|
||||
pip install soil
|
||||
|
||||
|
||||
Now test that it worked by running the command line tool
|
||||
|
||||
.. code:: bash
|
||||
|
||||
soil --help
|
||||
|
||||
#or
|
||||
|
||||
python -m soil --help
|
||||
|
||||
Or, if you're using using soil programmatically:
|
||||
|
||||
.. code:: python
|
||||
|
||||
import soil
|
||||
print(soil.__version__)
|
||||
|
||||
|
||||
|
||||
Web UI
|
||||
======
|
||||
|
||||
Soil also includes a web server that allows you to upload your simulations, change parameters, and visualize the results, including a timeline of the network.
|
||||
To make it work, you have to install soil like this:
|
||||
|
||||
.. code::
|
||||
|
||||
pip install soil[web]
|
||||
|
||||
Once installed, the soil web UI can be run in two ways:
|
||||
|
||||
.. code::
|
||||
|
||||
soil-web
|
||||
|
||||
# OR
|
||||
|
||||
python -m soil.web
|
||||
|
||||
|
||||
Development
|
||||
===========
|
||||
|
||||
The latest version can be downloaded from `GitHub <https://github.com/gsi-upm/soil>`_ and installed manually:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
git clone https://github.com/gsi-upm/soil
|
||||
cd soil
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install --editable .
|
@@ -12,7 +12,7 @@ set BUILDDIR=_build
|
||||
set SPHINXPROJ=Soil
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
eE
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
|
112
docs/models.rst
@@ -1,112 +0,0 @@
|
||||
Developing new models
|
||||
---------------------
|
||||
This document describes how to develop a new analysis model.
|
||||
|
||||
What is a model?
|
||||
================
|
||||
|
||||
A model defines the behaviour of the agents with a view to assessing their effects on the system as a whole.
|
||||
In practice, a model consists of at least two parts:
|
||||
|
||||
* Python module: the actual code that describes the behaviour.
|
||||
* Setting up the variables in the Settings JSON file.
|
||||
|
||||
This separation allows us to run the simulation with different agents.
|
||||
|
||||
Models Code
|
||||
===========
|
||||
|
||||
All the models are imported to the main file. The initialization look like this:
|
||||
|
||||
.. code:: python
|
||||
|
||||
import settings
|
||||
|
||||
networkStatus = {} # Dict that will contain the status of every agent in the network
|
||||
|
||||
sentimentCorrelationNodeArray = []
|
||||
for x in range(0, settings.network_params["number_of_nodes"]):
|
||||
sentimentCorrelationNodeArray.append({'id': x})
|
||||
# Initialize agent states. Let's assume everyone is normal.
|
||||
init_states = [{'id': 0, } for _ in range(settings.network_params["number_of_nodes"])]
|
||||
# add keys as as necessary, but "id" must always refer to that state category
|
||||
|
||||
A new model have to inherit the BaseBehaviour class which is in the same module.
|
||||
There are two basics methods:
|
||||
|
||||
* __init__
|
||||
* step: used to define the behaviour over time.
|
||||
|
||||
Variable Initialization
|
||||
=======================
|
||||
|
||||
The different parameters of the model have to be initialize in the Simulation Settings JSON file which will be
|
||||
passed as a parameter to the simulation.
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"agent": ["SISaModel","ControlModelM2"],
|
||||
|
||||
"neutral_discontent_spon_prob": 0.04,
|
||||
"neutral_discontent_infected_prob": 0.04,
|
||||
"neutral_content_spon_prob": 0.18,
|
||||
"neutral_content_infected_prob": 0.02,
|
||||
|
||||
"discontent_neutral": 0.13,
|
||||
"discontent_content": 0.07,
|
||||
"variance_d_c": 0.02,
|
||||
|
||||
"content_discontent": 0.009,
|
||||
"variance_c_d": 0.003,
|
||||
"content_neutral": 0.088,
|
||||
|
||||
"standard_variance": 0.055,
|
||||
|
||||
|
||||
"prob_neutral_making_denier": 0.035,
|
||||
|
||||
"prob_infect": 0.075,
|
||||
|
||||
"prob_cured_healing_infected": 0.035,
|
||||
"prob_cured_vaccinate_neutral": 0.035,
|
||||
|
||||
"prob_vaccinated_healing_infected": 0.035,
|
||||
"prob_vaccinated_vaccinate_neutral": 0.035,
|
||||
"prob_generate_anti_rumor": 0.035
|
||||
}
|
||||
|
||||
In this file you will also define the models you are going to simulate. You can simulate as many models as you want.
|
||||
The simulation returns one result for each model, executing each model separately. For the usage, see :doc:`usage`.
|
||||
|
||||
Example Model
|
||||
=============
|
||||
|
||||
In this section, we will implement a Sentiment Correlation Model.
|
||||
|
||||
The class would look like this:
|
||||
|
||||
.. code:: python
|
||||
|
||||
from ..BaseBehaviour import *
|
||||
from .. import sentimentCorrelationNodeArray
|
||||
|
||||
class SentimentCorrelationModel(BaseBehaviour):
|
||||
|
||||
def __init__(self, environment=None, agent_id=0, state=()):
|
||||
super().__init__(environment=environment, agent_id=agent_id, state=state)
|
||||
self.outside_effects_prob = environment.environment_params['outside_effects_prob']
|
||||
self.anger_prob = environment.environment_params['anger_prob']
|
||||
self.joy_prob = environment.environment_params['joy_prob']
|
||||
self.sadness_prob = environment.environment_params['sadness_prob']
|
||||
self.disgust_prob = environment.environment_params['disgust_prob']
|
||||
self.time_awareness = []
|
||||
for i in range(4): # In this model we have 4 sentiments
|
||||
self.time_awareness.append(0) # 0-> Anger, 1-> joy, 2->sadness, 3 -> disgust
|
||||
sentimentCorrelationNodeArray[self.id][self.env.now] = 0
|
||||
|
||||
def step(self, now):
|
||||
self.behaviour() # Method which define the behaviour
|
||||
super().step(now)
|
||||
|
||||
The variables will be modified by the user, so you have to include them in the Simulation Settings JSON file.
|
38
docs/notes_v1.0.rst
Normal file
@@ -0,0 +1,38 @@
|
||||
Upgrading to Soil 1.0
|
||||
---------------------
|
||||
|
||||
What are the main changes in version 1.0?
|
||||
#########################################
|
||||
|
||||
Version 1.0 is a major rewrite of the Soil system, focused on simplifying the API, aligning it with Mesa, and making it easier to use.
|
||||
Unfortunately, this comes at the cost of backwards compatibility.
|
||||
|
||||
We drew several lessons from the previous version of Soil, and tried to address them in this version.
|
||||
Mainly:
|
||||
|
||||
- The split between simulation configuration and simulation code was overly complicated for most use cases. As a result, most users ended up reusing configuration.
|
||||
- Storing **all** the simulation data in a database is costly and unnecessary for most use cases. For most use cases, only a handful of variables need to be stored. This fits nicely with Mesa's data collection system.
|
||||
- The API was too complex, and it was difficult to understand how to use it.
|
||||
- Most parts of the API were not aligned with Mesa, which made it difficult to use Mesa's features or to integrate Soil modules with Mesa code, especially for newcomers.
|
||||
- Many parts of the API were tightly coupled, which made it difficult to find bugs, test the system and add new features.
|
||||
|
||||
The 0.30 rewrite should provide a middle ground between Soil's opinionated approach and Mesa's flexibility.
|
||||
The new Soil is less configuration-centric.
|
||||
It aims to provide more modular and convenient functions, most of which can be used in vanilla Mesa.
|
||||
|
||||
How are agents assigned to nodes in the network
|
||||
###############################################
|
||||
|
||||
The constructor of the `NetworkAgent` class has two arguments: `node_id` and `topology`.
|
||||
If `topology` is not provided, it will default to `self.model.topology`.
|
||||
This assignment might err if the model does not have a `topology` attribute, but most Soil environments derive from `NetworkEnvironment`, so they include a topology by default.
|
||||
If `node_id` is not provided, a random node will be selected from the topology, until a node with no agent is found.
|
||||
Then, the `node_id` of that node is assigned to the agent.
|
||||
If no node with no agent is found, a new node is automatically added to the topology.
|
||||
|
||||
|
||||
Can Soil environments include more than one network / topology?
|
||||
###############################################################
|
||||
|
||||
Yes, but each network has to be included manually.
|
||||
Somewhere between 0.20 and 0.30 we included the ability to include multiple networks, but it was deemed too complex and was removed.
|
2
docs/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
ipython>=7.31.1
|
||||
nbsphinx==0.9.1
|
55
docs/soil-vs.rst
Normal file
@@ -0,0 +1,55 @@
|
||||
Soil vs other ABM frameworks
|
||||
============================
|
||||
|
||||
MESA
|
||||
----
|
||||
|
||||
Starting with version 0.3, Soil has been redesigned to complement Mesa, while remaining compatible with it.
|
||||
That means that every component in Soil (i.e., Models, Environments, etc.) can be mixed with existing mesa components.
|
||||
In fact, there are examples that show how that integration may be used, in the `examples/mesa` folder in the repository.
|
||||
|
||||
Here are some reasons to use Soil instead of plain mesa:
|
||||
|
||||
- Less boilerplate for common scenarios (by some definitions of common)
|
||||
- Functions to automatically populate a topology with an agent distribution (i.e., different ratios of agent class and state)
|
||||
- The `soil.Simulation` class allows you to run multiple instances of the same experiment (i.e., multiple trials with the same parameters but a different randomness seed)
|
||||
- Reporting functions that aggregate multiple
|
||||
|
||||
Mesa compatibility
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Soil is in the process of becoming fully compatible with MESA.
|
||||
The idea is to provide a set of modular classes and functions that extend the functionality of mesa, whilst staying compatible.
|
||||
In the end, it should be possible to add regular mesa agents to a soil simulation, or use a soil agent within a mesa simulation/model.
|
||||
|
||||
This is a non-exhaustive list of tasks to achieve compatibility:
|
||||
|
||||
.. |check| raw:: html
|
||||
|
||||
☑
|
||||
|
||||
.. |uncheck| raw:: html
|
||||
|
||||
☐
|
||||
|
||||
- |check| Integrate `soil.Simulation` with mesa's runners:
|
||||
|
||||
- |check| `soil.Simulation` can replace `mesa.batchrunner`
|
||||
|
||||
- |check| Integrate `soil.Environment` with `mesa.Model`:
|
||||
|
||||
- |check| `Soil.Environment` inherits from `mesa.Model`
|
||||
- |check| `Soil.Environment` includes a Mesa-like Scheduler (see the `soil.time` module.
|
||||
- |check| Allow for `mesa.Model` to be used in a simulation.
|
||||
|
||||
- |check| Integrate `soil.Agent` with `mesa.Agent`:
|
||||
|
||||
- |check| Rename agent.id to unique_id
|
||||
- |check| mesa agents can be used in soil simulations (see `examples/mesa`)
|
||||
|
||||
- |check| Provide examples
|
||||
|
||||
- |check| Using mesa modules in a soil simulation (see `examples/mesa`)
|
||||
- |uncheck| Using soil modules in a mesa simulation (see `examples/mesa`)
|
||||
|
||||
- |uncheck| Document the new APIs and usage
|
BIN
docs/soil.png
Normal file
After Width: | Height: | Size: 43 KiB |
23569
docs/tutorial/soil_tutorial.html
Normal file
2277
docs/tutorial/soil_tutorial.ipynb
Normal file
@@ -1,99 +0,0 @@
|
||||
Usage
|
||||
-----
|
||||
|
||||
First of all, you need to install the package. See :doc:`installation` for installation instructions.
|
||||
|
||||
Simulation Settings
|
||||
===================
|
||||
|
||||
Once installed, before running a simulation, you need to configure it.
|
||||
|
||||
* In the Settings JSON file you will find the configuration of the network.
|
||||
|
||||
.. code:: python
|
||||
|
||||
{
|
||||
"network_type": 1,
|
||||
"number_of_nodes": 1000,
|
||||
"max_time": 50,
|
||||
"num_trials": 1,
|
||||
"timeout": 2
|
||||
}
|
||||
|
||||
* In the Settings JSON file, you will also find the configuration of the models.
|
||||
|
||||
Network Types
|
||||
=============
|
||||
|
||||
There are three types of network implemented, but you could add more.
|
||||
|
||||
.. code:: python
|
||||
|
||||
if settings.network_type == 0:
|
||||
G = nx.complete_graph(settings.number_of_nodes)
|
||||
if settings.network_type == 1:
|
||||
G = nx.barabasi_albert_graph(settings.number_of_nodes, 10)
|
||||
if settings.network_type == 2:
|
||||
G = nx.margulis_gabber_galil_graph(settings.number_of_nodes, None)
|
||||
# More types of networks can be added here
|
||||
|
||||
Models Settings
|
||||
===============
|
||||
|
||||
After having configured the simulation, the next step is setting up the variables of the models.
|
||||
For this, you will need to modify the Settings JSON file again.
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"agent": ["SISaModel","ControlModelM2"],
|
||||
|
||||
"neutral_discontent_spon_prob": 0.04,
|
||||
"neutral_discontent_infected_prob": 0.04,
|
||||
"neutral_content_spon_prob": 0.18,
|
||||
"neutral_content_infected_prob": 0.02,
|
||||
|
||||
"discontent_neutral": 0.13,
|
||||
"discontent_content": 0.07,
|
||||
"variance_d_c": 0.02,
|
||||
|
||||
"content_discontent": 0.009,
|
||||
"variance_c_d": 0.003,
|
||||
"content_neutral": 0.088,
|
||||
|
||||
"standard_variance": 0.055,
|
||||
|
||||
|
||||
"prob_neutral_making_denier": 0.035,
|
||||
|
||||
"prob_infect": 0.075,
|
||||
|
||||
"prob_cured_healing_infected": 0.035,
|
||||
"prob_cured_vaccinate_neutral": 0.035,
|
||||
|
||||
"prob_vaccinated_healing_infected": 0.035,
|
||||
"prob_vaccinated_vaccinate_neutral": 0.035,
|
||||
"prob_generate_anti_rumor": 0.035
|
||||
}
|
||||
|
||||
In this file you will define the different models you are going to simulate. You can simulate as many models
|
||||
as you want. Each model will be simulated separately.
|
||||
|
||||
After setting up the models, you have to initialize the parameters of each one. You will find the parameters needed
|
||||
in the documentation of each model.
|
||||
|
||||
Parameter validation will fail if a required parameter without a default has not been provided.
|
||||
|
||||
Running the Simulation
|
||||
======================
|
||||
|
||||
After setting all the configuration, you will be able to run the simulation. All you need to do is execute:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
python3 soil.py
|
||||
|
||||
The simulation will return a dynamic graph .gexf file which could be visualized with
|
||||
`Gephi <https://gephi.org/users/download/>`__.
|
||||
|
||||
It will also return one .png picture for each model simulated.
|
1
examples/README.md
Normal file
@@ -0,0 +1 @@
|
||||
Some of these examples are close to real life simulations, whereas some others are only a demonstration of Soil's capatibilities.
|
9
examples/cars/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
This example can be run like with command-line options, like this:
|
||||
|
||||
```bash
|
||||
python cars.py --level DEBUG -e summary --csv
|
||||
#or
|
||||
soil cars.py -e summary
|
||||
```
|
||||
|
||||
This will set the `CSV` (save the agent and model data to a CSV) and `summary` (print the a summary of the data to stdout) exporters, and set the log level to DEBUG.
|
251
examples/cars/cars_sim.py
Normal file
@@ -0,0 +1,251 @@
|
||||
"""
|
||||
This is an example of a simplified city, where there are Passengers and Drivers that can take those passengers
|
||||
from their location to their desired location.
|
||||
|
||||
An example scenario could play like the following:
|
||||
|
||||
- Drivers start in the `wandering` state, where they wander around the city until they have been assigned a journey
|
||||
- Passenger(1) tells every driver that it wants to request a Journey.
|
||||
- Each driver receives the request.
|
||||
If Driver(2) is interested in providing the Journey, it asks Passenger(1) to confirm that it accepts Driver(2)'s request
|
||||
- When Passenger(1) accepts the request, two things happen:
|
||||
- Passenger(1) changes its state to `driving_home`
|
||||
- Driver(2) starts moving towards the origin of the Journey
|
||||
- Once Driver(2) reaches the origin, it starts moving itself and Passenger(1) to the destination of the Journey
|
||||
- When Driver(2) reaches the destination (carrying Passenger(1) along):
|
||||
- Driver(2) starts wondering again
|
||||
- Passenger(1) dies, and is removed from the simulation
|
||||
- If there are no more passengers available in the simulation, Drivers die
|
||||
"""
|
||||
from __future__ import annotations
|
||||
from typing import Optional
|
||||
from soil import *
|
||||
from soil import events
|
||||
from mesa.space import MultiGrid
|
||||
|
||||
|
||||
# More complex scenarios may use more than one type of message between objects.
|
||||
# A common pattern is to use `enum.Enum` to represent state changes in a request.
|
||||
@dataclass
|
||||
class Journey:
|
||||
"""
|
||||
This represents a request for a journey. Passengers and drivers exchange this object.
|
||||
|
||||
A journey may have a driver assigned or not. If the driver has not been assigned, this
|
||||
object is considered a "request for a journey".
|
||||
"""
|
||||
|
||||
origin: (int, int)
|
||||
destination: (int, int)
|
||||
tip: float
|
||||
|
||||
passenger: Passenger
|
||||
driver: Optional[Driver] = None
|
||||
|
||||
|
||||
class City(Environment):
|
||||
"""
|
||||
An environment with a grid where drivers and passengers will be placed.
|
||||
|
||||
The number of drivers and riders is configurable through its parameters:
|
||||
|
||||
:param str n_cars: The total number of drivers to add
|
||||
:param str n_passengers: The number of passengers in the simulation
|
||||
:param list agents: Specific agents to use in the simulation. It overrides the `n_passengers`
|
||||
and `n_cars` params.
|
||||
:param int height: Height of the internal grid
|
||||
:param int width: Width of the internal grid
|
||||
"""
|
||||
n_cars = 1
|
||||
n_passengers = 10
|
||||
height = 100
|
||||
width = 100
|
||||
|
||||
def init(self):
|
||||
self.grid = MultiGrid(width=self.width, height=self.height, torus=False)
|
||||
if not self.agents:
|
||||
self.add_agents(Driver, k=self.n_cars)
|
||||
self.add_agents(Passenger, k=self.n_passengers)
|
||||
|
||||
for agent in self.agents:
|
||||
self.grid.place_agent(agent, (0, 0))
|
||||
self.grid.move_to_empty(agent)
|
||||
|
||||
self.total_earnings = 0
|
||||
self.add_model_reporter("total_earnings")
|
||||
|
||||
@report
|
||||
@property
|
||||
def number_passengers(self):
|
||||
return self.count_agents(agent_class=Passenger)
|
||||
|
||||
|
||||
class Driver(Evented, FSM):
|
||||
pos = None
|
||||
journey = None
|
||||
earnings = 0
|
||||
|
||||
# TODO: remove
|
||||
# def on_receive(self, msg, sender):
|
||||
# """This is not a state. It will run (and block) every time process_messages is invoked"""
|
||||
# if self.journey is None and isinstance(msg, Journey) and msg.driver is None:
|
||||
# msg.driver = self
|
||||
# self.journey = msg
|
||||
|
||||
def check_passengers(self):
|
||||
"""If there are no more passengers, stop forever"""
|
||||
c = self.count_agents(agent_class=Passenger)
|
||||
self.debug(f"Passengers left {c}")
|
||||
return c
|
||||
|
||||
@state(default=True)
|
||||
async def wandering(self):
|
||||
"""Move around the city until a journey is accepted"""
|
||||
target = None
|
||||
if not self.check_passengers():
|
||||
return self.die("No passengers left")
|
||||
self.journey = None
|
||||
while self.journey is None: # No potential journeys detected
|
||||
if target is None or not self.move_towards(target):
|
||||
target = self.random.choice(
|
||||
self.model.grid.get_neighborhood(self.pos, moore=False)
|
||||
)
|
||||
|
||||
if not self.check_passengers():
|
||||
return self.die("No passengers left")
|
||||
# This will call on_receive behind the scenes, and the agent's status will be updated
|
||||
|
||||
await self.delay(30) # Wait at least 30 seconds before checking again
|
||||
|
||||
try:
|
||||
# Re-send the journey to the passenger, to confirm that we have been selected
|
||||
self.journey = await self.journey.passenger.ask(self.journey, timeout=60, delay=5)
|
||||
except events.TimedOut:
|
||||
# No journey has been accepted. Try again
|
||||
self.journey = None
|
||||
return
|
||||
|
||||
return self.driving
|
||||
|
||||
@state
|
||||
async def driving(self):
|
||||
"""The journey has been accepted. Pick them up and take them to their destination"""
|
||||
self.info(f"Driving towards Passenger {self.journey.passenger.unique_id}")
|
||||
while self.move_towards(self.journey.origin):
|
||||
await self.delay()
|
||||
self.info(f"Driving {self.journey.passenger.unique_id} from {self.journey.origin} to {self.journey.destination}")
|
||||
while self.move_towards(self.journey.destination, with_passenger=True):
|
||||
await self.delay()
|
||||
self.info("Arrived at destination")
|
||||
self.earnings += self.journey.tip
|
||||
self.model.total_earnings += self.journey.tip
|
||||
if not self.check_passengers():
|
||||
return self.die("No passengers left")
|
||||
return self.wandering
|
||||
|
||||
def move_towards(self, target, with_passenger=False):
|
||||
"""Move one cell at a time towards a target"""
|
||||
self.debug(f"Moving { self.pos } -> { target }")
|
||||
if target[0] == self.pos[0] and target[1] == self.pos[1]:
|
||||
return False
|
||||
|
||||
next_pos = [self.pos[0], self.pos[1]]
|
||||
for idx in [0, 1]:
|
||||
if self.pos[idx] < target[idx]:
|
||||
next_pos[idx] += 1
|
||||
break
|
||||
if self.pos[idx] > target[idx]:
|
||||
next_pos[idx] -= 1
|
||||
break
|
||||
self.model.grid.move_agent(self, tuple(next_pos))
|
||||
if with_passenger:
|
||||
self.journey.passenger.pos = (
|
||||
self.pos
|
||||
) # This could be communicated through messages
|
||||
return True
|
||||
|
||||
|
||||
class Passenger(Evented, FSM):
|
||||
pos = None
|
||||
|
||||
# TODO: Remove
|
||||
# def on_receive(self, msg, sender):
|
||||
# """This is not a state. It will be run synchronously every time `process_messages` is run"""
|
||||
|
||||
# if isinstance(msg, Journey):
|
||||
# self.journey = msg
|
||||
# return msg
|
||||
|
||||
@default_state
|
||||
@state
|
||||
async def asking(self):
|
||||
destination = (
|
||||
self.random.randint(0, self.model.grid.height-1),
|
||||
self.random.randint(0, self.model.grid.width-1),
|
||||
)
|
||||
self.journey = None
|
||||
journey = Journey(
|
||||
origin=self.pos,
|
||||
destination=destination,
|
||||
tip=self.random.randint(10, 100),
|
||||
passenger=self,
|
||||
)
|
||||
|
||||
timeout = 60
|
||||
expiration = self.now + timeout
|
||||
self.info(f"Asking for journey at: { self.pos }")
|
||||
self.broadcast(journey, ttl=timeout, agent_class=Driver)
|
||||
while not self.journey:
|
||||
self.debug(f"Waiting for responses at: { self.pos }")
|
||||
try:
|
||||
offers = await self.received(expiration=expiration, delay=10)
|
||||
accepted = None
|
||||
for event in offers:
|
||||
offer = event.payload
|
||||
if isinstance(offer, Journey):
|
||||
self.journey = offer
|
||||
assert isinstance(event.sender, Driver)
|
||||
try:
|
||||
answer = await event.sender.ask(True, sender=self, timeout=60, delay=5)
|
||||
if answer:
|
||||
accepted = offer
|
||||
self.journey = offer
|
||||
break
|
||||
except events.TimedOut:
|
||||
pass
|
||||
if accepted:
|
||||
for event in offers:
|
||||
if event.payload != accepted:
|
||||
event.sender.tell(False, timeout=60, delay=5)
|
||||
|
||||
except events.TimedOut:
|
||||
self.info(f"Still no response. Waiting at: { self.pos }")
|
||||
self.broadcast(
|
||||
journey, ttl=timeout, agent_class=Driver
|
||||
)
|
||||
expiration = self.now + timeout
|
||||
self.info(f"Got a response! Waiting for driver")
|
||||
return self.driving_home
|
||||
|
||||
@state
|
||||
async def driving_home(self):
|
||||
while (
|
||||
self.pos[0] != self.journey.destination[0]
|
||||
or self.pos[1] != self.journey.destination[1]
|
||||
):
|
||||
try:
|
||||
await self.received(timeout=60)
|
||||
except events.TimedOut:
|
||||
pass
|
||||
|
||||
self.die("Got home safe!")
|
||||
|
||||
|
||||
simulation = Simulation(name="RideHailing",
|
||||
model=City,
|
||||
seed="carsSeed",
|
||||
max_time=1000,
|
||||
parameters=dict(n_passengers=2))
|
||||
|
||||
if __name__ == "__main__":
|
||||
easy(simulation)
|
40
examples/custom_timeouts_sim.py
Normal file
@@ -0,0 +1,40 @@
|
||||
from soil.agents import FSM, state, default_state
|
||||
|
||||
|
||||
class Fibonacci(FSM):
|
||||
"""Agent that only executes in t_steps that are Fibonacci numbers"""
|
||||
prev = 1
|
||||
|
||||
@default_state
|
||||
@state
|
||||
def counting(self):
|
||||
self.log("Stopping at {}".format(self.now))
|
||||
prev, self["prev"] = self["prev"], max([self.now, self["prev"]])
|
||||
return self.delay(prev)
|
||||
|
||||
|
||||
|
||||
class Odds(FSM):
|
||||
"""Agent that only executes in odd t_steps"""
|
||||
|
||||
@state(default=True)
|
||||
def odds(self):
|
||||
self.log("Stopping at {}".format(self.now))
|
||||
return self.delay(1 + (self.now % 2))
|
||||
|
||||
|
||||
from soil import Environment, Simulation
|
||||
from networkx import complete_graph
|
||||
|
||||
|
||||
class TimeoutsEnv(Environment):
|
||||
def init(self):
|
||||
self.create_network(generator=complete_graph, n=2)
|
||||
self.add_agent(agent_class=Fibonacci, node_id=0)
|
||||
self.add_agent(agent_class=Odds, node_id=1)
|
||||
|
||||
|
||||
sim = Simulation(model=TimeoutsEnv, max_steps=10)
|
||||
|
||||
if __name__ == "__main__":
|
||||
sim.run(dump=False)
|
39
examples/generator_sim.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from networkx import Graph
|
||||
import random
|
||||
import networkx as nx
|
||||
from soil import Simulation, Environment, CounterModel, parameters
|
||||
|
||||
|
||||
def mygenerator(n=5, n_edges=5):
|
||||
"""
|
||||
Just a simple generator that creates a network with n nodes and
|
||||
n_edges edges. Edges are assigned randomly, only avoiding self loops.
|
||||
"""
|
||||
G = nx.Graph()
|
||||
|
||||
for i in range(n):
|
||||
G.add_node(i)
|
||||
|
||||
for i in range(n_edges):
|
||||
nodes = list(G.nodes)
|
||||
n_in = random.choice(nodes)
|
||||
nodes.remove(n_in) # Avoid loops
|
||||
n_out = random.choice(nodes)
|
||||
G.add_edge(n_in, n_out)
|
||||
return G
|
||||
|
||||
|
||||
class GeneratorEnv(Environment):
|
||||
"""Using a custom generator for the network"""
|
||||
|
||||
generator: parameters.function = staticmethod(mygenerator)
|
||||
|
||||
def init(self):
|
||||
self.create_network(generator=self.generator, n=10, n_edges=5)
|
||||
self.add_agents(CounterModel)
|
||||
|
||||
|
||||
sim = Simulation(model=GeneratorEnv, max_steps=10)
|
||||
|
||||
if __name__ == '__main__':
|
||||
sim.run(dump=False)
|
7
examples/mesa/mesa_sim.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from soil import Simulation
|
||||
from social_wealth import MoneyEnv, graph_generator
|
||||
|
||||
sim = Simulation(name="mesa_sim", dump=False, max_steps=10, model=MoneyEnv, parameters=dict(generator=graph_generator, N=10, width=50, height=50))
|
||||
|
||||
if __name__ == "__main__":
|
||||
sim.run()
|
111
examples/mesa/server.py
Normal file
@@ -0,0 +1,111 @@
|
||||
from mesa.visualization.ModularVisualization import ModularServer
|
||||
from mesa.visualization.UserParam import Slider, Choice
|
||||
from mesa.visualization.modules import ChartModule, NetworkModule, CanvasGrid
|
||||
from social_wealth import MoneyEnv, graph_generator, SocialMoneyAgent
|
||||
import networkx as nx
|
||||
|
||||
|
||||
class MyNetwork(NetworkModule):
|
||||
def render(self, model):
|
||||
return self.portrayal_method(model)
|
||||
|
||||
|
||||
def network_portrayal(env):
|
||||
# The model ensures there is 0 or 1 agent per node
|
||||
|
||||
portrayal = dict()
|
||||
wealths = {
|
||||
node_id: data["agent"].wealth for (node_id, data) in env.G.nodes(data=True)
|
||||
}
|
||||
portrayal["nodes"] = [
|
||||
{
|
||||
"id": node_id,
|
||||
"size": 2 * (wealth + 1),
|
||||
"color": "#CC0000" if wealth == 0 else "#007959",
|
||||
# "color": "#CC0000",
|
||||
"label": f"{node_id}: {wealth}",
|
||||
}
|
||||
for (node_id, wealth) in wealths.items()
|
||||
]
|
||||
|
||||
portrayal["edges"] = [
|
||||
{"id": edge_id, "source": source, "target": target, "color": "#000000"}
|
||||
for edge_id, (source, target) in enumerate(env.G.edges)
|
||||
]
|
||||
|
||||
return portrayal
|
||||
|
||||
|
||||
def gridPortrayal(agent):
|
||||
"""
|
||||
This function is registered with the visualization server to be called
|
||||
each tick to indicate how to draw the agent in its current state.
|
||||
:param agent: the agent in the simulation
|
||||
:return: the portrayal dictionary
|
||||
"""
|
||||
color = max(10, min(agent.wealth * 10, 100))
|
||||
return {
|
||||
"Shape": "rect",
|
||||
"w": 1,
|
||||
"h": 1,
|
||||
"Filled": "true",
|
||||
"Layer": 0,
|
||||
"Label": agent.unique_id,
|
||||
"Text": agent.unique_id,
|
||||
"x": agent.pos[0],
|
||||
"y": agent.pos[1],
|
||||
"Color": f"rgba(31, 10, 255, 0.{color})",
|
||||
}
|
||||
|
||||
|
||||
grid = MyNetwork(network_portrayal, 500, 500)
|
||||
chart = ChartModule(
|
||||
[{"Label": "Gini", "Color": "Black"}], data_collector_name="datacollector"
|
||||
)
|
||||
|
||||
parameters = {
|
||||
"N": Slider(
|
||||
"N",
|
||||
5,
|
||||
1,
|
||||
10,
|
||||
1,
|
||||
description="Choose how many agents to include in the model",
|
||||
),
|
||||
"height": Slider(
|
||||
"height",
|
||||
5,
|
||||
5,
|
||||
10,
|
||||
1,
|
||||
description="Grid height",
|
||||
),
|
||||
"width": Slider(
|
||||
"width",
|
||||
5,
|
||||
5,
|
||||
10,
|
||||
1,
|
||||
description="Grid width",
|
||||
),
|
||||
"agent_class": Choice(
|
||||
"Agent class",
|
||||
value="MoneyAgent",
|
||||
choices=["MoneyAgent", "SocialMoneyAgent"],
|
||||
),
|
||||
"generator": graph_generator,
|
||||
}
|
||||
|
||||
|
||||
canvas_element = CanvasGrid(
|
||||
gridPortrayal, parameters["width"].value, parameters["height"].value, 500, 500
|
||||
)
|
||||
|
||||
|
||||
server = ModularServer(
|
||||
MoneyEnv, [grid, chart, canvas_element], "Money Model", parameters
|
||||
)
|
||||
server.port = 8521
|
||||
|
||||
if __name__ == '__main__':
|
||||
server.launch(open_browser=False)
|