mirror of
https://github.com/JHUAPL/kami.git
synced 2026-01-11 07:47:57 -05:00
Compare commits
159 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
568a5f6f5d | ||
|
|
ffe25478c1 | ||
|
|
f10bc39cce | ||
|
|
5a0f76f7fa | ||
|
|
da5347b102 | ||
|
|
dcbfddad13 | ||
|
|
7e3052bac0 | ||
|
|
14b244586c | ||
|
|
eacac535ab | ||
|
|
cdce9feb51 | ||
|
|
4602d87024 | ||
|
|
8f658dc5ca | ||
|
|
597bb298eb | ||
|
|
60018c2bce | ||
|
|
74905d04a5 | ||
|
|
adec433bce | ||
|
|
431a4ac8cf | ||
|
|
e56c257707 | ||
|
|
8cf2193f7c | ||
|
|
dacb7d38de | ||
|
|
62e80ed547 | ||
|
|
e02b339055 | ||
|
|
3af8d8b8e0 | ||
|
|
2e212ad5c6 | ||
|
|
426f1b740d | ||
|
|
0d75e3b691 | ||
|
|
9d27ec927d | ||
|
|
91fdb5fc68 | ||
|
|
c08f9509e5 | ||
|
|
2ead0b30e0 | ||
|
|
3e88c87644 | ||
|
|
003c3e1be0 | ||
|
|
f333f96bca | ||
|
|
5d58df48b2 | ||
|
|
022f9e31aa | ||
|
|
ad9dbf3eb2 | ||
|
|
0f1f1ad41a | ||
|
|
f0295e9891 | ||
|
|
8f7b6b65c2 | ||
|
|
80ac9571a7 | ||
|
|
7a7db768ed | ||
|
|
9eb47c8656 | ||
|
|
2aeef72f19 | ||
|
|
552d8fc725 | ||
|
|
8050ce9b8f | ||
|
|
317509ce0b | ||
|
|
e1028a377d | ||
|
|
860e367785 | ||
|
|
445e10252e | ||
|
|
a08b316b96 | ||
|
|
893c8da8e2 | ||
|
|
10cb3cf3f3 | ||
|
|
3ed40f4cdb | ||
|
|
003e7a4d12 | ||
|
|
6751951e84 | ||
|
|
7def091083 | ||
|
|
a872c89b78 | ||
|
|
b573a3f676 | ||
|
|
d2ccdbea73 | ||
|
|
b9b1cb12e6 | ||
|
|
948f744414 | ||
|
|
06355bdf67 | ||
|
|
a215ca7320 | ||
|
|
097024a6b8 | ||
|
|
a0c2a7869c | ||
|
|
a45b9bf22d | ||
|
|
7849a49355 | ||
|
|
f86dbd4c24 | ||
|
|
cdc9c56765 | ||
|
|
972e83aeeb | ||
|
|
922572973d | ||
|
|
8906825235 | ||
|
|
64dc35c9c4 | ||
|
|
6c34098b53 | ||
|
|
bc0740219f | ||
|
|
11660de09c | ||
|
|
3b3a7f0bca | ||
|
|
d4db95f925 | ||
|
|
0727030bcb | ||
|
|
85aede0721 | ||
|
|
06e98b1f94 | ||
|
|
8613bc29b3 | ||
|
|
67cdf9bc29 | ||
|
|
e7a5f342bf | ||
|
|
9333e1c2fa | ||
|
|
f18e2c99da | ||
|
|
d884e603cb | ||
|
|
78aca204f4 | ||
|
|
50b147beca | ||
|
|
3dff3afb6e | ||
|
|
80f98bdde0 | ||
|
|
72f0074d81 | ||
|
|
f6967d0ec3 | ||
|
|
318f6dfecf | ||
|
|
05995525f1 | ||
|
|
d1ba71b416 | ||
|
|
13185c1e0b | ||
|
|
beb6a1d99b | ||
|
|
a801720aa5 | ||
|
|
8a1ac91fae | ||
|
|
7c6fdd0b11 | ||
|
|
82d96676d7 | ||
|
|
40cbaced51 | ||
|
|
2351e6f23e | ||
|
|
018379554d | ||
|
|
0f8ee56948 | ||
|
|
237be8bbe8 | ||
|
|
e21da48f5a | ||
|
|
e19dc7b449 | ||
|
|
d52102615c | ||
|
|
d1b6c60ec6 | ||
|
|
0d00c3ec40 | ||
|
|
25b14c8aff | ||
|
|
2d88d32bd6 | ||
|
|
0015cdbd5a | ||
|
|
7a335feae2 | ||
|
|
8742637ac8 | ||
|
|
2f6c26406a | ||
|
|
cab9c86819 | ||
|
|
743c388fb3 | ||
|
|
cc7cf670bb | ||
|
|
c91caf723e | ||
|
|
c25d73b56e | ||
|
|
f7bf234f6a | ||
|
|
55b819ac8c | ||
|
|
ec28727ed9 | ||
|
|
a8c4212b94 | ||
|
|
fa74943eb5 | ||
|
|
6320f2d6da | ||
|
|
f8e26344c1 | ||
|
|
95165f4d72 | ||
|
|
3faa5b4c50 | ||
|
|
cddf317ee6 | ||
|
|
f87f9850d9 | ||
|
|
e82b08e8f8 | ||
|
|
10c42ec2fe | ||
|
|
084eda63f3 | ||
|
|
1c60e90514 | ||
|
|
9791cd6d8b | ||
|
|
e1c83d9158 | ||
|
|
9bdde03463 | ||
|
|
f5ae008df5 | ||
|
|
cf0b75c5fb | ||
|
|
cf35c348ca | ||
|
|
b5abb50892 | ||
|
|
1d192c99f6 | ||
|
|
b9a41e08a3 | ||
|
|
52191803ef | ||
|
|
9859000182 | ||
|
|
cc189f80c5 | ||
|
|
ddf96401c5 | ||
|
|
a5d3f8b1ad | ||
|
|
7af8e59b96 | ||
|
|
8747b22310 | ||
|
|
8d9b217298 | ||
|
|
7567ee7f36 | ||
|
|
c7685f03b7 | ||
|
|
715d4e0f7b | ||
|
|
7d53af7bdf |
2
.github/workflows/build-develop.yml
vendored
2
.github/workflows/build-develop.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
conan profile update settings.compiler.libcxx=libstdc++11 default
|
||||
|
||||
- name: Conan Install Dependencies
|
||||
run: conan install -if build .
|
||||
run: conan install -if build . --build=missing
|
||||
|
||||
- name: Configure CMake
|
||||
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||
|
||||
2
.github/workflows/build-main.yml
vendored
2
.github/workflows/build-main.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
conan profile update settings.compiler.libcxx=libstdc++11 default
|
||||
|
||||
- name: Conan Install Dependencies
|
||||
run: conan install -if build .
|
||||
run: conan install -if build . --build=missing
|
||||
|
||||
- name: Configure CMake
|
||||
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||
|
||||
89
.github/workflows/codeql-analysis.yml
vendored
Normal file
89
.github/workflows/codeql-analysis.yml
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main", "develop" ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ "main", "develop" ]
|
||||
schedule:
|
||||
- cron: '38 4 * * 1'
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Debug
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'cpp' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
- name: Install Conan
|
||||
id: conan
|
||||
uses: turtlebrowser/get-conan@main
|
||||
|
||||
- name: Conan The Frogarian
|
||||
run: conan frogarian
|
||||
|
||||
- name: Conan Profile Setup
|
||||
run: |
|
||||
conan config init
|
||||
conan profile update settings.compiler.libcxx=libstdc++11 default
|
||||
|
||||
- name: Conan Install Dependencies
|
||||
run: conan install -if build . --build=missing
|
||||
|
||||
- name: Configure CMake
|
||||
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
|
||||
run: cmake -B build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Build
|
||||
# Build your program with the given configuration
|
||||
run: cmake --build build --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Test
|
||||
# Execute tests defined by the CMake configuration.
|
||||
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
|
||||
run: cmake --build build --target test --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -40,4 +40,8 @@ build/*
|
||||
*.user
|
||||
|
||||
# VSCode files
|
||||
.vscode
|
||||
.vscode
|
||||
|
||||
# IDEA files
|
||||
.idea
|
||||
cmake-build-*
|
||||
|
||||
159
.gitlab-ci.yml
159
.gitlab-ci.yml
@@ -1,159 +0,0 @@
|
||||
stages:
|
||||
- analysis
|
||||
- build
|
||||
- test
|
||||
|
||||
cppcheck:
|
||||
image: ubuntu:bionic
|
||||
stage: analysis
|
||||
before_script:
|
||||
- apt update
|
||||
- apt install -y --no-install-recommends cppcheck=1.82-1 python3-pygments python-pygments
|
||||
- cppcheck --version
|
||||
script:
|
||||
- mkdir cppcheck
|
||||
#- cppcheck . -I include/ --verbose --enable=all --inconclusive --language=c++ --error-exitcode=1
|
||||
- cppcheck . -I include/ --enable=all --inconclusive --xml-version=2 --force --library=windows,posix,gnu . 2> cppcheck/result.xml
|
||||
- cppcheck-htmlreport --source-encoding="iso8859-1" --title="my project name" --source-dir . --report-dir=cppcheck --file=cppcheck/result.xml
|
||||
artifacts:
|
||||
paths:
|
||||
- cppcheck/
|
||||
expire_in: 1 week
|
||||
|
||||
|
||||
.build_template: &job_definition
|
||||
image: conanio/gcc7
|
||||
stage: build
|
||||
before_script:
|
||||
- env
|
||||
- sudo apt update
|
||||
- sudo apt install -y python3-pip
|
||||
- sudo pip3 install gcovr
|
||||
- sudo chmod +x ./.ci-files/install_conan.sh
|
||||
- sudo chmod +x ./.ci-files/install_cmake.sh
|
||||
- sudo chmod +x ./.ci-files/run_conan_server.sh
|
||||
- ./.ci-files/install_conan.sh
|
||||
- ./.ci-files/install_cmake.sh
|
||||
#
|
||||
- ./.ci-files/run_conan_server.sh
|
||||
- conan remote add local http://localhost:9300
|
||||
- conan remote list
|
||||
|
||||
script:
|
||||
- echo $USER --- $HOME
|
||||
- echo Working directory $PWD
|
||||
### Test that we can build the library properly
|
||||
- mkdir -p build && cd build
|
||||
- cmake .. -DCMAKE_INSTALL_PREFIX=$HOME/local
|
||||
- cmake --build .
|
||||
- ctest
|
||||
- mkdir -p artifacts/coverage
|
||||
- gcovr . -r .. --html-details --html -o artifacts/coverage/index.html -e ../test/third_party
|
||||
- sudo cmake --build . --target install
|
||||
## Test that we can build the test application
|
||||
## and the find_package method works correctly.
|
||||
- rm -rf *
|
||||
- cmake ../test_cmake_install/cmake -D CMAKE_PREFIX_PATH=$HOME/local
|
||||
- cmake --build .
|
||||
- ctest
|
||||
## Build the Conan package and upload it to the local repo.
|
||||
- cd ..
|
||||
- conan user -p demo demo -r=local
|
||||
- conan create . local/testing
|
||||
- conan upload foo* -r=local -c --all
|
||||
- conan search "*" -r=local
|
||||
artifacts:
|
||||
paths:
|
||||
- artifacts/*
|
||||
expire_in: 1 week
|
||||
|
||||
build-gcc5:
|
||||
<<: *job_definition # Merge the contents of the 'job_definition' alias
|
||||
image: conanio/gcc5
|
||||
|
||||
build-gcc6:
|
||||
<<: *job_definition # Merge the contents of the 'job_definition' alias
|
||||
image: conanio/gcc6
|
||||
|
||||
build-gcc7:
|
||||
<<: *job_definition # Merge the contents of the 'job_definition' alias
|
||||
image: conanio/gcc7
|
||||
|
||||
build-gcc8:
|
||||
<<: *job_definition # Merge the contents of the 'job_definition' alias
|
||||
image: conanio/gcc8
|
||||
|
||||
build-clang60:
|
||||
<<: *job_definition
|
||||
image: conanio/clang60
|
||||
|
||||
build-clang40:
|
||||
<<: *job_definition
|
||||
image: conanio/clang40
|
||||
|
||||
build-clang50:
|
||||
<<: *job_definition
|
||||
image: conanio/clang50
|
||||
|
||||
build-clang7:
|
||||
<<: *job_definition
|
||||
image: conanio/clang7
|
||||
|
||||
build-clang39:
|
||||
<<: *job_definition
|
||||
image: conanio/clang39
|
||||
|
||||
|
||||
###############################################################################
|
||||
# The test package stage tests whether the different Conan generators work
|
||||
# correctly with the
|
||||
###############################################################################
|
||||
.test_package: &test_package_definition
|
||||
image: conanio/gcc7
|
||||
stage: test
|
||||
dependencies:
|
||||
- build-gcc5
|
||||
- build-gcc6
|
||||
- build-gcc7
|
||||
- build-gcc8
|
||||
before_script:
|
||||
- env
|
||||
- sudo apt update
|
||||
- sudo chmod +x ./.ci-files/install_conan.sh
|
||||
- sudo chmod +x ./.ci-files/install_cmake.sh
|
||||
- sudo chmod +x ./.ci-files/run_conan_server.sh
|
||||
- ./.ci-files/install_conan.sh
|
||||
- ./.ci-files/install_cmake.sh
|
||||
#
|
||||
- ./.ci-files/run_conan_server.sh
|
||||
- conan remote add local http://localhost:9300
|
||||
- conan remote list
|
||||
script:
|
||||
# Create and uplaod the recipe to the repository
|
||||
- conan user -p demo demo -r=local
|
||||
- conan create . local/testing
|
||||
- conan upload foo* -r=local -c --all
|
||||
# Test the Conan cmake generator
|
||||
- mkdir build && cd build
|
||||
- conan install ../test_cmake_install/conan_cmake_generator --build missing
|
||||
- cmake ../test_cmake_install/conan_cmake_generator
|
||||
- cmake --build .
|
||||
- rm -rf *
|
||||
# Test the Conan cmake_paths generator
|
||||
- conan install ../test_cmake_install/conan_cmake_paths_generator --build missing
|
||||
- cmake ../test_cmake_install/conan_cmake_paths_generator
|
||||
- cmake --build .
|
||||
- rm -rf *
|
||||
# Test the Conan find_package generator
|
||||
- conan install ../test_cmake_install/conan_cmake_find_package_generator --build missing
|
||||
- cmake ../test_cmake_install/conan_cmake_find_package_generator
|
||||
- cmake --build .
|
||||
- rm -rf *
|
||||
|
||||
test-package-gcc8:
|
||||
<<: *test_package_definition # Merge the contents of the 'job_definition' alias
|
||||
image: conanio/gcc8
|
||||
|
||||
test-package-clang60:
|
||||
<<: *test_package_definition # Merge the contents of the 'job_definition' alias
|
||||
image: conanio/clang60
|
||||
89
.travis.yml
89
.travis.yml
@@ -1,89 +0,0 @@
|
||||
linux: &linux
|
||||
os: linux
|
||||
dist: xenial
|
||||
language: python
|
||||
python: "3.7"
|
||||
services:
|
||||
- docker
|
||||
|
||||
osx: &osx
|
||||
os: osx
|
||||
language: generic
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- <<: *linux
|
||||
env: CONAN_GCC_VERSIONS=4.9 CONAN_DOCKER_IMAGE=conanio/gcc49
|
||||
- <<: *linux
|
||||
env: CONAN_GCC_VERSIONS=5 CONAN_DOCKER_IMAGE=conanio/gcc5
|
||||
- <<: *linux
|
||||
env: CONAN_GCC_VERSIONS=6 CONAN_DOCKER_IMAGE=conanio/gcc6
|
||||
- <<: *linux
|
||||
env: CONAN_GCC_VERSIONS=7 CONAN_DOCKER_IMAGE=conanio/gcc7
|
||||
- <<: *linux
|
||||
env: CONAN_GCC_VERSIONS=8 CONAN_DOCKER_IMAGE=conanio/gcc8
|
||||
- <<: *linux
|
||||
env: CONAN_CLANG_VERSIONS=3.9 CONAN_DOCKER_IMAGE=conanio/clang39
|
||||
- <<: *linux
|
||||
env: CONAN_CLANG_VERSIONS=4.0 CONAN_DOCKER_IMAGE=conanio/clang40
|
||||
- <<: *linux
|
||||
env: CONAN_CLANG_VERSIONS=5.0 CONAN_DOCKER_IMAGE=conanio/clang50
|
||||
- <<: *linux
|
||||
env: CONAN_CLANG_VERSIONS=6.0 CONAN_DOCKER_IMAGE=conanio/clang60
|
||||
- <<: *linux
|
||||
env: CONAN_CLANG_VERSIONS=7.0 CONAN_DOCKER_IMAGE=conanio/clang7
|
||||
- <<: *osx
|
||||
osx_image: xcode7.3
|
||||
env: CONAN_APPLE_CLANG_VERSIONS=7.3
|
||||
- <<: *osx
|
||||
osx_image: xcode8.3
|
||||
env: CONAN_APPLE_CLANG_VERSIONS=8.1
|
||||
- <<: *osx
|
||||
osx_image: xcode9
|
||||
env: CONAN_APPLE_CLANG_VERSIONS=9.0
|
||||
- <<: *osx
|
||||
osx_image: xcode9.4
|
||||
env: CONAN_APPLE_CLANG_VERSIONS=9.1
|
||||
- <<: *osx
|
||||
osx_image: xcode10.1
|
||||
env: CONAN_APPLE_CLANG_VERSIONS=10.0
|
||||
|
||||
addons:
|
||||
apt:
|
||||
- doxygen
|
||||
|
||||
install:
|
||||
# Get some stuff we need for Sphinx and Conan
|
||||
- pip3 install -r docs/requirements.txt
|
||||
- pip3 install conan
|
||||
# first we create a directory for the CMake binaries
|
||||
- DEPS_DIR="${TRAVIS_BUILD_DIR}/deps"
|
||||
- mkdir ${DEPS_DIR} && cd ${DEPS_DIR}
|
||||
# we use wget to fetch the cmake binaries
|
||||
- travis_retry wget --no-check-certificate https://github.com/Kitware/CMake/releases/download/v3.18.3/cmake-3.18.3-Linux-x86_64.tar.gz
|
||||
- travis_retry wget --no-check-certificate https://github.com/Kitware/CMake/releases/download/v3.18.3/cmake-3.18.3-SHA-256.txt
|
||||
- travis_retry wget --no-check-certificate https://github.com/Kitware/CMake/releases/download/v3.18.3/cmake-3.18.3-SHA-256.txt.asc
|
||||
# this is optional, but useful:
|
||||
# do a quick md5 check to ensure that the archive we downloaded did not get compromised, leave off GPG verification for now
|
||||
- sha256sum --ignore-missing -c cmake-3.18.3-SHA-256.txt
|
||||
# extract the binaries; the output here is quite lengthy,
|
||||
# so we swallow it to not clutter up the travis console
|
||||
- tar -xvf cmake-3.18.3-Linux-x86_64.tar.gz > /dev/null
|
||||
- mv cmake-3.18.3-Linux-x86_64 cmake-install
|
||||
# add both the top-level directory and the bin directory from the archive
|
||||
# to the system PATH. By adding it to the front of the path we hide the
|
||||
# preinstalled CMake with our own.
|
||||
- PATH=${DEPS_DIR}/cmake-install:${DEPS_DIR}/cmake-install/bin:$PATH
|
||||
# don't forget to switch back to the main build directory once you are done
|
||||
- cd ${TRAVIS_BUILD_DIR}
|
||||
|
||||
before_script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- conan install ..
|
||||
- cmake ..
|
||||
|
||||
script: |
|
||||
- cmake --build .
|
||||
- ctest -C Debug
|
||||
- cmake --build . --target install
|
||||
25
CITATION.cff
Normal file
25
CITATION.cff
Normal file
@@ -0,0 +1,25 @@
|
||||
# This CITATION.cff file was generated with cffinit.
|
||||
# Visit https://bit.ly/cffinit to generate yours today!
|
||||
|
||||
cff-version: 1.2.0
|
||||
title: Kami is Agent-Based Modeling in Modern C++
|
||||
message: >-
|
||||
If you use this software, please cite it using the
|
||||
metadata from this file.
|
||||
type: software
|
||||
license: MIT
|
||||
repository-code: "https://github.com/JHUAPL/kami"
|
||||
authors:
|
||||
- given-names: James Patrick
|
||||
family-names: Howard
|
||||
name-suffix: II
|
||||
email: james.howard@jhu.edu
|
||||
affiliation: Johns Hopkins Applied Physics Laboratory
|
||||
orcid: 'https://orcid.org/0000-0003-4530-1547'
|
||||
keywords:
|
||||
- "agent-based modelling"
|
||||
- research
|
||||
identifiers:
|
||||
- description: "This is the collection of archived snapshots of all versions of Kami"
|
||||
type: doi
|
||||
value: 10.5281/zenodo.6975259
|
||||
@@ -1,44 +1,17 @@
|
||||
#
|
||||
# This is the Top level CMakelists file which creates the namespace and
|
||||
# organizes all sublibraries under it.
|
||||
#
|
||||
# The project name in this file is considered the "Namespace"
|
||||
# and any libraries under it will be given a target of
|
||||
#
|
||||
# Namespace::library_name
|
||||
#
|
||||
#
|
||||
# This Lists file was modified from https://github.com/forexample/package-example
|
||||
#
|
||||
# This file creates project 'Foo' with two library targets 'bar' and 'cat'.
|
||||
# Target 'cat' depends on 'bar'. After installation this project can be found
|
||||
# by 'find_package(... CONFIG)' command:
|
||||
#
|
||||
# find_package(foo CONFIG REQUIRED)
|
||||
# target_link_libraries(... foo::bar)
|
||||
#
|
||||
# Note that requirements propagated automatically, for example:
|
||||
# * Foo::baz linked automatically
|
||||
# * <prefix>/include added to header search path
|
||||
# * FOO_BAZ_DEBUG=1/FOO_BAR_DEBUG=1 added on Debug
|
||||
# * FOO_BAZ_DEBUG=0/FOO_BAR_DEBUG=0 added on other configurations
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
####
|
||||
# Set minimum version of CMake. We need 3.13 at least.
|
||||
cmake_minimum_required(VERSION 3.13) # GENERATOR_IS_MULTI_CONFIG
|
||||
set(PROJECT_NAME "kami")
|
||||
|
||||
set(KAMI_VERSION_MAJOR 0)
|
||||
set(KAMI_VERSION_MINOR 4)
|
||||
set(KAMI_VERSION_PATCH 1)
|
||||
set(KAMI_VERSION_STRING ${KAMI_VERSION_MAJOR}.${KAMI_VERSION_MINOR}.${KAMI_VERSION_PATCH})
|
||||
set(VERSION_MAJOR 0)
|
||||
set(VERSION_MINOR 7)
|
||||
set(VERSION_PATCH 2)
|
||||
set(VERSION_STRING ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH})
|
||||
|
||||
################################################################################
|
||||
# Set variables for the project. The:
|
||||
# * PROJECT_NAME
|
||||
# * PROJECT_VERSION
|
||||
# * PROJECT_NAMESPACE should be the same as the project.
|
||||
project(kami VERSION ${KAMI_VERSION_STRING}
|
||||
LANGUAGES CXX)
|
||||
project(${PROJECT_NAME}
|
||||
VERSION ${VERSION_STRING}
|
||||
LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
@@ -48,14 +21,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_BINARY_DIR}" CACHE STRING "M
|
||||
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
|
||||
conan_basic_setup()
|
||||
|
||||
set(PROJECT_NAMESPACE kami ) # The project namespace. Library targets
|
||||
# will be referred by
|
||||
# foo::bar. This value should usually be
|
||||
# the same as the project.
|
||||
################################################################################
|
||||
|
||||
find_package(spdlog)
|
||||
find_package(Threads)
|
||||
set(PROJECT_NAMESPACE ${PROJECT_NAME})
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCC)
|
||||
option(ENABLE_COVERAGE "Enable coverage reporting for gcc/clang" FALSE)
|
||||
@@ -75,7 +41,6 @@ endif()
|
||||
|
||||
include(cmake/functions.cmake)
|
||||
include(cmake/warnings.cmake)
|
||||
include(cmake/cppcheck.cmake)
|
||||
|
||||
################################################################################
|
||||
# Sub libraries.
|
||||
@@ -96,7 +61,7 @@ ENDFOREACH()
|
||||
################################################################################
|
||||
# Examples.
|
||||
#
|
||||
# Each example will be built as a static or shared library and a
|
||||
# Each example will be built as a static or shared binary and a
|
||||
# target will be created for it.
|
||||
################################################################################
|
||||
|
||||
@@ -126,7 +91,7 @@ include(GNUInstallDirs)
|
||||
# * <prefix>/include/
|
||||
set(config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
|
||||
|
||||
set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated")
|
||||
set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated")
|
||||
|
||||
# Configuration
|
||||
set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake")
|
||||
@@ -159,38 +124,32 @@ configure_package_config_file(
|
||||
# * <prefix>/lib/libbaz.a
|
||||
# * header location after install: <prefix>/include/foo/Bar.hpp
|
||||
# * headers can be included by C++ code `#include <foo/Bar.hpp>`
|
||||
install(
|
||||
TARGETS
|
||||
${sub_modules} ${example_modules}
|
||||
${COVERAGE_INSTALL_TARGET}
|
||||
EXPORT
|
||||
"${TARGETS_EXPORT_NAME}"
|
||||
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||
)
|
||||
install(
|
||||
TARGETS ${sub_modules} ${example_modules} ${COVERAGE_INSTALL_TARGET}
|
||||
EXPORT "${TARGETS_EXPORT_NAME}"
|
||||
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||
)
|
||||
|
||||
|
||||
# Config
|
||||
# * <prefix>/lib/cmake/Foo/FooConfig.cmake
|
||||
# * <prefix>/lib/cmake/Foo/FooConfigVersion.cmake
|
||||
install(
|
||||
FILES
|
||||
"${project_config}" "${version_config}"
|
||||
DESTINATION
|
||||
"${config_install_dir}"
|
||||
FILES "${project_config}" "${version_config}"
|
||||
DESTINATION "${config_install_dir}"
|
||||
)
|
||||
|
||||
# Config
|
||||
# * <prefix>/lib/cmake/Foo/FooTargets.cmake
|
||||
install(
|
||||
EXPORT
|
||||
"${TARGETS_EXPORT_NAME}"
|
||||
NAMESPACE
|
||||
"${namespace}"
|
||||
DESTINATION
|
||||
"${config_install_dir}"
|
||||
EXPORT "${TARGETS_EXPORT_NAME}"
|
||||
NAMESPACE "${namespace}"
|
||||
DESTINATION "${config_install_dir}"
|
||||
)
|
||||
|
||||
add_subdirectory(docs)
|
||||
|
||||
set(ignoreMe "${CMAKE_C_COMPILER}")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2020 The Johns Hopkins University Applied Physics
|
||||
Copyright (c) 2020-2023 The Johns Hopkins University Applied Physics
|
||||
Laboratory LLC
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
|
||||
26
README.md
26
README.md
@@ -1,11 +1,24 @@
|
||||
[](https://github.com/JHUAPL/kami/actions/workflows/build-main.yml)
|
||||
[](https://github.com/JHUAPL/kami/actions/workflows/build-develop.yml)
|
||||
[](https://kami.readthedocs.io/en/latest/?badge=latest)
|
||||
[](https://kami.readthedocs.io/en/latest/?badge=latest)
|
||||
[](https://kami.readthedocs.io/en/main/)
|
||||
[](https://github.com/JHUAPL/kami/releases)
|
||||

|
||||
[](https://doi.org/10.5281/zenodo.6975259)
|
||||
|
||||
# Kami is Agent-Based Modeling in Modern C++
|
||||
|
||||
Agent-based models (ABMs) are models for simulating the actions of
|
||||
individual actors within a provided environment to understand the
|
||||
behavior of the agents, most individually and collectively. ABMs
|
||||
are particularly suited for addressing problems governed by nonlinear
|
||||
processes or where there is a wide variety of potential responses
|
||||
an individual agent may provide depending on the environment and
|
||||
behavior of other agents. Because of this, ABMs have become powerful
|
||||
tools in both simulation and modeling, especially in public health
|
||||
and ecology, where they are also known as individual-based models.
|
||||
ABMs also provide support in economic, business, robotics, and many
|
||||
other fields.
|
||||
|
||||
## Compiling
|
||||
|
||||
```Bash
|
||||
@@ -40,15 +53,18 @@ conan create . kami/develop
|
||||
## Direct Dependencies
|
||||
|
||||
* [CLI11](https://github.com/CLIUtils/CLI11)
|
||||
* [Google Test](https://github.com/google/googletest)
|
||||
* [neargye-semver](https://github.com/Neargye/semver)
|
||||
* [spdlog](https://github.com/gabime/spdlog)
|
||||
|
||||
CLI11 and spdlog are both used extensively in the examples and unit
|
||||
tests. Neither is used directly by the Kami library.
|
||||
CLI11 and spdlog are both used extensively in the examples.
|
||||
Neither is used directly by the Kami library.
|
||||
|
||||
## Contribution guidelines
|
||||
|
||||
* Use [GitFlow](http://nvie.com/posts/a-successful-git-branching-model/)
|
||||
|
||||
* Use [Google Test](https://github.com/google/googletest)
|
||||
|
||||
## For more information
|
||||
|
||||
* James P. Howard, II <<james.howard@jhu.edu>>
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
add_custom_target(cppcheck
|
||||
#COMMAND mkdir -p coverage
|
||||
#COMMAND ${CMAKE_MAKE_PROGRAM} test
|
||||
#WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
|
||||
add_custom_command(TARGET cppcheck
|
||||
COMMAND echo "=================== CPPCHECK ===================="
|
||||
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/cppcheck
|
||||
COMMAND cppcheck . -I include/ --enable=all --inconclusive --xml-version=2 --force --library=windows,posix,gnu . --output-file=${CMAKE_BINARY_DIR}/cppcheck/result.xml
|
||||
COMMAND cppcheck-htmlreport --source-encoding="iso8859-1" --title="mmp2top" --source-dir . --report-dir=${CMAKE_BINARY_DIR}/cppcheck --file=${CMAKE_BINARY_DIR}/cppcheck/result.xml
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} # Need separate command for this line
|
||||
)
|
||||
15
conanfile.py
15
conanfile.py
@@ -3,10 +3,10 @@ from conans import ConanFile, CMake
|
||||
|
||||
class KamiConan(ConanFile):
|
||||
name = "kami"
|
||||
version = "0.4.1"
|
||||
version = "0.7.2"
|
||||
license = "MIT"
|
||||
author = "James P. Howard, II <james.howard@jhu.edu>"
|
||||
url = "http://github.com/jhuapl/kami"
|
||||
url = "https://github.com/jhuapl/kami"
|
||||
description = "Agent-Based Modeling in Modern C++"
|
||||
topics = ("agent-based modeling", "simulation", "orms")
|
||||
settings = "os", "compiler", "build_type", "arch"
|
||||
@@ -14,7 +14,7 @@ class KamiConan(ConanFile):
|
||||
exports_sources = "*"
|
||||
|
||||
options = {"shared": [True, False], "fPIC": [True, False]}
|
||||
default_options = {"shared": True, "fPIC": True}
|
||||
default_options = {"shared": False, "fPIC": False}
|
||||
|
||||
|
||||
def _configure_cmake(self):
|
||||
@@ -44,6 +44,9 @@ class KamiConan(ConanFile):
|
||||
|
||||
|
||||
def requirements(self):
|
||||
self.requires("fmt/7.1.3")
|
||||
self.requires("spdlog/1.8.5")
|
||||
self.requires("cli11/1.9.1")
|
||||
self.requires("fmt/9.0.0")
|
||||
self.requires("spdlog/1.10.0")
|
||||
self.requires("cli11/2.2.0")
|
||||
self.requires("neargye-semver/0.3.0")
|
||||
self.requires("gtest/cci.20210126")
|
||||
self.requires("nlohmann_json/3.11.1")
|
||||
|
||||
371
docs/Doxyfile.in
371
docs/Doxyfile.in
@@ -38,7 +38,7 @@ PROJECT_NAME = "Kami"
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = 0.1.0
|
||||
PROJECT_NUMBER = "@VERSION_MAJOR@.@VERSION_MINOR@.@VERSION_PATCH@"
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
@@ -837,7 +837,7 @@ EXCLUDE_PATTERNS =
|
||||
# Note that the wildcards are matched against the file with absolute path, so to
|
||||
# exclude all test directories use the pattern */test/*
|
||||
|
||||
EXCLUDE_SYMBOLS = KAMI_SEQUENTIAL_H
|
||||
EXCLUDE_SYMBOLS = *_H
|
||||
|
||||
# The EXAMPLE_PATH tag can be used to specify one or more files or directories
|
||||
# that contain example code fragments that are included (see the \include
|
||||
@@ -1034,7 +1034,7 @@ IGNORE_PREFIX =
|
||||
# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
|
||||
# The default value is: YES.
|
||||
|
||||
GENERATE_HTML = NO
|
||||
GENERATE_HTML = YES
|
||||
|
||||
# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
|
||||
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
|
||||
@@ -1425,17 +1425,6 @@ EXT_LINKS_IN_WINDOW = NO
|
||||
|
||||
FORMULA_FONTSIZE = 10
|
||||
|
||||
# Use the FORMULA_TRANPARENT tag to determine whether or not the images
|
||||
# generated for formulas are transparent PNGs. Transparent PNGs are not
|
||||
# supported properly for IE 6.0, but are supported on all modern browsers.
|
||||
#
|
||||
# Note that when changing this option you need to delete any form_*.png files in
|
||||
# the HTML output directory before the changes have effect.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
FORMULA_TRANSPARENT = YES
|
||||
|
||||
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
|
||||
# http://www.mathjax.org) which uses client side Javascript for the rendering
|
||||
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
|
||||
@@ -1580,166 +1569,6 @@ EXTRA_SEARCH_MAPPINGS =
|
||||
|
||||
GENERATE_LATEX = NO
|
||||
|
||||
# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
|
||||
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
|
||||
# it.
|
||||
# The default directory is: latex.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_OUTPUT = latex
|
||||
|
||||
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
|
||||
# invoked.
|
||||
#
|
||||
# Note that when enabling USE_PDFLATEX this option is only used for generating
|
||||
# bitmaps for formulas in the HTML output, but not in the Makefile that is
|
||||
# written to the output directory.
|
||||
# The default file is: latex.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_CMD_NAME = latex
|
||||
|
||||
# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
|
||||
# index for LaTeX.
|
||||
# The default file is: makeindex.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
MAKEINDEX_CMD_NAME = makeindex
|
||||
|
||||
# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
|
||||
# documents. This may be useful for small projects and may help to save some
|
||||
# trees in general.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
COMPACT_LATEX = NO
|
||||
|
||||
# The PAPER_TYPE tag can be used to set the paper type that is used by the
|
||||
# printer.
|
||||
# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
|
||||
# 14 inches) and executive (7.25 x 10.5 inches).
|
||||
# The default value is: a4.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
PAPER_TYPE = letter
|
||||
|
||||
# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
|
||||
# that should be included in the LaTeX output. The package can be specified just
|
||||
# by its name or with the correct syntax as to be used with the LaTeX
|
||||
# \usepackage command. To get the times font for instance you can specify :
|
||||
# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
|
||||
# To use the option intlimits with the amsmath package you can specify:
|
||||
# EXTRA_PACKAGES=[intlimits]{amsmath}
|
||||
# If left blank no extra packages will be included.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
EXTRA_PACKAGES =
|
||||
|
||||
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
|
||||
# generated LaTeX document. The header should contain everything until the first
|
||||
# chapter. If it is left blank doxygen will generate a standard header. See
|
||||
# section "Doxygen usage" for information on how to let doxygen write the
|
||||
# default header to a separate file.
|
||||
#
|
||||
# Note: Only use a user-defined header if you know what you are doing! The
|
||||
# following commands have a special meaning inside the header: $title,
|
||||
# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
|
||||
# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
|
||||
# string, for the replacement values of the other commands the user is referred
|
||||
# to HTML_HEADER.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_HEADER =
|
||||
|
||||
# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
|
||||
# generated LaTeX document. The footer should contain everything after the last
|
||||
# chapter. If it is left blank doxygen will generate a standard footer. See
|
||||
# LATEX_HEADER for more information on how to generate a default footer and what
|
||||
# special commands can be used inside the footer.
|
||||
#
|
||||
# Note: Only use a user-defined footer if you know what you are doing!
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_FOOTER =
|
||||
|
||||
# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
|
||||
# LaTeX style sheets that are included after the standard style sheets created
|
||||
# by doxygen. Using this option one can overrule certain style aspects. Doxygen
|
||||
# will copy the style sheet files to the output directory.
|
||||
# Note: The order of the extra style sheet files is of importance (e.g. the last
|
||||
# style sheet in the list overrules the setting of the previous ones in the
|
||||
# list).
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_EXTRA_STYLESHEET =
|
||||
|
||||
# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
|
||||
# other source files which should be copied to the LATEX_OUTPUT output
|
||||
# directory. Note that the files will be copied as-is; there are no commands or
|
||||
# markers available.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_EXTRA_FILES =
|
||||
|
||||
# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
|
||||
# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
|
||||
# contain links (just like the HTML output) instead of page references. This
|
||||
# makes the output suitable for online browsing using a PDF viewer.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
PDF_HYPERLINKS = YES
|
||||
|
||||
# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
|
||||
# the PDF file directly from the LaTeX files. Set this option to YES, to get a
|
||||
# higher quality PDF documentation.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
USE_PDFLATEX = YES
|
||||
|
||||
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
|
||||
# command to the generated LaTeX files. This will instruct LaTeX to keep running
|
||||
# if errors occur, instead of asking the user for help. This option is also used
|
||||
# when generating formulas in HTML.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_BATCHMODE = NO
|
||||
|
||||
# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
|
||||
# index chapters (such as File Index, Compound Index, etc.) in the output.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_HIDE_INDICES = NO
|
||||
|
||||
# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
|
||||
# code with syntax highlighting in the LaTeX output.
|
||||
#
|
||||
# Note that which sources are shown also depends on other settings such as
|
||||
# SOURCE_BROWSER.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_SOURCE_CODE = NO
|
||||
|
||||
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
|
||||
# bibliography, e.g. plainnat, or ieeetr. See
|
||||
# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
|
||||
# The default value is: plain.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_BIB_STYLE = plain
|
||||
|
||||
# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
|
||||
# page will contain the date and time when the page was generated. Setting this
|
||||
# to NO can help when comparing the output of multiple runs.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_TIMESTAMP = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the RTF output
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -1751,61 +1580,6 @@ LATEX_TIMESTAMP = NO
|
||||
|
||||
GENERATE_RTF = NO
|
||||
|
||||
# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
|
||||
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
|
||||
# it.
|
||||
# The default directory is: rtf.
|
||||
# This tag requires that the tag GENERATE_RTF is set to YES.
|
||||
|
||||
RTF_OUTPUT = rtf
|
||||
|
||||
# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
|
||||
# documents. This may be useful for small projects and may help to save some
|
||||
# trees in general.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_RTF is set to YES.
|
||||
|
||||
COMPACT_RTF = NO
|
||||
|
||||
# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
|
||||
# contain hyperlink fields. The RTF file will contain links (just like the HTML
|
||||
# output) instead of page references. This makes the output suitable for online
|
||||
# browsing using Word or some other Word compatible readers that support those
|
||||
# fields.
|
||||
#
|
||||
# Note: WordPad (write) and others do not support links.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_RTF is set to YES.
|
||||
|
||||
RTF_HYPERLINKS = NO
|
||||
|
||||
# Load stylesheet definitions from file. Syntax is similar to doxygen's config
|
||||
# file, i.e. a series of assignments. You only have to provide replacements,
|
||||
# missing definitions are set to their default value.
|
||||
#
|
||||
# See also section "Doxygen usage" for information on how to generate the
|
||||
# default style sheet that doxygen normally uses.
|
||||
# This tag requires that the tag GENERATE_RTF is set to YES.
|
||||
|
||||
RTF_STYLESHEET_FILE =
|
||||
|
||||
# Set optional variables used in the generation of an RTF document. Syntax is
|
||||
# similar to doxygen's config file. A template extensions file can be generated
|
||||
# using doxygen -e rtf extensionFile.
|
||||
# This tag requires that the tag GENERATE_RTF is set to YES.
|
||||
|
||||
RTF_EXTENSIONS_FILE =
|
||||
|
||||
# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
|
||||
# with syntax highlighting in the RTF output.
|
||||
#
|
||||
# Note that which sources are shown also depends on other settings such as
|
||||
# SOURCE_BROWSER.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_RTF is set to YES.
|
||||
|
||||
RTF_SOURCE_CODE = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the man page output
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -1816,40 +1590,6 @@ RTF_SOURCE_CODE = NO
|
||||
|
||||
GENERATE_MAN = NO
|
||||
|
||||
# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
|
||||
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
|
||||
# it. A directory man3 will be created inside the directory specified by
|
||||
# MAN_OUTPUT.
|
||||
# The default directory is: man.
|
||||
# This tag requires that the tag GENERATE_MAN is set to YES.
|
||||
|
||||
MAN_OUTPUT = man
|
||||
|
||||
# The MAN_EXTENSION tag determines the extension that is added to the generated
|
||||
# man pages. In case the manual section does not start with a number, the number
|
||||
# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
|
||||
# optional.
|
||||
# The default value is: .3.
|
||||
# This tag requires that the tag GENERATE_MAN is set to YES.
|
||||
|
||||
MAN_EXTENSION = .3
|
||||
|
||||
# The MAN_SUBDIR tag determines the name of the directory created within
|
||||
# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
|
||||
# MAN_EXTENSION with the initial . removed.
|
||||
# This tag requires that the tag GENERATE_MAN is set to YES.
|
||||
|
||||
MAN_SUBDIR =
|
||||
|
||||
# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
|
||||
# will generate one additional man file for each entity documented in the real
|
||||
# man page(s). These additional files only source the real man page, but without
|
||||
# them the man command would be unable to find the correct page.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_MAN is set to YES.
|
||||
|
||||
MAN_LINKS = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the XML output
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -1860,23 +1600,6 @@ MAN_LINKS = NO
|
||||
|
||||
GENERATE_XML = NO
|
||||
|
||||
# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
|
||||
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
|
||||
# it.
|
||||
# The default directory is: xml.
|
||||
# This tag requires that the tag GENERATE_XML is set to YES.
|
||||
|
||||
XML_OUTPUT = xml
|
||||
|
||||
# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
|
||||
# listings (including syntax highlighting and cross-referencing information) to
|
||||
# the XML output. Note that enabling this will significantly increase the size
|
||||
# of the XML output.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_XML is set to YES.
|
||||
|
||||
XML_PROGRAMLISTING = YES
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the DOCBOOK output
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -1887,23 +1610,6 @@ XML_PROGRAMLISTING = YES
|
||||
|
||||
GENERATE_DOCBOOK = NO
|
||||
|
||||
# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
|
||||
# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
|
||||
# front of it.
|
||||
# The default directory is: docbook.
|
||||
# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
|
||||
|
||||
DOCBOOK_OUTPUT = docbook
|
||||
|
||||
# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
|
||||
# program listings (including syntax highlighting and cross-referencing
|
||||
# information) to the DOCBOOK output. Note that enabling this will significantly
|
||||
# increase the size of the DOCBOOK output.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
|
||||
|
||||
DOCBOOK_PROGRAMLISTING = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options for the AutoGen Definitions output
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -1928,32 +1634,6 @@ GENERATE_AUTOGEN_DEF = NO
|
||||
|
||||
GENERATE_PERLMOD = NO
|
||||
|
||||
# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
|
||||
# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
|
||||
# output from the Perl module output.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_PERLMOD is set to YES.
|
||||
|
||||
PERLMOD_LATEX = NO
|
||||
|
||||
# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
|
||||
# formatted so it can be parsed by a human reader. This is useful if you want to
|
||||
# understand what is going on. On the other hand, if this tag is set to NO, the
|
||||
# size of the Perl module output will be much smaller and Perl will parse it
|
||||
# just the same.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_PERLMOD is set to YES.
|
||||
|
||||
PERLMOD_PRETTY = YES
|
||||
|
||||
# The names of the make variables in the generated doxyrules.make file are
|
||||
# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
|
||||
# so different doxyrules.make files included by the same Makefile don't
|
||||
# overwrite each other's variables.
|
||||
# This tag requires that the tag GENERATE_PERLMOD is set to YES.
|
||||
|
||||
PERLMOD_MAKEVAR_PREFIX =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the preprocessor
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -2082,15 +1762,6 @@ EXTERNAL_PAGES = YES
|
||||
# Configuration options related to the dot tool
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
|
||||
# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
|
||||
# NO turns the diagrams off. Note that this option also works with HAVE_DOT
|
||||
# disabled, but it is recommended to install and use dot, since it yields more
|
||||
# powerful graphs.
|
||||
# The default value is: YES.
|
||||
|
||||
CLASS_DIAGRAMS = YES
|
||||
|
||||
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
||||
# then run dia to produce the diagram and insert it in the documentation. The
|
||||
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
|
||||
@@ -2123,30 +1794,6 @@ HAVE_DOT = NO
|
||||
|
||||
DOT_NUM_THREADS = 0
|
||||
|
||||
# When you want a differently looking font in the dot files that doxygen
|
||||
# generates you can specify the font name using DOT_FONTNAME. You need to make
|
||||
# sure dot is able to find the font, which can be done by putting it in a
|
||||
# standard location or by setting the DOTFONTPATH environment variable or by
|
||||
# setting DOT_FONTPATH to the directory containing the font.
|
||||
# The default value is: Helvetica.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_FONTNAME = Helvetica
|
||||
|
||||
# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
|
||||
# dot graphs.
|
||||
# Minimum value: 4, maximum value: 24, default value: 10.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_FONTSIZE = 10
|
||||
|
||||
# By default doxygen will tell dot to use the default font as specified with
|
||||
# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
|
||||
# the path where dot can find it using this tag.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_FONTPATH =
|
||||
|
||||
# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
|
||||
# each documented class showing the direct and indirect inheritance relations.
|
||||
# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
|
||||
@@ -2347,18 +1994,6 @@ DOT_GRAPH_MAX_NODES = 50
|
||||
|
||||
MAX_DOT_GRAPH_DEPTH = 0
|
||||
|
||||
# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
|
||||
# background. This is disabled by default, because dot on Windows does not seem
|
||||
# to support this out of the box.
|
||||
#
|
||||
# Warning: Depending on the platform used, enabling this option may lead to
|
||||
# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
|
||||
# read).
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_TRANSPARENT = NO
|
||||
|
||||
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
|
||||
# files in one run (i.e. multiple -o and -T options on the command line). This
|
||||
# makes dot run faster, but since only newer versions of dot (>1.8.10) support
|
||||
|
||||
40
docs/abm.rst
Normal file
40
docs/abm.rst
Normal file
@@ -0,0 +1,40 @@
|
||||
About Agent-Based Models
|
||||
========================
|
||||
|
||||
Agent-based models (ABM) are a type of computational model used
|
||||
to simulate the behavior of autonomous agents within a system. These
|
||||
agents can be individuals, groups, organizations, or other entities
|
||||
that interact with one another and with their environment.
|
||||
|
||||
One of the key features of ABMs is that they focus on the micro-level
|
||||
interactions between individual agents, rather than aggregating
|
||||
data to study macro-level phenomena. This allows for the examination
|
||||
of complex behaviors that emerge from the interactions between
|
||||
agents, such as the spread of a disease or the formation of social
|
||||
networks.
|
||||
|
||||
ABMs are often used in fields such as economics, sociology, and
|
||||
biology to study the behavior of individuals and groups. They can
|
||||
also be used to simulate the effects of different policies or
|
||||
interventions on a system.
|
||||
|
||||
In order to create an ABM, the researcher must first define the
|
||||
agents and their characteristics, such as their behavior, beliefs,
|
||||
and goals. They must also define the rules of interaction between
|
||||
the agents and their environment. Once these parameters are set,
|
||||
the model can be run to simulate the behavior of the agents over
|
||||
time.
|
||||
|
||||
ABMs are a powerful tool for understanding complex systems, but
|
||||
they also have their limitations. Because they focus on micro-level
|
||||
interactions, they may not accurately capture macro-level phenomena.
|
||||
Additionally, they often require a significant amount of computational
|
||||
resources and can be difficult to validate.
|
||||
|
||||
Overall, agent-based models are a valuable tool for understanding
|
||||
the behavior of complex systems and the emergence of complex behaviors
|
||||
from the interactions between individuals. However, it is important
|
||||
to use them in conjunction with other methods to fully understand
|
||||
the system being studied.
|
||||
|
||||
.. toctree::
|
||||
@@ -1,12 +1,62 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
Below is the consolidated changelog for Kami.
|
||||
|
||||
- :feature:`0` Added baseline for continuous domains
|
||||
|
||||
- :release:`0.7.2 <2023.01.22>`
|
||||
- :bug:`0` Streamlined documentation builds
|
||||
|
||||
- :release:`0.7.1 <2023.01.22>`
|
||||
- :bug:`0` Corrected bug in documentation build
|
||||
|
||||
- :release:`0.7.0 <2023.01.22>`
|
||||
- :support:`0` Added a minimal example and tutorial
|
||||
- :support:`0` Added documentation for each example
|
||||
- :feature:`0` Readded step() to the Model interface
|
||||
- :feature:`0` Moved to exception-based error handling
|
||||
- :feature:`0` Added Bank Reserves model to demonstrate reporting
|
||||
- :feature:`0` Added data collecting and reporting modules
|
||||
- :feature:`0` Added some useful constants, for use as random seeds
|
||||
- :feature:`0` Switched from ranlux24 to mt19937 due to speed
|
||||
- :feature:`0` Added distance measures to grid coordinate objects
|
||||
|
||||
- :release:`0.6.0 <2022.08.19>`
|
||||
- :feature:`0` Added a to do list to the documentation
|
||||
- :feature:`0` Completed basic unit tests
|
||||
- :feature:`0` Removed step()/run() from Model interface
|
||||
- :feature:`0` Revised interfaces to the grids
|
||||
- :feature:`0` Updated all support packages to most current versions
|
||||
|
||||
- :release:`0.5.1 <2022.08.11>`
|
||||
- :support:`0` Completed initial unit tests
|
||||
- :support:`0` Added background info to READ ME
|
||||
|
||||
- :release:`0.5.0 <2022.08.08>`
|
||||
- :feature:`0` Added a barebones "starter" model to build from
|
||||
- :support:`0` Numerous documentation cleanups
|
||||
- :bug:`0` Numerous code cleanups to remove void returns and eliminate stored Model references
|
||||
- :feature:`0` Restructured the entire interface
|
||||
- :bug:`0` Numerous build process cleanups
|
||||
- :feature:`0` Added support semver versioning via neargye-semver
|
||||
- :bug:`0` Make library static by default
|
||||
- :bug:`0` Corrected the badge link in the README file
|
||||
|
||||
- :release:`0.4.2 <2021.09.25>`
|
||||
- :bug:`0` Fixed Changelog to move docs to support
|
||||
- :bug:`0` Fixed Changelog with current releases added
|
||||
|
||||
- :release:`0.4.1 <2021.09.25>`
|
||||
- :bug:`0` Fixed README file links
|
||||
|
||||
- :release:`0.4.0 <2021.09.25>`
|
||||
- :bug:`0` Cleaned up numerous issues found by CLion's linter
|
||||
- :feature:`0` Added a new overview to the documents
|
||||
- :feature:`0` Added basic installation instructions
|
||||
- :support:`0` Added a new overview to the documents
|
||||
- :support:`0` Added basic installation instructions
|
||||
- :bug:`0` Retagged previous versions using pure Semantic Versioning
|
||||
- :feature:`0` Documentation for `kami::RandomScheduler`
|
||||
- :feature:`0` Added a changelog!
|
||||
- :support:`0` Documentation for `kami::RandomScheduler`
|
||||
- :support:`0` Added a changelog!
|
||||
|
||||
- :release:`0.3.0 <2021.09.20>`
|
||||
- :feature:`0` Initial public release.
|
||||
|
||||
16
docs/conf.py
16
docs/conf.py
@@ -1,10 +1,11 @@
|
||||
import subprocess, os
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from documenteer.sphinxconfig.utils import form_ltd_edition_name
|
||||
|
||||
def configureDoxyfile(input_dir, output_dir):
|
||||
|
||||
with open('Doxyfile.in', 'r') as file :
|
||||
def configureDoxyfile(input_dir, output_dir):
|
||||
with open('Doxyfile.in', 'r') as file:
|
||||
filedata = file.read()
|
||||
|
||||
filedata = filedata.replace('@DOXYGEN_INPUT_DIR@', input_dir)
|
||||
@@ -26,7 +27,7 @@ if read_the_docs_build:
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'Kami'
|
||||
copyright = '2020-2021 The Johns Hopkins University Applied Physics Laboratory LLC'
|
||||
copyright = '2020-2023 The Johns Hopkins University Applied Physics Laboratory LLC'
|
||||
author = 'James P. Howard, II <james.howard@jhu.edu>'
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
@@ -48,6 +49,7 @@ extensions = [
|
||||
|
||||
# 'releases' (changelog) settings
|
||||
releases_github_path = "JHUAPL/kami"
|
||||
releases_unstable_prehistory = True
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
@@ -92,11 +94,11 @@ highlight_language = 'cpp'
|
||||
# The short X.Y version.
|
||||
github_ref = os.getenv("GITHUB_REF", "")
|
||||
if github_ref == "":
|
||||
git_ref = "master"
|
||||
git_ref = "main"
|
||||
else:
|
||||
match = re.match(r"refs/(heads|tags|pull)/(?P<ref>.+)", github_ref)
|
||||
if not match:
|
||||
git_ref = "master"
|
||||
git_ref = "main"
|
||||
else:
|
||||
git_ref = match.group("ref")
|
||||
|
||||
@@ -176,7 +178,7 @@ html_theme_options = {
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'kami', u'Kami: Agent-Based Modeling in Modern C++',
|
||||
[author], 3)
|
||||
[author], '3kami')
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
|
||||
56
docs/examples/bankreserves.rst
Normal file
56
docs/examples/bankreserves.rst
Normal file
@@ -0,0 +1,56 @@
|
||||
bankreserves
|
||||
============
|
||||
|
||||
This example provides a two-dimensional bank reserves model (BSM)
|
||||
as an example of a simple application of the reporter classes for
|
||||
monitoring the internal functioning of the model.
|
||||
|
||||
The BSM is a type of computational model that simulates the behavior
|
||||
of customers and their interactions with a bank. It is used to study
|
||||
the dynamics of the money supply and the management of reserves by
|
||||
the bank.
|
||||
|
||||
In a BSM, individuals are represented as autonomous agents that
|
||||
make decisions about saving, borrowing, and repaying loans based
|
||||
on their individual objectives and constraints. The bank is also
|
||||
represented as an agent that maintains accounts for each individual.
|
||||
The interactions between individuals and the bank are simulated
|
||||
over time, and the model can be used to study the effects of different
|
||||
reserve requirements policies on the creation of money, borrowing,
|
||||
lending, and savings.
|
||||
|
||||
One of the main advantages of a BSM is that it allows for the
|
||||
examination of the micro-level interactions between individuals and
|
||||
the bank, which can provide a more detailed understanding of the
|
||||
dynamics of the monetary system.
|
||||
|
||||
It is important to note that BSMs are a simplified representation
|
||||
of the real world and may not capture all the nuances of the monetary
|
||||
system being studied. It's also important to use this model in
|
||||
conjunction with other methods to fully understand the monetary
|
||||
system.
|
||||
|
||||
.. list-table::
|
||||
:widths: 30 70
|
||||
:header-rows: 1
|
||||
|
||||
* - Option
|
||||
- Description
|
||||
* - -c *agent_count*
|
||||
- Set the number of agents
|
||||
* - -f *output_file_name*
|
||||
- Set the JSON report file
|
||||
* - -l *log_level_option*
|
||||
- Set the logging level
|
||||
* - -n *max_steps*
|
||||
- Set the number of steps to run the model
|
||||
* - -s *initial_seed*
|
||||
- Set the initial seed
|
||||
* - -x *x_size*
|
||||
- Set the number of columns
|
||||
* - -y *y_size*
|
||||
- Set the number of rows
|
||||
* - -w *max_initial_wealth*
|
||||
- Set the maximum initial agent wealth
|
||||
|
||||
.. toctree::
|
||||
60
docs/examples/boltzmann1d.rst
Normal file
60
docs/examples/boltzmann1d.rst
Normal file
@@ -0,0 +1,60 @@
|
||||
boltzmann1d
|
||||
===========
|
||||
|
||||
This example provides a one-dimensional Boltzmann wealth model (BWM)
|
||||
as an example of a simple application of the one-dimensional gridded
|
||||
system.
|
||||
|
||||
The BWM is a type of agent-based model used to study the distribution
|
||||
of wealth among individuals or agents within a population. The model
|
||||
is named after the physicist Ludwig Boltzmann, who first proposed
|
||||
a similar model to study the distribution of energy among particles
|
||||
in a gas.
|
||||
|
||||
In a BWM, agents are assigned a certain amount of wealth, and the
|
||||
model simulates their interactions over time. These interactions
|
||||
can include buying and selling goods and services, lending and
|
||||
borrowing money, and inheriting wealth from other agents.
|
||||
|
||||
The key feature of the BWM is that it incorporates a "wealth-exchange
|
||||
mechanism" which determines the probability of agents making a
|
||||
wealth exchange with each other. This mechanism is often based on
|
||||
the difference in wealth between agents, with wealthier agents more
|
||||
likely to make exchanges with other wealthy agents.
|
||||
|
||||
The model can be run for a specified number of time steps, and the
|
||||
resulting wealth distribution can be analyzed to study the emergence
|
||||
of wealth inequality and the factors that contribute to it. The
|
||||
model can also be used to study the effects of different policies
|
||||
or interventions on the wealth distribution.
|
||||
|
||||
The BWM has been used to study a variety of different economic
|
||||
systems, including capitalist, socialist, and feudal systems.
|
||||
However, it is important to note that like other agent-based models,
|
||||
the BWM is a simplified representation of the real world and may
|
||||
not capture all the nuances of the economic system being studied.
|
||||
|
||||
Overall, the BWM is a useful tool for studying the distribution of
|
||||
wealth and the emergence of wealth inequality in a population. It
|
||||
can provide insight into the factors that contribute to wealth
|
||||
inequality and the effects of different policies on the distribution
|
||||
of wealth.
|
||||
|
||||
.. list-table::
|
||||
:widths: 30 70
|
||||
:header-rows: 1
|
||||
|
||||
* - Option
|
||||
- Description
|
||||
* - -c *agent_count*
|
||||
- Set the number of agents
|
||||
* - -l *log_level_option*
|
||||
- Set the logging level
|
||||
* - -n *max_steps*
|
||||
- Set the number of steps to run the model
|
||||
* - -s *initial_seed*
|
||||
- Set the initial seed
|
||||
* - -x *x_size*
|
||||
- Set the number of columns
|
||||
|
||||
.. toctree::
|
||||
43
docs/examples/boltzmann2d.rst
Normal file
43
docs/examples/boltzmann2d.rst
Normal file
@@ -0,0 +1,43 @@
|
||||
boltzmann2d
|
||||
===========
|
||||
|
||||
This example provides a two-dimensional Boltzmann wealth model (BWM)
|
||||
as an example of a simple application of the two-dimensional gridded
|
||||
system.
|
||||
|
||||
The BWM is a type of agent-based model used to study the distribution
|
||||
of wealth among individuals within a population. The model simulates
|
||||
agents' interactions over time, such as buying and selling goods,
|
||||
lending and borrowing money, and inheriting wealth. The model is
|
||||
based on a "wealth-exchange mechanism" which determines the probability
|
||||
of agents making a wealth exchange with each other, it is often
|
||||
based on the difference in wealth between agents. The model can be
|
||||
run for a specified number of time steps, and the resulting wealth
|
||||
distribution can be analyzed to study the emergence of wealth
|
||||
inequality and the factors that contribute to it.
|
||||
|
||||
For more information on BWMs, please see the boltzmann1d_
|
||||
example documentation.
|
||||
|
||||
.. list-table::
|
||||
:widths: 30 70
|
||||
:header-rows: 1
|
||||
|
||||
* - Option
|
||||
- Description
|
||||
* - -c *agent_count*
|
||||
- Set the number of agents
|
||||
* - -l *log_level_option*
|
||||
- Set the logging level
|
||||
* - -n *max_steps*
|
||||
- Set the number of steps to run the model
|
||||
* - -s *initial_seed*
|
||||
- Set the initial seed
|
||||
* - -x *x_size*
|
||||
- Set the number of columns
|
||||
* - -y *y_size*
|
||||
- Set the number of rows
|
||||
|
||||
.. _boltzmann1d: boltzmann1d.html
|
||||
|
||||
.. toctree::
|
||||
21
docs/examples/index.rst
Normal file
21
docs/examples/index.rst
Normal file
@@ -0,0 +1,21 @@
|
||||
Examples
|
||||
========
|
||||
|
||||
* bankreserves_
|
||||
* boltzmann1d_
|
||||
* boltzmann2d_
|
||||
* starter_
|
||||
|
||||
.. _bankreserves: bankreserves.html
|
||||
.. _boltzmann1d: boltzmann1d.html
|
||||
.. _boltzmann2d: boltzmann2d.html
|
||||
.. _starter: starter.html
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:maxdepth: 1
|
||||
|
||||
bankreserves
|
||||
boltzmann1d
|
||||
boltzmann2d
|
||||
starter
|
||||
24
docs/examples/starter.rst
Normal file
24
docs/examples/starter.rst
Normal file
@@ -0,0 +1,24 @@
|
||||
starter
|
||||
=======
|
||||
|
||||
This example provides a starter scaffold for beginning a new
|
||||
agent-based model (ABM). The agents and models perform no real
|
||||
functions in the starter and is likely to be the most minimum
|
||||
functioning model.
|
||||
|
||||
.. list-table::
|
||||
:widths: 30 70
|
||||
:header-rows: 1
|
||||
|
||||
* - Option
|
||||
- Description
|
||||
* - -c *agent_count*
|
||||
- Set the number of agents
|
||||
* - -l *log_level_option*
|
||||
- Set the logging level
|
||||
* - -n *max_steps*
|
||||
- Set the number of steps to run the model
|
||||
* - -s *initial_seed*
|
||||
- Set the initial seed
|
||||
|
||||
.. toctree::
|
||||
@@ -7,24 +7,65 @@ Introduction
|
||||
.. image:: https://github.com/JHUAPL/kami/actions/workflows/build-develop.yml/badge.svg?branch=develop
|
||||
:target: https://github.com/JHUAPL/kami/actions/workflows/build-develop.yml
|
||||
:alt: Build Status (develop)
|
||||
.. image:: https://readthedocs.org/projects/kami/badge/?version=latest
|
||||
:target: https://kami.readthedocs.io/en/latest/?badge=latest
|
||||
.. image:: https://readthedocs.org/projects/kami/badge/?version=main
|
||||
:target: https://kami.readthedocs.io/en/main/
|
||||
:alt: Documentation Status
|
||||
.. image:: https://img.shields.io/github/release/JHUAPL/kami.svg
|
||||
:target: https://github.com/JHUAPL/kami/releases
|
||||
:alt: Release Status
|
||||
.. image:: https://img.shields.io/github/license/JHUAPL/kami
|
||||
:alt: License Information
|
||||
.. image:: https://img.shields.io/badge/DOI-10.5281%2Fzenodo.6975259-success.svg
|
||||
:target: https://doi.org/10.5281/zenodo.6975259
|
||||
:alt: Package DOI
|
||||
|
||||
Kami is Agent-Based Modeling in Modern C++.
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
Agent-based models (ABMs) are models for simulating the actions of
|
||||
individual actors within a provided environment to understand the
|
||||
behavior of the agents, most individually and collectively. ABMs
|
||||
are particularly suited for addressing problems governed by nonlinear
|
||||
processes or where there is a wide variety of potential responses
|
||||
an individual agent may provide depending on the environment and
|
||||
behavior of other agents. Because of this, ABMs have become powerful
|
||||
tools in both simulation and modeling, especially in public health
|
||||
and ecology, where they are also known as individual-based models.
|
||||
ABMs also provide support in economic, business, robotics, and many
|
||||
other fields.
|
||||
|
||||
Design Objectives
|
||||
-----------------
|
||||
|
||||
Kami provides agent-based modeling modern C++. The objectives in
|
||||
writing Kami are that it be lightweight, memory-efficient, and fast.
|
||||
It should be possible to develop a simple working model in under
|
||||
one hour of C++ development time. Accordingly, the platform is
|
||||
modeled on the Mesa_ library in Python, which itself was inspired
|
||||
by the MASON_ library in Java.
|
||||
|
||||
Many ABM platforms are designed around interaction and real time
|
||||
observation of the agent dynamics. Kami does not provide a
|
||||
visualization interface. Instead, Kami is meant to be used for
|
||||
ABMs requiring many runs with different starting conditions.
|
||||
Accordingly, Kami is single-threaded and multiple cores should be
|
||||
taken advantage of through multiple parallel runs of the supervising
|
||||
model.
|
||||
|
||||
.. _MASON: https://cs.gmu.edu/~eclab/projects/mason/
|
||||
.. _Mesa: https://mesa.readthedocs.io
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:maxdepth: 2
|
||||
|
||||
overview
|
||||
installation
|
||||
abm
|
||||
tutorial
|
||||
api/library_root
|
||||
examples/index
|
||||
changelog
|
||||
todo
|
||||
license
|
||||
@@ -2,18 +2,23 @@
|
||||
|
||||
## Requirements
|
||||
|
||||
The core of Kami, `libkami`, has no requirements beyond a modern C++ compiler. However, both the examples provided and
|
||||
the unit tests provided rely on three additional C++ packages:
|
||||
The core of Kami, `libkami`, has no requirements beyond a modern C++ compiler and `neargye-semver/0.3.0`. However, both the examples provided and
|
||||
the unit tests provided rely on three additional C++ packages. The full list is:
|
||||
|
||||
* cli11/1.9.1
|
||||
* gtest/cci.20210126
|
||||
* neargye-semver/0.3.0"
|
||||
* spdlog/1.8.5
|
||||
* fmt/7.1.3
|
||||
|
||||
[`CLI11`](https://cliutils.github.io/CLI11/) provides a command line interface for each of the utilities that makeup the
|
||||
examples and test suite. [`spdlog`](https://github.com/gabime/spdlog)
|
||||
provides a uniform output interface. Coupled with a command line option to set the output level, `spdlog` allows the
|
||||
unit tests and example programs to provide variable output levels depending on the users needs.
|
||||
Finally, [`fmt`](https://fmt.dev/) is required by
|
||||
[`Google Test`](https://github.com/google/googletest) provides a
|
||||
unit testing framework. [`CLI11`](https://cliutils.github.io/CLI11/)
|
||||
provides a command line interface for each of the utilities that
|
||||
makeup the examples. [`spdlog`](https://github.com/gabime/spdlog)
|
||||
provides a uniform output interface. Coupled with a command line
|
||||
option to set the output level, `spdlog` allows the unit tests and
|
||||
example programs to provide variable output levels depending on the
|
||||
users needs. Finally, [`fmt`](https://fmt.dev/) is required by
|
||||
`spdlog` for simple and easy string formatting.
|
||||
|
||||
## Compiling
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
License
|
||||
=======
|
||||
|
||||
Copyright (c) 2020 The Johns Hopkins University Applied Physics
|
||||
Copyright (c) 2020-2023 The Johns Hopkins University Applied Physics
|
||||
Laboratory LLC
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
Overview
|
||||
========
|
||||
|
||||
Agent-based models (ABMs) are models for simulating the actions of
|
||||
individual actors within a provided environment to understand the
|
||||
behavior of the agents, most individually and collectively. ABMs
|
||||
are particularly suited for addressing problems governed by nonlinear
|
||||
processes or where there is a wide variety of potential responses
|
||||
an individual agent may provide depending on the environment and
|
||||
behavior of other agents. Because of this, ABMs have become powerful
|
||||
tools in both simulation and modeling, especially in public health
|
||||
and ecology, where they are also known as individual-based models.
|
||||
ABMs also provide support in economic, business, robotics, and many
|
||||
other fields.
|
||||
|
||||
Design Objectives
|
||||
-----------------
|
||||
|
||||
Kami provides agent-based modeling modern C++. The objectives in
|
||||
writing Kami are that it be lightweight, memory-efficient, and fast.
|
||||
It should be possible to develop a simple working model in under
|
||||
one hour of C++ development time. Accordingly, the platform is
|
||||
modeled on the Mesa_ library in Python, which itself was inspired
|
||||
by the MASON_ library in Java.
|
||||
|
||||
Many ABM platforms are designed around interaction and real time
|
||||
observation of the agent dynamics. Kami does not provide a
|
||||
visualization interface. Instead, Kami is meant to be used for
|
||||
ABMs requiring many runs with different starting conditions.
|
||||
Accordingly, Kami is single-threaded and multiple cores should be
|
||||
taken advantage of through multiple parallel runs of the supervising
|
||||
model.
|
||||
|
||||
.. _MASON: https://cs.gmu.edu/~eclab/projects/mason/
|
||||
.. _Mesa: https://mesa.readthedocs.io
|
||||
|
||||
.. toctree::
|
||||
@@ -1,8 +1,8 @@
|
||||
breathe
|
||||
sphinx
|
||||
exhale
|
||||
documenteer
|
||||
myst-parser
|
||||
sphinx_bootstrap_theme
|
||||
sphinx_rtd_theme
|
||||
releases
|
||||
breathe==4.34.0
|
||||
Sphinx==4.5.0
|
||||
exhale==0.3.6
|
||||
documenteer==0.7.0
|
||||
myst-parser==0.18.1
|
||||
sphinx-bootstrap-theme==0.8.1
|
||||
sphinx-rtd-theme==1.1.1
|
||||
releases==1.6.3
|
||||
|
||||
23
docs/todo.rst
Normal file
23
docs/todo.rst
Normal file
@@ -0,0 +1,23 @@
|
||||
To Do List
|
||||
==========
|
||||
|
||||
Must Dos
|
||||
--------
|
||||
The list below is a list of things considered necessary before
|
||||
a 1.0 release. This list is *not* static.
|
||||
|
||||
- Network domain
|
||||
- Hexgrid domain
|
||||
- Continuous grid domain
|
||||
|
||||
Wishlist
|
||||
--------
|
||||
The list below is a list of things considered nice to have at
|
||||
any point.
|
||||
|
||||
- Revise unit tests to take advantage of fixtures
|
||||
- Network Boltzmann model example
|
||||
- Additional examples as appropriate
|
||||
- Globe domain, on sphere, with latitude and longitude
|
||||
|
||||
.. toctree::
|
||||
@@ -1,55 +1,108 @@
|
||||
Tutorial
|
||||
========
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer
|
||||
varius est at dignissim sodales. Nullam mauris velit, imperdiet sit
|
||||
amet neque nec, fringilla consectetur odio. In non erat varius,
|
||||
fringilla felis ut, sodales orci. Aliquam in turpis ultricies enim
|
||||
accumsan commodo. Duis at dolor quis dolor tristique suscipit eget
|
||||
at magna. Integer non eros vitae ipsum pellentesque pharetra ac sed
|
||||
sapien. Duis justo diam, bibendum ut ullamcorper ac, viverra sit
|
||||
amet risus. Curabitur blandit nisl ac posuere fermentum. Nulla
|
||||
convallis purus id velit pellentesque tempus. Pellentesque euismod
|
||||
augue non diam eleifend fermentum. Vestibulum ante ipsum primis in
|
||||
faucibus orci luctus et ultrices posuere cubilia curae;
|
||||
Kami's interface is heavily influenced by Mesa_'s interface. However,
|
||||
by being written in C++, Kami runs substantially faster. This
|
||||
allows for faster runs and more runs within a fixed amount of time.
|
||||
The advantage here is that an agent-based model (ABM) built on the
|
||||
Kami platform is better suited for statistical and Monte Carlo
|
||||
approaches to modelling.
|
||||
|
||||
Nulla iaculis orci neque, a rhoncus mi vestibulum vitae. Nam ut
|
||||
gravida magna. Nam vel dignissim lacus, id accumsan orci. Nullam
|
||||
cursus, dui nec finibus sagittis, nisi purus feugiat tortor, a
|
||||
aliquet quam metus eget enim. Cras et quam vitae nisi auctor varius
|
||||
eget vel lacus. In nibh orci, tempus eu odio et, euismod sodales
|
||||
nulla. Fusce luctus sit amet orci non interdum. Ut cursus volutpat
|
||||
feugiat. Nulla vitae ultricies augue. Donec orci dolor, convallis
|
||||
non tincidunt sit amet, consectetur ut nibh. Cras efficitur dictum
|
||||
eros, faucibus pretium odio rutrum at.
|
||||
Model Form
|
||||
----------
|
||||
|
||||
Phasellus lobortis ex nec felis iaculis tincidunt. Sed consequat
|
||||
sagittis urna at lobortis. Cras velit lorem, iaculis non felis et,
|
||||
sodales tempus erat. Mauris in ultricies metus. Ut bibendum nisl
|
||||
vel lectus consequat, vel pharetra est ultrices. Aliquam non lobortis
|
||||
massa. Mauris euismod turpis mi, eu tempor lectus molestie in. Donec
|
||||
auctor ante sed eros scelerisque volutpat. Morbi semper diam vitae
|
||||
ante feugiat, eu hendrerit felis aliquet. Sed placerat velit sit
|
||||
amet odio suscipit, a posuere lectus hendrerit. Nulla felis augue,
|
||||
cursus a tempus vitae, ullamcorper a ante. Aenean et elit mi.
|
||||
Suspendisse potenti. Mauris ac enim libero. Donec finibus id enim
|
||||
ut ullamcorper. Suspendisse eu imperdiet tellus.
|
||||
Kami-based models have five key components:
|
||||
|
||||
Cras commodo vitae massa ac blandit. Donec ut mauris at lectus
|
||||
congue euismod in eleifend felis. Mauris id sapien orci. Cras ac
|
||||
enim et lectus fringilla vestibulum. Aliquam varius est mattis
|
||||
condimentum finibus. Nunc tristique justo nec nunc mollis, sit amet
|
||||
tempor neque iaculis. Class aptent taciti sociosqu ad litora torquent
|
||||
per conubia nostra, per inceptos himenaeos. Vestibulum ante ipsum
|
||||
primis in faucibus orci luctus et ultrices posuere cubilia curae;
|
||||
In commodo molestie porttitor. Duis blandit ligula a purus bibendum
|
||||
volutpat id in metus. Cras bibendum vel ex in accumsan. Phasellus
|
||||
congue ex eu scelerisque consectetur.
|
||||
1. Agents, which are objects representing the actors within the model
|
||||
2. Populations, which are collections of Agents
|
||||
3. Domains, which provide a representation of "physical" space the Agent inhabits
|
||||
4. Schedulers, which provide a representation of "time" within the model
|
||||
5. Model, which are objects connecting Populations, Domains, and Schedulers
|
||||
|
||||
Maecenas pellentesque eget quam ac pellentesque. Morbi id tempus
|
||||
urna. In accumsan molestie neque nec imperdiet. Nam ultricies lacinia
|
||||
magna. Nullam dictum, massa ac fermentum rhoncus, lacus eros
|
||||
pellentesque ante, sed sollicitudin eros est in dui. Interdum et
|
||||
malesuada fames ac ante ipsum primis in faucibus. Integer porttitor,
|
||||
ante id bibendum volutpat, mi nunc mollis eros, sed auctor turpis
|
||||
mi et sem.
|
||||
In general, a model should have one scheduler, one domain, and some
|
||||
number of agents. However, it would not be impossible to have more
|
||||
than one scheduler or more than one domain. Because this is
|
||||
implemented in C++, your agents should subclass Agent and your model
|
||||
should subclass model. The schedulers and domains are sufficient
|
||||
as is for their purposes though custom schedulers and domains are
|
||||
not unreasonable.
|
||||
|
||||
A Minimal ABM
|
||||
-------------
|
||||
|
||||
The minimal ABM starts with the simplest possible agent. Here, we
|
||||
create a class called ``MinimalAgent``:
|
||||
|
||||
.. code-block:: c++
|
||||
:linenos:
|
||||
|
||||
class MinimalAgent : public kami::Agent {
|
||||
public:
|
||||
kami::AgentID step(std::shared_ptr<kami::Model> model) override {
|
||||
return this->get_agent_id();
|
||||
}
|
||||
};
|
||||
|
||||
An ``Agent``, and its subclasses, will automatically inherit an ``AgentID``,
|
||||
which is the unique identifier for the session. The only explicit
|
||||
requirement on the ``Agent`` subclass is a `step()` method that accepts
|
||||
a ``shared_ptr`` to a ``Model`` and it must return the ``Agent``'s ``AgentID``.
|
||||
Obviously, an ``Agent`` should do something useful before returning.
|
||||
|
||||
The second component is ``MinimalModel:``
|
||||
|
||||
.. code-block:: c++
|
||||
:linenos:
|
||||
|
||||
class MinimalModel: public kami::Model {
|
||||
public:
|
||||
MinimalModel() {
|
||||
auto sched = std::make_shared<kami::SequentialScheduler>();
|
||||
set_scheduler(sched);
|
||||
|
||||
auto pop = std::make_shared<kami::Population>();
|
||||
set_population(pop);
|
||||
|
||||
for (auto i = 0; i < 10; i++) {
|
||||
auto new_agent = std::make_shared<MinimalAgent>();
|
||||
pop->add_agent(new_agent);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
The ``MinimalModel`` performs some important tasks that important to do
|
||||
during the setup or soon thereafter. In the constructor, first, a
|
||||
scheduler is created. The ``SequentialScheduler`` is the simplest
|
||||
scheduler and has no configuration needed. Using `set_scheduler()`,
|
||||
part of the Model class, the scheduler is associated with this
|
||||
model. Second, a `Population` is created and associated with this
|
||||
model with the `set_population()` method.
|
||||
|
||||
After this, the constructor initializes 10 ``MinimalAgents`` and adds
|
||||
them to the population.
|
||||
|
||||
.. code-block:: c++
|
||||
:linenos:
|
||||
|
||||
int main() {
|
||||
auto model = std::make_shared<MinimalModel>();
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
model->step();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
The last part is our `main()` function. It creates the `MinimalModel`
|
||||
then executes its `step()` method 10 times. The `step()` method, by
|
||||
default, calls the `step()` method of the scheduler. In the case of
|
||||
the ``SequentialScheduler``, it loops over all the ``Agent`` instances in the
|
||||
``Population`` and executes the associated `step()` method of each ``Agent``.
|
||||
|
||||
That is it. It is the simplest minimal model that can be created
|
||||
using the Kami platform. However, for a basis, it is likely better
|
||||
to use the starter model, included in the examples directory.
|
||||
|
||||
.. _Mesa: https://mesa.readthedocs.io
|
||||
|
||||
.. toctree::
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
Each folder in here is automatically traversed by the root level cmake list file.
|
||||
|
||||
1. Copy one of the existing folders to a new name.
|
||||
2. The name of the folder will be the name of the library
|
||||
2. The name of the folder will be the name of the example
|
||||
3. Change the source file list.
|
||||
|
||||
22
examples/bankreserves/CMakeLists.txt
Normal file
22
examples/bankreserves/CMakeLists.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
####
|
||||
# Set minimum version of CMake.
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
find_package(spdlog)
|
||||
|
||||
set(EXAMPLE_NAME "bankreserves")
|
||||
|
||||
project(${EXAMPLE_NAME} LANGUAGES CXX)
|
||||
|
||||
file(GLOB EXAMPLE_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cc")
|
||||
|
||||
create_executable(
|
||||
NAME ${EXAMPLE_NAME}
|
||||
SOURCES ${EXAMPLE_SOURCES}
|
||||
PUBLIC_DEFINITIONS USE_DOUBLE_PRECISION=1
|
||||
PRIVATE_DEFINITIONS DEBUG_VERBOSE
|
||||
PRIVATE_INCLUDE_PATHS ${CMAKE_SOURCE_DIR}/include
|
||||
PUBLIC_LINKED_TARGETS fmt spdlog::spdlog kami::libkami
|
||||
)
|
||||
|
||||
set_target_properties(${EXAMPLE_NAME} PROPERTIES VERSION ${VERSION_STRING})
|
||||
106
examples/bankreserves/bankreserves.cc
Normal file
106
examples/bankreserves/bankreserves.cc
Normal file
@@ -0,0 +1,106 @@
|
||||
/*-
|
||||
* Copyright (c) 2022 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "bankreserves.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
|
||||
#include <CLI/CLI.hpp>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <spdlog/stopwatch.h>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/multigrid2d.h>
|
||||
#include <kami/kami.h>
|
||||
#include <kami/model.h>
|
||||
#include <kami/population.h>
|
||||
#include <kami/random.h>
|
||||
#include <kami/reporter.h>
|
||||
|
||||
std::shared_ptr<spdlog::logger> console = nullptr;
|
||||
std::shared_ptr<std::mt19937> rng = nullptr;
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma ide diagnostic ignored "EmptyDeclOrStmt"
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
std::string ident = "bankreserves";
|
||||
std::string log_level_option = "info";
|
||||
std::string output_file_name = ident + ".json";
|
||||
CLI::App app{ident};
|
||||
unsigned int
|
||||
initial_seed = kami::Constants::JENNYS_NUMBER,
|
||||
max_steps = 100,
|
||||
x_size = 20,
|
||||
y_size = 20,
|
||||
agent_count = x_size * y_size,
|
||||
max_initial_wealth = 10;
|
||||
|
||||
// This exercise is really stupid.
|
||||
auto levels_list = std::make_unique<std::list<std::string>>();
|
||||
for (auto& level_name : SPDLOG_LEVEL_NAMES)
|
||||
levels_list->push_back(std::string(level_name.data(), level_name.size()));
|
||||
|
||||
app.add_option("-c", agent_count, "Set the number of agents")->check(CLI::PositiveNumber);
|
||||
app.add_option("-f", output_file_name, "Set the JSON report file")->check(CLI::ExistingPath);
|
||||
app.add_option("-l", log_level_option, "Set the logging level")->check(
|
||||
CLI::IsMember(levels_list.get(), CLI::ignore_case));
|
||||
app.add_option("-n", max_steps, "Set the number of steps to run the model")->check(CLI::PositiveNumber);
|
||||
app.add_option("-s", initial_seed, "Set the initial seed")->check(CLI::Number);
|
||||
app.add_option("-x", x_size, "Set the number of columns")->check(CLI::PositiveNumber);
|
||||
app.add_option("-y", y_size, "Set the number of rows")->check(CLI::PositiveNumber);
|
||||
app.add_option("-w", max_initial_wealth, "Set the maximum initial agent wealth")->check(CLI::PositiveNumber);
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
console = spdlog::stdout_color_st(ident);
|
||||
console->set_level(spdlog::level::from_str(log_level_option));
|
||||
console->info("Compiled with Kami/{}, log level {}", kami::version.to_string(), log_level_option);
|
||||
|
||||
console->info("Starting Bank Reserves Model with {} agents for {} steps", agent_count, max_steps);
|
||||
auto model = std::make_shared<BankReservesModel>(agent_count, x_size, y_size, initial_seed, max_initial_wealth);
|
||||
spdlog::stopwatch sw;
|
||||
for (int i = 0; i < max_steps; i++)
|
||||
model->step();
|
||||
console->info("Bank Reserves Model simulation complete, requiring {} seconds", sw);
|
||||
|
||||
auto rpt = model->report();
|
||||
std::ofstream output_file(output_file_name);
|
||||
output_file << *rpt;
|
||||
console->info("JSON data report written to {}", output_file_name);
|
||||
console->trace("Done.");
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
195
examples/bankreserves/bankreserves.h
Normal file
195
examples/bankreserves/bankreserves.h
Normal file
@@ -0,0 +1,195 @@
|
||||
/*-
|
||||
* Copyright (c) 2022 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef BANKRESERVES_H
|
||||
//! @cond SuppressGuard
|
||||
#define BANKRESERVES_H
|
||||
//! @endcond
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <spdlog/stopwatch.h>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/multigrid2d.h>
|
||||
#include <kami/kami.h>
|
||||
#include <kami/random.h>
|
||||
#include <kami/reporter.h>
|
||||
|
||||
extern std::shared_ptr<spdlog::logger> console;
|
||||
extern std::shared_ptr<std::mt19937> rng;
|
||||
|
||||
template<>
|
||||
struct fmt::formatter<kami::AgentID>
|
||||
: fmt::formatter<std::string> {
|
||||
static auto format(
|
||||
kami::AgentID agent_id,
|
||||
format_context& ctx
|
||||
) {
|
||||
return format_to(ctx.out(), "{}", agent_id.to_string());
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct fmt::formatter<kami::GridCoord2D>
|
||||
: fmt::formatter<std::string> {
|
||||
static auto format(
|
||||
const kami::GridCoord2D& coord,
|
||||
format_context& ctx
|
||||
) {
|
||||
return format_to(ctx.out(), "{}", coord.to_string());
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A starter agent for a starter model
|
||||
*/
|
||||
class BankAgent
|
||||
: public kami::ReporterAgent {
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
explicit BankAgent(int reserve_percent)
|
||||
:_reserve_percent(reserve_percent) {
|
||||
};
|
||||
|
||||
inline std::unique_ptr<nlohmann::json> collect() override {
|
||||
auto ret = std::make_unique<nlohmann::json>();
|
||||
|
||||
(*ret)["reserves"] = _reserves;
|
||||
(*ret)["available"] = _available_to_loan;
|
||||
|
||||
return std::move(ret);
|
||||
}
|
||||
|
||||
inline kami::AgentID step(std::shared_ptr<kami::ReporterModel> model) override {
|
||||
return get_agent_id();
|
||||
};
|
||||
|
||||
int bank_balance() {
|
||||
_reserves = (_reserve_percent / 100) * _deposits;
|
||||
return _available_to_loan = _deposits - (_reserves + _bank_loans);
|
||||
}
|
||||
|
||||
private:
|
||||
double _bank_loans = 0;
|
||||
double _reserve_percent = 0;
|
||||
double _deposits = 0;
|
||||
double _reserves = (_reserve_percent / 100) * _deposits;
|
||||
double _available_to_loan = 0;
|
||||
|
||||
friend class PersonAgent;
|
||||
};
|
||||
|
||||
class PersonAgent
|
||||
: public kami::ReporterAgent {
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
explicit PersonAgent(
|
||||
int wallet,
|
||||
std::shared_ptr<BankAgent>& bank
|
||||
)
|
||||
:
|
||||
_wallet(wallet), _bank(bank) {
|
||||
};
|
||||
|
||||
inline std::unique_ptr<nlohmann::json> collect() override {
|
||||
auto ret = std::make_unique<nlohmann::json>();
|
||||
|
||||
(*ret)["savings"] = _savings;
|
||||
(*ret)["loans"] = _loans;
|
||||
(*ret)["wallet"] = _wallet;
|
||||
(*ret)["wealth"] = _wealth;
|
||||
|
||||
return std::move(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a single time-step for the agent
|
||||
*/
|
||||
kami::AgentID step(std::shared_ptr<kami::ReporterModel> model) override;
|
||||
|
||||
private:
|
||||
int _step_counter = 0;
|
||||
double _savings = 0;
|
||||
double _loans = 0;
|
||||
double _wallet = 0;
|
||||
double _wealth = 0;
|
||||
std::shared_ptr<BankAgent> _bank;
|
||||
|
||||
/**
|
||||
* Move the agent to a random location on the world
|
||||
*/
|
||||
std::optional<kami::GridCoord2D> move_agent(std::shared_ptr<kami::ReporterModel>& model);
|
||||
|
||||
std::optional<kami::AgentID> do_business(std::shared_ptr<kami::ReporterModel>& model);
|
||||
|
||||
std::optional<int> balance_books(std::shared_ptr<kami::ReporterModel>& model);
|
||||
|
||||
kami::AgentID deposit_to_savings(double amount);
|
||||
|
||||
kami::AgentID withdraw_from_savings(double amount);
|
||||
|
||||
kami::AgentID repay_a_loan(double amount);
|
||||
|
||||
kami::AgentID take_out_loan(double amount);
|
||||
};
|
||||
|
||||
/**
|
||||
* The one-dimensional Boltzmann wealth model
|
||||
*/
|
||||
class BankReservesModel
|
||||
: public kami::ReporterModel {
|
||||
public:
|
||||
/**
|
||||
* Create an instance of the one-dimensional Boltzmann wealth model.
|
||||
*
|
||||
* @param[in] number_agents the number of agents to assign to the model.
|
||||
* @param[in] length_x the length of the one-dimensional world the agents
|
||||
* occupy.
|
||||
*/
|
||||
explicit BankReservesModel(
|
||||
unsigned int agent_count,
|
||||
unsigned int x_size,
|
||||
unsigned int y_size,
|
||||
unsigned int initial_seed,
|
||||
unsigned int max_initial_wealth
|
||||
);
|
||||
|
||||
inline std::unique_ptr<nlohmann::json> collect() override {
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // BANKRESERVES_H
|
||||
76
examples/bankreserves/brmodel.cc
Normal file
76
examples/bankreserves/brmodel.cc
Normal file
@@ -0,0 +1,76 @@
|
||||
/*-
|
||||
* Copyright (c) 2022 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "bankreserves.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <spdlog/stopwatch.h>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/multigrid2d.h>
|
||||
#include <kami/kami.h>
|
||||
#include <kami/random.h>
|
||||
#include <kami/reporter.h>
|
||||
|
||||
BankReservesModel::BankReservesModel(
|
||||
unsigned int agent_count,
|
||||
unsigned int x_size,
|
||||
unsigned int y_size,
|
||||
unsigned int initial_seed,
|
||||
unsigned int max_initial_wealth
|
||||
) {
|
||||
rng = std::make_shared<std::mt19937>();
|
||||
rng->seed(initial_seed);
|
||||
|
||||
auto domain = std::make_shared<kami::MultiGrid2D>(x_size, y_size, true, true);
|
||||
auto sched = std::make_shared<kami::RandomScheduler>(rng);
|
||||
auto pop = std::make_shared<kami::Population>();
|
||||
|
||||
std::static_pointer_cast<void>(set_scheduler(sched));
|
||||
std::static_pointer_cast<void>(set_domain(domain));
|
||||
std::static_pointer_cast<void>(set_population(pop));
|
||||
|
||||
console->debug("Scheduler initiated with seed {}", initial_seed);
|
||||
auto bank_agent = std::make_shared<BankAgent>(10);
|
||||
pop->add_agent(bank_agent);
|
||||
|
||||
_step_count = 0;
|
||||
|
||||
std::uniform_int_distribution<int> dist_x(0, (int) x_size - 1);
|
||||
std::uniform_int_distribution<int> dist_y(0, (int) y_size - 1);
|
||||
std::uniform_int_distribution<unsigned int> dist(1, max_initial_wealth);
|
||||
|
||||
for (auto i = 0; i < agent_count; i++) {
|
||||
auto wallet = dist(*rng);
|
||||
auto new_agent = std::make_shared<PersonAgent>(10, bank_agent);
|
||||
|
||||
pop->add_agent(new_agent);
|
||||
domain->add_agent(new_agent->get_agent_id(), kami::GridCoord2D(dist_x(*rng), dist_x(*rng)));
|
||||
console->trace("Initialized agent with AgentID {} with wallet {}", new_agent->get_agent_id(), wallet);
|
||||
}
|
||||
}
|
||||
183
examples/bankreserves/person.cc
Normal file
183
examples/bankreserves/person.cc
Normal file
@@ -0,0 +1,183 @@
|
||||
/*-
|
||||
* Copyright (c) 2022 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "bankreserves.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <spdlog/stopwatch.h>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/multigrid2d.h>
|
||||
#include <kami/kami.h>
|
||||
#include <kami/random.h>
|
||||
#include <kami/reporter.h>
|
||||
|
||||
kami::AgentID PersonAgent::step(std::shared_ptr<kami::ReporterModel> model) {
|
||||
console->trace("step() called for agent {}", get_agent_id());
|
||||
move_agent(model);
|
||||
do_business(model);
|
||||
balance_books(model);
|
||||
_bank->bank_balance();
|
||||
|
||||
return get_agent_id();
|
||||
}
|
||||
|
||||
std::optional<kami::GridCoord2D> PersonAgent::move_agent(std::shared_ptr<kami::ReporterModel>& model) {
|
||||
console->trace("move_agent() called for agent {}", get_agent_id());
|
||||
auto agent_id = get_agent_id();
|
||||
|
||||
auto domain = model->get_domain();
|
||||
auto world = std::static_pointer_cast<kami::MultiGrid2D>(domain);
|
||||
|
||||
auto move_list = world->get_neighborhood(agent_id, false, kami::GridNeighborhoodType::Moore);
|
||||
std::uniform_int_distribution<int> dist(0, (int) move_list->size() - 1);
|
||||
auto new_location = *std::next(move_list->begin(), dist(*rng));
|
||||
|
||||
console->trace("Moving Agent {} to location {}", agent_id, new_location);
|
||||
world->move_agent(agent_id, new_location);
|
||||
|
||||
return new_location;
|
||||
}
|
||||
|
||||
std::optional<kami::AgentID> PersonAgent::do_business(std::shared_ptr<kami::ReporterModel>& model) {
|
||||
console->trace("do_business() called for agent {}", get_agent_id());
|
||||
auto agent_id = get_agent_id();
|
||||
|
||||
if (!(_savings > 0 | _wallet > 0 | _bank->_available_to_loan > 0))
|
||||
return agent_id;
|
||||
|
||||
auto world = std::static_pointer_cast<kami::MultiGrid2D>(model->get_domain());
|
||||
auto population = model->get_population();
|
||||
|
||||
auto location = world->get_location_by_agent(agent_id);
|
||||
auto cell_mates_opt = world->get_location_contents(location);
|
||||
|
||||
if (!cell_mates_opt)
|
||||
return std::nullopt;
|
||||
|
||||
// Note, here we reverse the logic from that used in the Mesa
|
||||
// implementation. We prefer the guard clause to the nested
|
||||
// if statements. See Fowler.
|
||||
auto cell_mates = cell_mates_opt;
|
||||
if (cell_mates->size() < 2)
|
||||
return std::nullopt;
|
||||
|
||||
auto customer_id = agent_id;
|
||||
std::uniform_int_distribution<int> dist(0, (int) cell_mates->size() - 1);
|
||||
do { // There aren't enough do loops, ya know?
|
||||
customer_id = *std::next(cell_mates->begin(), dist(*rng));
|
||||
} while (customer_id == agent_id);
|
||||
|
||||
std::bernoulli_distribution coin_flip(0.5);
|
||||
if (coin_flip(*rng))
|
||||
return std::nullopt;
|
||||
|
||||
// If true, trade_amount = 5, if false, trade_amount = 2
|
||||
// Dropping the conditional should make this faster, but
|
||||
// really, this is just more elegant.
|
||||
auto trade_amount = (int) std::round(coin_flip(*rng)) * 3 + 2;
|
||||
|
||||
auto customer = std::static_pointer_cast<PersonAgent>(population->get_agent_by_id(customer_id));
|
||||
console->debug("Agent {} trading amount {} with agent {}", agent_id, trade_amount, customer_id);
|
||||
customer->_wallet += trade_amount;
|
||||
_wallet -= trade_amount;
|
||||
|
||||
return customer_id;
|
||||
}
|
||||
|
||||
std::optional<int> PersonAgent::balance_books(std::shared_ptr<kami::ReporterModel>& model) {
|
||||
console->debug(
|
||||
"balance_books() called for agent {} with wallet {}, savings {}, loans {}", get_agent_id(), _wallet,
|
||||
_savings, _loans);
|
||||
|
||||
if (_wallet < 0) {
|
||||
if (_savings >= -_wallet) {
|
||||
withdraw_from_savings(-_wallet);
|
||||
} else {
|
||||
if (_savings > 0) {
|
||||
withdraw_from_savings(_savings);
|
||||
}
|
||||
auto temp_loan = _bank->_available_to_loan;
|
||||
if (temp_loan >= -_wallet) {
|
||||
take_out_loan(-_wallet);
|
||||
} else {
|
||||
take_out_loan(temp_loan);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
deposit_to_savings(_wallet);
|
||||
}
|
||||
|
||||
if (_loans > 0 & _savings > 0) {
|
||||
if (_savings > _loans) {
|
||||
withdraw_from_savings(_loans);
|
||||
repay_a_loan(_loans);
|
||||
} else {
|
||||
withdraw_from_savings(_savings);
|
||||
repay_a_loan(_wallet);
|
||||
}
|
||||
}
|
||||
|
||||
_wealth = _savings - _loans;
|
||||
console->debug("balance_books() exiting for agent {}, and wealth {}", get_agent_id(), _wealth);
|
||||
return _wealth;
|
||||
}
|
||||
|
||||
kami::AgentID PersonAgent::deposit_to_savings(double amount) {
|
||||
console->debug("deposit_to_savings() called for agent {}, and amount {}", get_agent_id(), amount);
|
||||
_wallet -= amount;
|
||||
_savings += amount;
|
||||
_bank->_deposits += amount;
|
||||
return get_agent_id();
|
||||
}
|
||||
|
||||
kami::AgentID PersonAgent::withdraw_from_savings(double amount) {
|
||||
console->debug("withdraw_from_savings() called for agent {}, and amount {}", get_agent_id(), amount);
|
||||
_wallet += amount;
|
||||
_savings -= amount;
|
||||
_bank->_deposits -= amount;
|
||||
return get_agent_id();
|
||||
}
|
||||
|
||||
kami::AgentID PersonAgent::repay_a_loan(double amount) {
|
||||
console->debug("repay_a_loan() called for agent {}, and amount {}", get_agent_id(), amount);
|
||||
_loans -= amount;
|
||||
_wallet -= amount;
|
||||
_bank->_available_to_loan += amount;
|
||||
_bank->_bank_loans -= amount;
|
||||
return get_agent_id();
|
||||
}
|
||||
|
||||
kami::AgentID PersonAgent::take_out_loan(double amount) {
|
||||
console->debug("take_out_loan() called for agent {}, and amount {}", get_agent_id(), amount);
|
||||
_loans += amount;
|
||||
_wallet += amount;
|
||||
_bank->_available_to_loan -= amount;
|
||||
_bank->_bank_loans += amount;
|
||||
return get_agent_id();
|
||||
}
|
||||
@@ -2,24 +2,21 @@
|
||||
# Set minimum version of CMake.
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
find_package(spdlog)
|
||||
|
||||
set(EXAMPLE_NAME "boltzmann1d")
|
||||
|
||||
project(${EXAMPLE_NAME} LANGUAGES CXX)
|
||||
|
||||
create_executable( NAME ${EXAMPLE_NAME}
|
||||
SOURCES
|
||||
boltzmann1d.cc
|
||||
PUBLIC_DEFINITIONS
|
||||
USE_DOUBLE_PRECISION=1
|
||||
PRIVATE_DEFINITIONS
|
||||
DEBUG_VERBOSE
|
||||
PRIVATE_INCLUDE_PATHS
|
||||
${CMAKE_SOURCE_DIR}/include
|
||||
PUBLIC_LINKED_TARGETS
|
||||
kami::libkami
|
||||
fmt
|
||||
spdlog::spdlog
|
||||
)
|
||||
file(GLOB EXAMPLE_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cc")
|
||||
|
||||
set_target_properties(${EXAMPLE_NAME} PROPERTIES VERSION ${KAMI_VERSION_STRING})
|
||||
create_executable(
|
||||
NAME ${EXAMPLE_NAME}
|
||||
SOURCES ${EXAMPLE_SOURCES}
|
||||
PUBLIC_DEFINITIONS USE_DOUBLE_PRECISION=1
|
||||
PRIVATE_DEFINITIONS DEBUG_VERBOSE
|
||||
PRIVATE_INCLUDE_PATHS ${CMAKE_SOURCE_DIR}/include
|
||||
PUBLIC_LINKED_TARGETS fmt spdlog::spdlog kami::libkami
|
||||
)
|
||||
|
||||
set_target_properties(${EXAMPLE_NAME} PROPERTIES VERSION ${VERSION_STRING})
|
||||
|
||||
@@ -23,137 +23,182 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "boltzmann1d.h"
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/kami.h>
|
||||
#include <kami/multigrid1d.h>
|
||||
#include <CLI/CLI.hpp>
|
||||
|
||||
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <spdlog/stopwatch.h>
|
||||
|
||||
#include <CLI/App.hpp>
|
||||
#include <CLI/Config.hpp>
|
||||
#include <CLI/Formatter.hpp>
|
||||
#include <kami/agent.h>
|
||||
#include <kami/kami.h>
|
||||
#include <kami/multigrid1d.h>
|
||||
#include <kami/population.h>
|
||||
#include <kami/random.h>
|
||||
|
||||
#include "boltzmann1d.h"
|
||||
std::shared_ptr<spdlog::logger> console = nullptr;
|
||||
std::shared_ptr<std::mt19937> rng = nullptr;
|
||||
|
||||
using namespace kami;
|
||||
using namespace std;
|
||||
|
||||
MultiGrid1D *MoneyAgent1D::_world = nullptr;
|
||||
BoltzmannWealthModel1D *MoneyAgent1D::_model = nullptr;
|
||||
shared_ptr<spdlog::logger> console = nullptr;
|
||||
shared_ptr<mt19937> rng = nullptr;
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<AgentID> : fmt::formatter<string> {
|
||||
[[maybe_unused]] static auto format(AgentID agent_id, format_context &ctx) {
|
||||
template<>
|
||||
struct fmt::formatter<kami::AgentID>
|
||||
: fmt::formatter<std::string> {
|
||||
static auto format(
|
||||
kami::AgentID agent_id,
|
||||
format_context& ctx
|
||||
) {
|
||||
return format_to(ctx.out(), "{}", agent_id.to_string());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<GridCoord1D> : fmt::formatter<string> {
|
||||
[[maybe_unused]] static auto format(const GridCoord1D& coord, format_context &ctx) {
|
||||
template<>
|
||||
struct fmt::formatter<kami::GridCoord1D>
|
||||
: fmt::formatter<std::string> {
|
||||
static auto format(
|
||||
const kami::GridCoord1D& coord,
|
||||
format_context& ctx
|
||||
) {
|
||||
return format_to(ctx.out(), "{}", coord.to_string());
|
||||
}
|
||||
};
|
||||
|
||||
void MoneyAgent1D::step() {
|
||||
_step_counter++;
|
||||
MoneyAgent1D::~MoneyAgent1D() {
|
||||
console->trace("Deconstructing Agent {} with final wealth {}", get_agent_id(), _agent_wealth);
|
||||
}
|
||||
|
||||
kami::AgentID MoneyAgent1D::step(std::shared_ptr<kami::Model> model) {
|
||||
this->_step_counter++;
|
||||
|
||||
console->trace("Agent {} is moving", this->get_agent_id());
|
||||
move_agent();
|
||||
console->trace("Agent {} is giving money", this->get_agent_id());
|
||||
if (_agent_wealth > 0) give_money();
|
||||
this->move_agent(model);
|
||||
if (_agent_wealth > 0)
|
||||
this->give_money(model);
|
||||
|
||||
return this->get_agent_id();
|
||||
}
|
||||
|
||||
void MoneyAgent1D::set_world(MultiGrid1D *world) { _world = world; }
|
||||
|
||||
void MoneyAgent1D::set_model(BoltzmannWealthModel1D *model) { _model = model; }
|
||||
|
||||
void MoneyAgent1D::move_agent() {
|
||||
std::optional<kami::GridCoord1D> MoneyAgent1D::move_agent(std::shared_ptr<kami::Model> model) {
|
||||
console->trace("Entering move_agent");
|
||||
auto agent_id = get_agent_id();
|
||||
auto move_list = _world->get_neighborhood(agent_id, false);
|
||||
std::uniform_int_distribution<int> dist(0, (int)move_list.size() - 1);
|
||||
auto new_location = move_list[dist(*rng)];
|
||||
|
||||
auto domain = model->get_domain();
|
||||
if (!domain)
|
||||
throw (std::domain_error("model is missing domain"));
|
||||
auto world = std::static_pointer_cast<kami::MultiGrid1D>(domain);
|
||||
|
||||
auto move_list_opt = world->get_neighborhood(agent_id, false);
|
||||
if (!move_list_opt)
|
||||
return std::nullopt;
|
||||
auto move_list = move_list_opt;
|
||||
std::uniform_int_distribution<int> dist(0, (int) move_list->size() - 1);
|
||||
auto new_location = *std::next(move_list->begin(), dist(*rng));
|
||||
|
||||
console->trace("Moving Agent {} to location {}", agent_id, new_location);
|
||||
_world->move_agent(agent_id, new_location);
|
||||
world->move_agent(agent_id, new_location);
|
||||
console->trace("Exiting move_agent");
|
||||
|
||||
return new_location;
|
||||
}
|
||||
|
||||
void MoneyAgent1D::give_money() {
|
||||
AgentID agent_id = get_agent_id();
|
||||
GridCoord1D location = _world->get_location_by_agent(agent_id);
|
||||
vector<AgentID> *cell_mates = _world->get_location_contents(location);
|
||||
std::optional<kami::AgentID> MoneyAgent1D::give_money(std::shared_ptr<kami::Model> model) {
|
||||
console->trace("Entering give_money");
|
||||
auto agent_id = get_agent_id();
|
||||
|
||||
if (cell_mates->size() > 1) {
|
||||
std::uniform_int_distribution<int> dist(0, (int)cell_mates->size() - 1);
|
||||
AgentID other_agent_id = cell_mates->at(dist(*rng));
|
||||
auto other_agent = _model->get_agent_by_id(other_agent_id);
|
||||
auto domain = model->get_domain();
|
||||
if (!domain)
|
||||
throw (std::domain_error("model is missing domain"));
|
||||
auto world = std::static_pointer_cast<kami::MultiGrid1D>(domain);
|
||||
|
||||
console->trace("Agent {} giving unit of wealth to agent {}", agent_id, other_agent_id);
|
||||
other_agent->_agent_wealth += 1;
|
||||
_agent_wealth -= 1;
|
||||
}
|
||||
auto agents = model->get_population();
|
||||
if (!agents)
|
||||
throw (std::domain_error("model is missing population"));
|
||||
auto population = std::static_pointer_cast<kami::Population>(agents);
|
||||
|
||||
auto location = world->get_location_by_agent(agent_id);
|
||||
auto cell_mates_opt = world->get_location_contents(location);
|
||||
|
||||
if (!cell_mates_opt)
|
||||
return std::nullopt;
|
||||
|
||||
auto cell_mates = cell_mates_opt;
|
||||
if (cell_mates->size() < 2)
|
||||
return std::nullopt;
|
||||
|
||||
std::uniform_int_distribution<int> dist(0, (int) cell_mates->size() - 1);
|
||||
auto other_agent_id = *std::next(cell_mates->begin(), dist(*rng));
|
||||
auto other_agent = std::static_pointer_cast<MoneyAgent1D>(population->get_agent_by_id(other_agent_id));
|
||||
|
||||
console->trace("Agent {} giving unit of wealth to agent {}", agent_id, other_agent_id);
|
||||
other_agent->_agent_wealth += 1;
|
||||
_agent_wealth -= 1;
|
||||
|
||||
console->trace("Exiting move_agent");
|
||||
return other_agent_id;
|
||||
}
|
||||
|
||||
BoltzmannWealthModel1D::BoltzmannWealthModel1D(unsigned int number_agents, unsigned int length_x) {
|
||||
_world = new MultiGrid1D(length_x, true);
|
||||
_sched = new RandomScheduler(this, rng);
|
||||
BoltzmannWealthModel1D::BoltzmannWealthModel1D(
|
||||
unsigned int number_agents,
|
||||
unsigned int length_x,
|
||||
unsigned int new_seed
|
||||
) {
|
||||
rng = std::make_shared<std::mt19937>();
|
||||
rng->seed(new_seed);
|
||||
|
||||
auto domain = std::make_shared<kami::MultiGrid1D>(length_x, true);
|
||||
auto scheduler = std::make_shared<kami::RandomScheduler>(rng);
|
||||
auto population = std::make_shared<kami::Population>();
|
||||
|
||||
this->set_domain(domain);
|
||||
this->set_scheduler(scheduler);
|
||||
this->set_population(population);
|
||||
|
||||
console->debug("Scheduler initiated with seed {}", new_seed);
|
||||
|
||||
_step_count = 0;
|
||||
MoneyAgent1D::set_world(_world);
|
||||
MoneyAgent1D::set_model(this);
|
||||
|
||||
std::uniform_int_distribution<int> dist(0, (int)length_x - 1);
|
||||
std::uniform_int_distribution<int> dist(0, (int) length_x - 1);
|
||||
|
||||
for (unsigned int i = 0; i < number_agents; i++) {
|
||||
auto *new_agent = new MoneyAgent1D();
|
||||
auto new_agent = std::make_shared<MoneyAgent1D>();
|
||||
|
||||
_agent_list.insert(pair<AgentID, MoneyAgent1D *>(new_agent->get_agent_id(), new_agent));
|
||||
_sched->add_agent(new_agent->get_agent_id());
|
||||
_world->add_agent(new_agent->get_agent_id(), GridCoord1D(dist(*rng)));
|
||||
console->trace("Initializing agent with AgentID {}", new_agent->get_agent_id());
|
||||
population->add_agent(new_agent);
|
||||
domain->add_agent(new_agent->get_agent_id(), kami::GridCoord1D(dist(*rng)));
|
||||
}
|
||||
}
|
||||
|
||||
BoltzmannWealthModel1D::~BoltzmannWealthModel1D() {
|
||||
for (auto & agent_pair : _agent_list)
|
||||
delete agent_pair.second;
|
||||
|
||||
delete _sched;
|
||||
delete _world;
|
||||
std::shared_ptr<kami::Model> BoltzmannWealthModel1D::step() {
|
||||
console->trace("Executing model step {}", ++_step_count);
|
||||
_sched->step(shared_from_this());
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
void BoltzmannWealthModel1D::step() {
|
||||
_step_count++;
|
||||
_sched->step();
|
||||
}
|
||||
#pragma clang diagnostic push
|
||||
#pragma ide diagnostic ignored "EmptyDeclOrStmt"
|
||||
|
||||
void BoltzmannWealthModel1D::run(unsigned int steps) {
|
||||
for (auto i = 0; i < steps; i++) step();
|
||||
}
|
||||
|
||||
MoneyAgent1D *BoltzmannWealthModel1D::get_agent_by_id(AgentID _agent_id) const {
|
||||
MoneyAgent1D *_agent_pair = _agent_list.at(_agent_id);
|
||||
|
||||
return _agent_pair;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
string ident = "boltzmann1d";
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
std::string ident = "boltzmann1d";
|
||||
std::string log_level_option = "info";
|
||||
CLI::App app{ident};
|
||||
string log_level_option = "info";
|
||||
unsigned int x_size = 16, agent_count = x_size, max_steps = 100, initial_seed = 42;
|
||||
|
||||
// This exercise is really stupid.
|
||||
auto levels_list = std::make_unique<std::list<std::string>>();
|
||||
for (auto& level_name : SPDLOG_LEVEL_NAMES)
|
||||
levels_list->push_back(std::string(level_name.data(), level_name.size()));
|
||||
|
||||
app.add_option("-c", agent_count, "Set the number of agents")->check(CLI::PositiveNumber);
|
||||
app.add_option("-l", log_level_option, "Set the logging level")->check(CLI::IsMember(SPDLOG_LEVEL_NAMES));
|
||||
app.add_option("-l", log_level_option, "Set the logging level")->check(
|
||||
CLI::IsMember(levels_list.get(), CLI::ignore_case));
|
||||
app.add_option("-n", max_steps, "Set the number of steps to run the model")->check(CLI::PositiveNumber);
|
||||
app.add_option("-s", initial_seed, "Set the initial seed")->check(CLI::Number);
|
||||
app.add_option("-x", x_size, "Set the number of columns")->check(CLI::PositiveNumber);
|
||||
@@ -161,16 +206,18 @@ int main(int argc, char **argv) {
|
||||
|
||||
console = spdlog::stdout_color_st(ident);
|
||||
console->set_level(spdlog::level::from_str(log_level_option));
|
||||
console->info("Compiled with Kami/{}, log level {}", KAMI_VERSION_STRING, log_level_option);
|
||||
console->info("Starting Boltzmann Wealth Model with {} agents on a {}-unit grid for {} steps",agent_count, x_size, max_steps);
|
||||
|
||||
rng = make_shared<mt19937>(initial_seed);
|
||||
BoltzmannWealthModel1D model(agent_count, x_size);
|
||||
console->info("Compiled with Kami/{}, log level {}", kami::version.to_string(), log_level_option);
|
||||
console->info(
|
||||
"Starting Boltzmann Wealth Model with {} agents on a {}-unit grid for {} steps", agent_count, x_size,
|
||||
max_steps);
|
||||
|
||||
spdlog::stopwatch sw;
|
||||
for (int i = 0; i < max_steps; i++) {
|
||||
console->trace("Initiating model step {}", i);
|
||||
model.step();
|
||||
}
|
||||
|
||||
auto model = std::make_shared<BoltzmannWealthModel1D>(agent_count, x_size, initial_seed);
|
||||
for (int i = 0; i < max_steps; i++)
|
||||
model->step();
|
||||
|
||||
console->info("Boltzmann Wealth Model simulation complete, requiring {} seconds", sw);
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
@@ -25,65 +25,68 @@
|
||||
|
||||
#pragma once
|
||||
#ifndef BOLTZMANN1D_H
|
||||
//! @cond SuppressGuard
|
||||
#define BOLTZMANN1D_H
|
||||
//! @endcond
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/kami.h>
|
||||
#include <kami/multigrid1d.h>
|
||||
#include <kami/population.h>
|
||||
#include <kami/random.h>
|
||||
|
||||
using namespace kami;
|
||||
using namespace std;
|
||||
|
||||
/**
|
||||
* A sample agent for a one-dimensional Boltzmann wealth model
|
||||
*/
|
||||
class MoneyAgent1D : public Agent {
|
||||
class MoneyAgent1D
|
||||
: public kami::Agent {
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create the agent
|
||||
*/
|
||||
MoneyAgent1D() : _step_counter(0), _agent_wealth(1) {}
|
||||
MoneyAgent1D()
|
||||
:_step_counter(0), _agent_wealth(1) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Deconstruct the agent
|
||||
*/
|
||||
~MoneyAgent1D();
|
||||
|
||||
/**
|
||||
* Execute a single time-step for the agent
|
||||
*/
|
||||
void step() override;
|
||||
|
||||
/**
|
||||
* Give the agent a reference copy of the domain it is expected to work in
|
||||
*/
|
||||
static void set_world(MultiGrid1D *world);
|
||||
|
||||
/**
|
||||
* Give the agent a reference copy of the model it is expected to work in
|
||||
*/
|
||||
static void set_model(class BoltzmannWealthModel1D *model);
|
||||
kami::AgentID step(std::shared_ptr<kami::Model> model) override;
|
||||
|
||||
/**
|
||||
* Move the agent to a random location on the world
|
||||
*/
|
||||
void move_agent();
|
||||
std::optional<kami::GridCoord1D> move_agent(std::shared_ptr<kami::Model> model);
|
||||
|
||||
/**
|
||||
* Give money to a random agent
|
||||
*/
|
||||
void give_money();
|
||||
std::optional<kami::AgentID> give_money(std::shared_ptr<kami::Model> model);
|
||||
|
||||
private:
|
||||
static MultiGrid1D *_world;
|
||||
static BoltzmannWealthModel1D *_model;
|
||||
int _step_counter;
|
||||
int _agent_wealth;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* The one-dimensional Boltzmann wealth model
|
||||
*/
|
||||
class BoltzmannWealthModel1D : public Model {
|
||||
class BoltzmannWealthModel1D
|
||||
: public kami::Model {
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create an instance of the one-dimensional Boltzmann wealth model.
|
||||
@@ -92,36 +95,18 @@ public:
|
||||
* @param[in] length_x the length of the one-dimensional world the agents
|
||||
* occupy.
|
||||
*/
|
||||
explicit BoltzmannWealthModel1D(unsigned int number_agents = 10, unsigned int length_x = 10);
|
||||
|
||||
/**
|
||||
* Destroy the instance
|
||||
*/
|
||||
~BoltzmannWealthModel1D();
|
||||
explicit BoltzmannWealthModel1D(
|
||||
unsigned int number_agents = 10,
|
||||
unsigned int length_x = 10,
|
||||
unsigned int new_seed = 42
|
||||
);
|
||||
|
||||
/**
|
||||
* Execute a single time-step for the model.
|
||||
*/
|
||||
void step() override;
|
||||
|
||||
/**
|
||||
* Execute a number of time-steps for the model.
|
||||
*
|
||||
* @param[in] n the number of steps to execute.
|
||||
*/
|
||||
void run(unsigned int n) override;
|
||||
|
||||
/**
|
||||
* Get the MoneyAgent instance associated with the given `AgentID`
|
||||
*
|
||||
* @returns an pointer to the `MoneyAgent` that was requested.
|
||||
*/
|
||||
[[nodiscard]] MoneyAgent1D *get_agent_by_id(AgentID agent_id) const override;
|
||||
std::shared_ptr<kami::Model> step() final;
|
||||
|
||||
private:
|
||||
map<AgentID, MoneyAgent1D *> _agent_list;
|
||||
RandomScheduler *_sched;
|
||||
MultiGrid1D *_world;
|
||||
unsigned int _step_count;
|
||||
};
|
||||
|
||||
|
||||
@@ -2,24 +2,21 @@
|
||||
# Set minimum version of CMake.
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
find_package(spdlog)
|
||||
|
||||
set(EXAMPLE_NAME "boltzmann2d")
|
||||
|
||||
project(${EXAMPLE_NAME} LANGUAGES CXX)
|
||||
|
||||
create_executable( NAME ${EXAMPLE_NAME}
|
||||
SOURCES
|
||||
boltzmann2d.cc
|
||||
PUBLIC_DEFINITIONS
|
||||
USE_DOUBLE_PRECISION=1
|
||||
PRIVATE_DEFINITIONS
|
||||
DEBUG_VERBOSE
|
||||
PRIVATE_INCLUDE_PATHS
|
||||
${CMAKE_SOURCE_DIR}/include
|
||||
PUBLIC_LINKED_TARGETS
|
||||
kami::libkami
|
||||
fmt
|
||||
spdlog::spdlog
|
||||
)
|
||||
file(GLOB EXAMPLE_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cc")
|
||||
|
||||
set_target_properties(${EXAMPLE_NAME} PROPERTIES VERSION ${KAMI_VERSION_STRING})
|
||||
create_executable(
|
||||
NAME ${EXAMPLE_NAME}
|
||||
SOURCES ${EXAMPLE_SOURCES}
|
||||
PUBLIC_DEFINITIONS USE_DOUBLE_PRECISION=1
|
||||
PRIVATE_DEFINITIONS DEBUG_VERBOSE
|
||||
PRIVATE_INCLUDE_PATHS ${CMAKE_SOURCE_DIR}/include
|
||||
PUBLIC_LINKED_TARGETS fmt spdlog::spdlog kami::libkami
|
||||
)
|
||||
|
||||
set_target_properties(${EXAMPLE_NAME} PROPERTIES VERSION ${VERSION_STRING})
|
||||
|
||||
@@ -23,142 +23,184 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "boltzmann2d.h"
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/kami.h>
|
||||
#include <kami/multigrid2d.h>
|
||||
#include <CLI/CLI.hpp>
|
||||
|
||||
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <spdlog/stopwatch.h>
|
||||
|
||||
#include <CLI/App.hpp>
|
||||
#include <CLI/Config.hpp>
|
||||
#include <CLI/Formatter.hpp>
|
||||
#include <kami/agent.h>
|
||||
#include <kami/kami.h>
|
||||
#include <kami/multigrid2d.h>
|
||||
#include <kami/population.h>
|
||||
#include <kami/random.h>
|
||||
|
||||
#include "boltzmann2d.h"
|
||||
std::shared_ptr<spdlog::logger> console = nullptr;
|
||||
std::shared_ptr<std::mt19937> rng = nullptr;
|
||||
|
||||
using namespace kami;
|
||||
using namespace std;
|
||||
|
||||
MultiGrid2D *MoneyAgent2D::_world = nullptr;
|
||||
BoltzmannWealthModel2D *MoneyAgent2D::_model = nullptr;
|
||||
shared_ptr<spdlog::logger> console = nullptr;
|
||||
shared_ptr<mt19937> rng = nullptr;
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<AgentID> : fmt::formatter<string> {
|
||||
[[maybe_unused]] static auto format(AgentID agent_id, format_context &ctx) {
|
||||
template<>
|
||||
struct fmt::formatter<kami::AgentID>
|
||||
: fmt::formatter<std::string> {
|
||||
static auto format(
|
||||
kami::AgentID agent_id,
|
||||
format_context& ctx
|
||||
) {
|
||||
return format_to(ctx.out(), "{}", agent_id.to_string());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<GridCoord2D> : fmt::formatter<string> {
|
||||
[[maybe_unused]] static auto format(const GridCoord2D& coord, format_context &ctx) {
|
||||
template<>
|
||||
struct fmt::formatter<kami::GridCoord2D>
|
||||
: fmt::formatter<std::string> {
|
||||
static auto format(
|
||||
const kami::GridCoord2D& coord,
|
||||
format_context& ctx
|
||||
) {
|
||||
return format_to(ctx.out(), "{}", coord.to_string());
|
||||
}
|
||||
};
|
||||
|
||||
void MoneyAgent2D::step() {
|
||||
_step_counter++;
|
||||
MoneyAgent2D::~MoneyAgent2D() {
|
||||
console->trace("Deconstructing Agent {} with final wealth {}", get_agent_id(), _agent_wealth);
|
||||
}
|
||||
|
||||
kami::AgentID MoneyAgent2D::step(std::shared_ptr<kami::Model> model) {
|
||||
this->_step_counter++;
|
||||
|
||||
console->trace("Agent {} is moving", this->get_agent_id());
|
||||
move_agent();
|
||||
console->trace("Agent {} is giving money", this->get_agent_id());
|
||||
if (_agent_wealth > 0) give_money();
|
||||
this->move_agent(model);
|
||||
if (_agent_wealth > 0)
|
||||
this->give_money(model);
|
||||
|
||||
return this->get_agent_id();
|
||||
}
|
||||
|
||||
void MoneyAgent2D::set_world(MultiGrid2D *world) { _world = world; }
|
||||
|
||||
void MoneyAgent2D::set_model(BoltzmannWealthModel2D *model) { _model = model; }
|
||||
|
||||
void MoneyAgent2D::move_agent() {
|
||||
std::optional<kami::GridCoord2D> MoneyAgent2D::move_agent(const std::shared_ptr<kami::Model>& model) {
|
||||
console->trace("Entering move_agent");
|
||||
auto agent_id = get_agent_id();
|
||||
auto move_list = _world->get_neighborhood(agent_id, GridNeighborhoodType::Moore, false);
|
||||
std::uniform_int_distribution<int> dist(0, (int) move_list.size() - 1);
|
||||
auto new_location = move_list[dist(*rng)];
|
||||
|
||||
auto domain = model->get_domain();
|
||||
if (!domain)
|
||||
throw (std::domain_error("model is missing domain"));
|
||||
auto world = std::static_pointer_cast<kami::MultiGrid2D>(domain);
|
||||
|
||||
auto move_list_opt = world->get_neighborhood(agent_id, false, kami::GridNeighborhoodType::VonNeumann);
|
||||
if (!move_list_opt)
|
||||
return std::nullopt;
|
||||
auto move_list = move_list_opt;
|
||||
std::uniform_int_distribution<int> dist(0, (int) move_list->size() - 1);
|
||||
auto new_location = *std::next(move_list->begin(), dist(*rng));
|
||||
|
||||
console->trace("Moving Agent {} to location {}", agent_id, new_location);
|
||||
_world->move_agent(agent_id, new_location);
|
||||
world->move_agent(agent_id, new_location);
|
||||
console->trace("Exiting move_agent");
|
||||
|
||||
return new_location;
|
||||
}
|
||||
|
||||
void MoneyAgent2D::give_money() {
|
||||
AgentID agent_id = get_agent_id();
|
||||
GridCoord2D location = _world->get_location_by_agent(agent_id);
|
||||
vector<AgentID> *cell_mates = _world->get_location_contents(location);
|
||||
std::optional<kami::AgentID> MoneyAgent2D::give_money(const std::shared_ptr<kami::Model>& model) {
|
||||
console->trace("Entering give_money");
|
||||
auto agent_id = get_agent_id();
|
||||
|
||||
if (cell_mates->size() > 1) {
|
||||
std::uniform_int_distribution<int> dist(0, (int)cell_mates->size() - 1);
|
||||
AgentID other_agent_id = cell_mates->at(dist(*rng));
|
||||
auto other_agent = _model->get_agent_by_id(other_agent_id);
|
||||
auto domain = model->get_domain();
|
||||
if (!domain)
|
||||
throw (std::domain_error("model is missing domain"));
|
||||
auto world = std::static_pointer_cast<kami::MultiGrid2D>(domain);
|
||||
|
||||
console->trace("Agent {} giving unit of wealth to agent {}", agent_id, other_agent_id);
|
||||
other_agent->_agent_wealth += 1;
|
||||
_agent_wealth -= 1;
|
||||
}
|
||||
auto agents = model->get_population();
|
||||
if (!agents)
|
||||
throw (std::domain_error("model is missing population"));
|
||||
auto population = std::static_pointer_cast<kami::Population>(agents);
|
||||
|
||||
auto location = world->get_location_by_agent(agent_id);
|
||||
auto cell_mates_opt = world->get_location_contents(location);
|
||||
|
||||
if (!cell_mates_opt)
|
||||
return std::nullopt;
|
||||
|
||||
auto cell_mates = cell_mates_opt;
|
||||
if (cell_mates->size() < 2)
|
||||
return std::nullopt;
|
||||
|
||||
std::uniform_int_distribution<int> dist(0, (int) cell_mates->size() - 1);
|
||||
auto other_agent_id = *std::next(cell_mates->begin(), dist(*rng));
|
||||
auto other_agent = std::static_pointer_cast<MoneyAgent2D>(population->get_agent_by_id(other_agent_id));
|
||||
|
||||
console->trace("Agent {} giving unit of wealth to agent {}", agent_id, other_agent_id);
|
||||
other_agent->_agent_wealth += 1;
|
||||
_agent_wealth -= 1;
|
||||
|
||||
console->trace("Exiting move_agent");
|
||||
return other_agent_id;
|
||||
}
|
||||
|
||||
BoltzmannWealthModel2D::BoltzmannWealthModel2D(unsigned int number_agents, unsigned int length_x, unsigned int length_y, unsigned int new_seed) {
|
||||
rng = make_shared<mt19937>();
|
||||
BoltzmannWealthModel2D::BoltzmannWealthModel2D(
|
||||
unsigned int number_agents,
|
||||
unsigned int length_x,
|
||||
unsigned int length_y,
|
||||
unsigned int new_seed
|
||||
) {
|
||||
rng = std::make_shared<std::mt19937>();
|
||||
rng->seed(new_seed);
|
||||
|
||||
_world = new MultiGrid2D(length_x, length_y, true, true);
|
||||
_sched = new RandomScheduler(this, rng);
|
||||
auto domain = std::make_shared<kami::MultiGrid2D>(length_x, length_y, true, true);
|
||||
auto scheduler = std::make_shared<kami::RandomScheduler>(rng);
|
||||
auto population = std::make_shared<kami::Population>();
|
||||
|
||||
this->set_domain(domain);
|
||||
this->set_scheduler(scheduler);
|
||||
this->set_population(population);
|
||||
|
||||
console->debug("Scheduler initiated with seed {}", new_seed);
|
||||
|
||||
_step_count = 0;
|
||||
MoneyAgent2D::set_world(_world);
|
||||
MoneyAgent2D::set_model(this);
|
||||
|
||||
std::uniform_int_distribution<int> dist_x(0, (int)length_x - 1);
|
||||
std::uniform_int_distribution<int> dist_y(0, (int)length_y - 1);
|
||||
std::uniform_int_distribution<int> dist_x(0, (int) length_x - 1);
|
||||
std::uniform_int_distribution<int> dist_y(0, (int) length_y - 1);
|
||||
|
||||
for (unsigned int i = 0; i < number_agents; i++) {
|
||||
auto *new_agent = new MoneyAgent2D();
|
||||
auto new_agent = std::make_shared<MoneyAgent2D>();
|
||||
|
||||
_agent_list.insert(pair<AgentID, MoneyAgent2D *>(new_agent->get_agent_id(), new_agent));
|
||||
_sched->add_agent(new_agent->get_agent_id());
|
||||
_world->add_agent(new_agent->get_agent_id(), GridCoord2D(dist_x(*rng), dist_x(*rng)));
|
||||
console->trace("Initializing agent with AgentID {}", new_agent->get_agent_id());
|
||||
population->add_agent(new_agent);
|
||||
domain->add_agent(new_agent->get_agent_id(), kami::GridCoord2D(dist_x(*rng), dist_x(*rng)));
|
||||
}
|
||||
}
|
||||
|
||||
BoltzmannWealthModel2D::~BoltzmannWealthModel2D() {
|
||||
for (auto & agent_pair : _agent_list)
|
||||
delete agent_pair.second;
|
||||
|
||||
delete _sched;
|
||||
delete _world;
|
||||
std::shared_ptr<kami::Model> BoltzmannWealthModel2D::step() {
|
||||
console->trace("Executing model step {}", _step_count++);
|
||||
_sched->step(shared_from_this());
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
void BoltzmannWealthModel2D::step() {
|
||||
_step_count++;
|
||||
_sched->step();
|
||||
}
|
||||
#pragma clang diagnostic push
|
||||
#pragma ide diagnostic ignored "EmptyDeclOrStmt"
|
||||
|
||||
void BoltzmannWealthModel2D::run(unsigned int steps) {
|
||||
for (auto i = 0; i < steps; i++) step();
|
||||
}
|
||||
|
||||
MoneyAgent2D *BoltzmannWealthModel2D::get_agent_by_id(AgentID agent_id) const {
|
||||
MoneyAgent2D *agent = _agent_list.at(agent_id);
|
||||
return agent;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
std::string ident = "boltzmann2d";
|
||||
std::string log_level_option = "info";
|
||||
CLI::App app{ident};
|
||||
string log_level_option = "info";
|
||||
unsigned int x_size = 16, y_size = 16, agent_count = x_size * y_size, max_steps = 100, initial_seed = 42;
|
||||
|
||||
// This exercise is really stupid.
|
||||
auto levels_list = std::make_unique<std::list<std::string>>();
|
||||
for (auto& level_name : SPDLOG_LEVEL_NAMES)
|
||||
levels_list->push_back(std::string(level_name.data(), level_name.size()));
|
||||
|
||||
app.add_option("-c", agent_count, "Set the number of agents")->check(CLI::PositiveNumber);
|
||||
app.add_option("-l", log_level_option, "Set the logging level")->check(CLI::IsMember(SPDLOG_LEVEL_NAMES));
|
||||
app.add_option("-l", log_level_option, "Set the logging level")->check(
|
||||
CLI::IsMember(levels_list.get(), CLI::ignore_case));
|
||||
app.add_option("-n", max_steps, "Set the number of steps to run the model")->check(CLI::PositiveNumber);
|
||||
app.add_option("-s", initial_seed, "Set the initial seed")->check(CLI::Number);
|
||||
app.add_option("-x", x_size, "Set the number of columns")->check(CLI::PositiveNumber);
|
||||
@@ -167,15 +209,18 @@ int main(int argc, char **argv) {
|
||||
|
||||
console = spdlog::stdout_color_st(ident);
|
||||
console->set_level(spdlog::level::from_str(log_level_option));
|
||||
console->info("Compiled with Kami/{}, log level {}", KAMI_VERSION_STRING, log_level_option);
|
||||
console->info("Starting Boltzmann Wealth Model with {} agents on a {}x{}-unit grid for {} steps", agent_count, x_size, y_size, max_steps);
|
||||
|
||||
BoltzmannWealthModel2D model(agent_count, x_size, y_size, initial_seed);
|
||||
console->info("Compiled with Kami/{}, log level {}", kami::version.to_string(), log_level_option);
|
||||
console->info(
|
||||
"Starting Boltzmann Wealth Model with {} agents on a {}x{}-unit grid for {} steps", agent_count,
|
||||
x_size, y_size, max_steps);
|
||||
|
||||
spdlog::stopwatch sw;
|
||||
for (int i = 0; i < max_steps; i++) {
|
||||
console->trace("Initiating model step {}", i);
|
||||
model.step();
|
||||
}
|
||||
|
||||
auto model = std::make_shared<BoltzmannWealthModel2D>(agent_count, x_size, y_size, initial_seed);
|
||||
for (int i = 0; i < max_steps; i++)
|
||||
model->step();
|
||||
|
||||
console->info("Boltzmann Wealth Model simulation complete, requiring {} seconds", sw);
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
@@ -25,57 +25,56 @@
|
||||
|
||||
#pragma once
|
||||
#ifndef BOLTZMANN2D_H
|
||||
//! @cond SuppressGuard
|
||||
#define BOLTZMANN2D_H
|
||||
//! @endcond
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/kami.h>
|
||||
#include <kami/multigrid2d.h>
|
||||
#include <kami/population.h>
|
||||
#include <kami/random.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
using namespace kami;
|
||||
using namespace std;
|
||||
|
||||
/**
|
||||
* A sample agent for a two-dimensional Boltzmann wealth model
|
||||
*/
|
||||
class MoneyAgent2D : public Agent {
|
||||
class MoneyAgent2D
|
||||
: public kami::Agent {
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create the agent
|
||||
*/
|
||||
MoneyAgent2D() : _step_counter(0), _agent_wealth(1) {}
|
||||
MoneyAgent2D()
|
||||
:_step_counter(0), _agent_wealth(1) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Deconstruct the agent
|
||||
*/
|
||||
~MoneyAgent2D();
|
||||
|
||||
/**
|
||||
* Execute a single time-step for the agent
|
||||
*/
|
||||
void step() override;
|
||||
|
||||
/**
|
||||
* Give the agent a reference copy of the domain it is expected to work in
|
||||
*/
|
||||
static void set_world(MultiGrid2D *world);
|
||||
|
||||
/**
|
||||
* Give the agent a reference copy of the model it is expected to work in
|
||||
*/
|
||||
static void set_model(class BoltzmannWealthModel2D *model);
|
||||
kami::AgentID step(std::shared_ptr<kami::Model> model) override;
|
||||
|
||||
/**
|
||||
* Move the agent to a random location on the world
|
||||
*/
|
||||
void move_agent();
|
||||
std::optional<kami::GridCoord2D> move_agent(const std::shared_ptr<kami::Model>& model);
|
||||
|
||||
/**
|
||||
* Give money to a random agent
|
||||
*/
|
||||
void give_money();
|
||||
std::optional<kami::AgentID> give_money(const std::shared_ptr<kami::Model>& model);
|
||||
|
||||
private:
|
||||
static MultiGrid2D *_world;
|
||||
static BoltzmannWealthModel2D *_model;
|
||||
int _step_counter;
|
||||
int _agent_wealth;
|
||||
};
|
||||
@@ -83,7 +82,9 @@ private:
|
||||
/**
|
||||
* The two-dimensional Boltzmann wealth model
|
||||
*/
|
||||
class BoltzmannWealthModel2D : public Model {
|
||||
class BoltzmannWealthModel2D
|
||||
: public kami::Model {
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create an instance of the two-dimensional Boltzmann wealth model.
|
||||
@@ -96,36 +97,19 @@ public:
|
||||
* @param[in] new_seed the initial seed used for the random number
|
||||
* generator.
|
||||
*/
|
||||
explicit BoltzmannWealthModel2D(unsigned int number_agents = 10, unsigned int length_x = 10, unsigned int length_y = 10, unsigned int new_seed = 42);
|
||||
|
||||
/**
|
||||
* Destroy the instance
|
||||
*/
|
||||
~BoltzmannWealthModel2D();
|
||||
explicit BoltzmannWealthModel2D(
|
||||
unsigned int number_agents = 10,
|
||||
unsigned int length_x = 10,
|
||||
unsigned int length_y = 10,
|
||||
unsigned int new_seed = 42
|
||||
);
|
||||
|
||||
/**
|
||||
* Execute a single time-step for the model.
|
||||
*/
|
||||
void step() override;
|
||||
|
||||
/**
|
||||
* Execute a number of time-steps for the model.
|
||||
*
|
||||
* @param[in] n the number of steps to execute.
|
||||
*/
|
||||
void run(unsigned int n) override;
|
||||
|
||||
/**
|
||||
* Get the MoneyAgent2D instance associated with the given `AgentID`
|
||||
*
|
||||
* @returns an pointer to the `MoneyAgent2D` that was requested.
|
||||
*/
|
||||
[[nodiscard]] MoneyAgent2D *get_agent_by_id(AgentID agent_id) const override;
|
||||
std::shared_ptr<kami::Model> step() final;
|
||||
|
||||
private:
|
||||
map<AgentID, MoneyAgent2D *> _agent_list;
|
||||
RandomScheduler *_sched;
|
||||
MultiGrid2D *_world;
|
||||
unsigned int _step_count;
|
||||
};
|
||||
|
||||
|
||||
22
examples/minimal/CMakeLists.txt
Normal file
22
examples/minimal/CMakeLists.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
####
|
||||
# Set minimum version of CMake.
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
find_package(spdlog)
|
||||
|
||||
set(EXAMPLE_NAME "minimal")
|
||||
|
||||
project(${EXAMPLE_NAME} LANGUAGES CXX)
|
||||
|
||||
file(GLOB EXAMPLE_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cc")
|
||||
|
||||
create_executable(
|
||||
NAME ${EXAMPLE_NAME}
|
||||
SOURCES ${EXAMPLE_SOURCES}
|
||||
PUBLIC_DEFINITIONS USE_DOUBLE_PRECISION=1
|
||||
PRIVATE_DEFINITIONS DEBUG_VERBOSE
|
||||
PRIVATE_INCLUDE_PATHS ${CMAKE_SOURCE_DIR}/include
|
||||
PUBLIC_LINKED_TARGETS fmt spdlog::spdlog kami::libkami
|
||||
)
|
||||
|
||||
set_target_properties(${EXAMPLE_NAME} PROPERTIES VERSION ${VERSION_STRING})
|
||||
68
examples/minimal/minimal.cc
Normal file
68
examples/minimal/minimal.cc
Normal file
@@ -0,0 +1,68 @@
|
||||
/*-
|
||||
* Copyright (c) 2023 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/kami.h>
|
||||
#include <kami/model.h>
|
||||
#include <kami/population.h>
|
||||
#include <kami/sequential.h>
|
||||
|
||||
class MinimalAgent
|
||||
: public kami::Agent {
|
||||
public:
|
||||
kami::AgentID step(std::shared_ptr<kami::Model> model) override {
|
||||
return this->get_agent_id();
|
||||
}
|
||||
};
|
||||
|
||||
class MinimalModel
|
||||
: public kami::Model {
|
||||
public:
|
||||
MinimalModel() {
|
||||
auto sched = std::make_shared<kami::SequentialScheduler>();
|
||||
set_scheduler(sched);
|
||||
|
||||
auto pop = std::make_shared<kami::Population>();
|
||||
set_population(pop);
|
||||
|
||||
for (auto i = 0; i < 10; i++) {
|
||||
auto new_agent = std::make_shared<MinimalAgent>();
|
||||
pop->add_agent(new_agent);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
auto model = std::make_shared<MinimalModel>();
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
model->step();
|
||||
|
||||
return 0;
|
||||
}
|
||||
22
examples/starter/CMakeLists.txt
Normal file
22
examples/starter/CMakeLists.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
####
|
||||
# Set minimum version of CMake.
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
find_package(spdlog)
|
||||
|
||||
set(EXAMPLE_NAME "starter")
|
||||
|
||||
project(${EXAMPLE_NAME} LANGUAGES CXX)
|
||||
|
||||
file(GLOB EXAMPLE_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cc")
|
||||
|
||||
create_executable(
|
||||
NAME ${EXAMPLE_NAME}
|
||||
SOURCES ${EXAMPLE_SOURCES}
|
||||
PUBLIC_DEFINITIONS USE_DOUBLE_PRECISION=1
|
||||
PRIVATE_DEFINITIONS DEBUG_VERBOSE
|
||||
PRIVATE_INCLUDE_PATHS ${CMAKE_SOURCE_DIR}/include
|
||||
PUBLIC_LINKED_TARGETS fmt spdlog::spdlog kami::libkami
|
||||
)
|
||||
|
||||
set_target_properties(${EXAMPLE_NAME} PROPERTIES VERSION ${VERSION_STRING})
|
||||
136
examples/starter/starter.cc
Normal file
136
examples/starter/starter.cc
Normal file
@@ -0,0 +1,136 @@
|
||||
/*-
|
||||
* Copyright (c) 2020 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "starter.h"
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
|
||||
#include <CLI/CLI.hpp>
|
||||
|
||||
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <spdlog/stopwatch.h>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/kami.h>
|
||||
#include <kami/model.h>
|
||||
#include <kami/population.h>
|
||||
#include <kami/random.h>
|
||||
|
||||
std::shared_ptr<spdlog::logger> console = nullptr;
|
||||
std::shared_ptr<std::mt19937> rng = nullptr;
|
||||
|
||||
template<>
|
||||
struct fmt::formatter<kami::AgentID>
|
||||
: fmt::formatter<std::string> {
|
||||
static auto format(
|
||||
kami::AgentID agent_id,
|
||||
format_context& ctx
|
||||
) {
|
||||
return format_to(ctx.out(), "{}", agent_id.to_string());
|
||||
}
|
||||
};
|
||||
|
||||
StarterAgent::StarterAgent() {
|
||||
console->debug("StarterAgent with ID {} constructed", this->get_agent_id());
|
||||
}
|
||||
|
||||
StarterAgent::~StarterAgent() {
|
||||
console->debug("StarterAgent with ID {} deconstructed", this->get_agent_id());
|
||||
}
|
||||
|
||||
kami::AgentID StarterAgent::step(std::shared_ptr<kami::Model> model) {
|
||||
this->_step_counter++;
|
||||
return this->get_agent_id();
|
||||
}
|
||||
|
||||
StarterModel::StarterModel(
|
||||
unsigned int number_agents,
|
||||
unsigned int new_seed
|
||||
) {
|
||||
rng = std::make_shared<std::mt19937>();
|
||||
rng->seed(new_seed);
|
||||
|
||||
auto sched = std::make_shared<kami::RandomScheduler>(rng);
|
||||
auto pop = std::make_shared<kami::Population>();
|
||||
|
||||
_sched = sched;
|
||||
_pop = pop;
|
||||
|
||||
console->debug("Scheduler initiated with seed {}", new_seed);
|
||||
|
||||
_step_count = 0;
|
||||
|
||||
for (auto i = 0; i < number_agents; i++) {
|
||||
auto new_agent = std::make_shared<StarterAgent>();
|
||||
|
||||
console->trace("Initializing agent with AgentID {}", new_agent->get_agent_id());
|
||||
_pop->add_agent(new_agent);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<kami::Model> StarterModel::step() {
|
||||
console->trace("Executing model step {}", ++_step_count);
|
||||
_sched->step(shared_from_this());
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
std::string ident = "starter";
|
||||
std::string log_level_option = "info";
|
||||
CLI::App app{ident};
|
||||
unsigned int agent_count = 100, max_steps = 100, initial_seed = 8675309;
|
||||
|
||||
// This exercise is really stupid.
|
||||
auto levels_list = std::make_unique<std::list<std::string>>();
|
||||
for (auto& level_name : SPDLOG_LEVEL_NAMES)
|
||||
levels_list->push_back(std::string(level_name.data(), level_name.size()));
|
||||
|
||||
app.add_option("-c", agent_count, "Set the number of agents")->check(CLI::PositiveNumber);
|
||||
app.add_option("-l", log_level_option, "Set the logging level")->check(
|
||||
CLI::IsMember(levels_list.get(), CLI::ignore_case));
|
||||
app.add_option("-n", max_steps, "Set the number of steps to run the model")->check(CLI::PositiveNumber);
|
||||
app.add_option("-s", initial_seed, "Set the initial seed")->check(CLI::Number);
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
console = spdlog::stdout_color_st(ident);
|
||||
console->set_level(spdlog::level::from_str(log_level_option));
|
||||
console->info("Compiled with Kami/{}, log level {}", kami::version.to_string(), log_level_option);
|
||||
console->info("Starting Starter Model with {} agents for {} steps", agent_count, max_steps);
|
||||
|
||||
auto model = std::make_shared<StarterModel>(agent_count, initial_seed);
|
||||
|
||||
spdlog::stopwatch sw;
|
||||
for (int i = 0; i < max_steps; i++)
|
||||
model->step();
|
||||
|
||||
console->info("Starter Model simulation complete, requiring {} seconds", sw);
|
||||
}
|
||||
93
examples/starter/starter.h
Normal file
93
examples/starter/starter.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/*-
|
||||
* Copyright (c) 2022 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef STARTER_H
|
||||
//! @cond SuppressGuard
|
||||
#define STARTER_H
|
||||
//! @endcond
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/kami.h>
|
||||
#include <kami/random.h>
|
||||
|
||||
/**
|
||||
* A starter agent for a starter model
|
||||
*/
|
||||
class StarterAgent
|
||||
: public kami::Agent {
|
||||
private:
|
||||
int _step_counter = 0;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
StarterAgent();
|
||||
|
||||
/**
|
||||
* Deconstructor
|
||||
*/
|
||||
~StarterAgent();
|
||||
|
||||
/**
|
||||
* Execute a single time-step for the agent
|
||||
*/
|
||||
kami::AgentID step(std::shared_ptr<kami::Model> model) override;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* The one-dimensional Boltzmann wealth model
|
||||
*/
|
||||
class StarterModel
|
||||
: public kami::Model {
|
||||
private:
|
||||
unsigned int _step_count;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create an instance of the one-dimensional Boltzmann wealth model.
|
||||
*
|
||||
* @param[in] number_agents the number of agents to assign to the model.
|
||||
* @param[in] length_x the length of the one-dimensional world the agents
|
||||
* occupy.
|
||||
*/
|
||||
explicit StarterModel(
|
||||
unsigned int number_agents = 10,
|
||||
unsigned int new_seed = 42
|
||||
);
|
||||
|
||||
/**
|
||||
* Execute a single time-step for the model.
|
||||
*/
|
||||
std::shared_ptr<kami::Model> step();
|
||||
};
|
||||
|
||||
#endif // STARTER_H
|
||||
@@ -25,61 +25,82 @@
|
||||
|
||||
#pragma once
|
||||
#ifndef KAMI_AGENT_H
|
||||
//! @cond SuppressGuard
|
||||
#define KAMI_AGENT_H
|
||||
//! @endcond
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <kami/kami.h>
|
||||
|
||||
#include <kami/model.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
/**
|
||||
* A unique identifier for each `Agent`.
|
||||
*
|
||||
* The unique identifier permits ordering to allow `AgentID`s to be used as keys
|
||||
* for `std::map`. The unique identifier is unique for the session, however,
|
||||
* `AgentID`s are not guaranteed to be unique from session-to-session.
|
||||
*
|
||||
* @see Agent
|
||||
*/
|
||||
/**
|
||||
* @brief A unique identifier for each `Agent`.
|
||||
*
|
||||
* @details The unique identifier permits ordering to allow `AgentID`s to be used as keys
|
||||
* for `std::map`. The unique identifier is unique for the session, however,
|
||||
* `AgentID`s are not guaranteed to be unique from session-to-session.
|
||||
*
|
||||
* @see Agent
|
||||
*/
|
||||
class LIBKAMI_EXPORT AgentID {
|
||||
public:
|
||||
/**
|
||||
* Constructs a new unique identifier.
|
||||
*/
|
||||
AgentID() : _id(_id_next++){};
|
||||
private:
|
||||
inline static long long _id_next = 1;
|
||||
|
||||
/**
|
||||
* Convert the identifier to a human-readable string.
|
||||
* @brief The unique identifier is a `long long`.
|
||||
*
|
||||
* @details The unique identifier is an unsigned integer that increments
|
||||
* monotonically with each new `AgentID` instantiated. This is
|
||||
* substantially faster than other potential identifiers, such
|
||||
* as MD5 hashes or UUID objects.
|
||||
*/
|
||||
long long _id;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a new unique identifier.
|
||||
*/
|
||||
AgentID();
|
||||
|
||||
/**
|
||||
* @brief Convert the identifier to a human-readable string.
|
||||
*
|
||||
* @return a human-readable form of the `AgentID` as `std::string`.
|
||||
*/
|
||||
[[nodiscard]] std::string to_string() const { return std::to_string(_id); }
|
||||
[[nodiscard]] std::string to_string() const;
|
||||
|
||||
/**
|
||||
* Test if two `AgentID` instances are equal.
|
||||
* @brief Test if two `AgentID` instances are equal.
|
||||
*
|
||||
* @param lhs is the left-hand side of the equality test.
|
||||
* @param rhs is the right-hand side of the equality test.
|
||||
* @return true is they are equal and false if not.
|
||||
*/
|
||||
friend bool operator==(const AgentID &lhs, const AgentID &rhs);
|
||||
friend bool operator==(
|
||||
const AgentID& lhs,
|
||||
const AgentID& rhs
|
||||
);
|
||||
|
||||
/**
|
||||
* Test if two `AgentID` instances are not equal.
|
||||
* @brief Test if two `AgentID` instances are not equal.
|
||||
*
|
||||
* @param lhs is the left-hand side of the equality test.
|
||||
* @param rhs is the right-hand side of the equality test.
|
||||
* @return true is they are not equal and false if they are.
|
||||
*/
|
||||
friend bool operator!=(const AgentID &lhs, const AgentID &rhs);
|
||||
friend bool operator!=(
|
||||
const AgentID& lhs,
|
||||
const AgentID& rhs
|
||||
);
|
||||
|
||||
/**
|
||||
* Test if one AgentID is less than another.
|
||||
* @brief Test if one AgentID is less than another.
|
||||
*
|
||||
* Due to the way AgentID instances are used internally,
|
||||
* @details Due to the way AgentID instances are used internally,
|
||||
* the AgentID must be orderable. The `<` operator provides a
|
||||
* basic ordering sufficient for `std::map`.
|
||||
*
|
||||
@@ -88,62 +109,62 @@ namespace kami {
|
||||
* @return true if `lhs` is "less than" `rhs` as determined by the
|
||||
* underlying implementation of the `AgentID`.
|
||||
*/
|
||||
friend bool operator<(const AgentID &lhs, const AgentID &rhs);
|
||||
friend bool operator<(
|
||||
const AgentID& lhs,
|
||||
const AgentID& rhs
|
||||
);
|
||||
|
||||
/**
|
||||
* Output an AgentID to the specified output stream
|
||||
* @brief Output an AgentID to the specified output stream
|
||||
*
|
||||
* The form of the output will be the same as that produced by the
|
||||
* @details The form of the output will be the same as that produced by the
|
||||
* `to_string()` member function.
|
||||
*
|
||||
* @param lhs is the stream to output the `AgentID` to
|
||||
* @param rhs is the `AgentID` to output
|
||||
* @return the output stream for reuse
|
||||
*/
|
||||
friend std::ostream &operator<<(std::ostream &lhs, const AgentID &rhs);
|
||||
|
||||
private:
|
||||
inline static long long _id_next = 1;
|
||||
|
||||
/**
|
||||
* The unique identifier is a `long long`.
|
||||
*
|
||||
* The unique identifier is an unsigned integer that increments
|
||||
* monotonically with each new `AgentID` instantiated. This is
|
||||
* substantially faster than other potential identifiers, such
|
||||
* as MD5 hashes or UUID objects.
|
||||
*/
|
||||
long long _id;
|
||||
friend std::ostream& operator<<(
|
||||
std::ostream& lhs,
|
||||
const AgentID& rhs
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* A superclass for all agents.
|
||||
*
|
||||
* All agents should subclass the `Agent` class. At a minimum, subclasses must
|
||||
* implement the `step()` function, to execute a single time step for each
|
||||
* agent.
|
||||
*
|
||||
* @see `StagedAgent`
|
||||
*/
|
||||
/**
|
||||
* @brief A superclass for all agents.
|
||||
*
|
||||
* @details All agents should subclass the `Agent` class. At a minimum, subclasses must
|
||||
* implement the `step()` function, to execute a single time step for each
|
||||
* agent.
|
||||
*
|
||||
* @see `ReporterAgent`, `StagedAgent`
|
||||
*/
|
||||
class LIBKAMI_EXPORT Agent {
|
||||
private:
|
||||
const AgentID _agent_id;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Get the `Agent`'s `AgentID`.
|
||||
* @brief Get the `Agent`'s `AgentID`.
|
||||
*
|
||||
* @return the `AgentID`
|
||||
*/
|
||||
[[nodiscard]] AgentID get_agent_id() const;
|
||||
|
||||
/**
|
||||
* Execute a time-step for the agent
|
||||
* @brief Execute a time-step for the agent
|
||||
*
|
||||
* This function should step the agent instance. Any activities that the
|
||||
* @details This function should step the agent instance. Any activities that the
|
||||
* agent should perform as part of its time step should be in this function.
|
||||
*
|
||||
* @param model a reference copy of the model
|
||||
*
|
||||
* @returns a copy of the AgentID
|
||||
*/
|
||||
virtual void step() = 0;
|
||||
virtual AgentID step(std::shared_ptr<Model> model) = 0;
|
||||
|
||||
/**
|
||||
* Compare if two `Agent`s are the same `Agent`.
|
||||
* @brief Compare if two `Agent`s are the same `Agent`.
|
||||
*
|
||||
* @param lhs is the left-hand side of the equality test.
|
||||
* @param rhs is the right-hand side of the equality test.
|
||||
@@ -156,10 +177,13 @@ namespace kami {
|
||||
* Subclasses of Agent may chose to extend this operator to tighten
|
||||
* the restrictions on the comparison.
|
||||
*/
|
||||
friend bool operator==(const Agent &lhs, const Agent &rhs);
|
||||
friend bool operator==(
|
||||
const Agent& lhs,
|
||||
const Agent& rhs
|
||||
);
|
||||
|
||||
/**
|
||||
* Compare if two `Agent`s are not the same `Agent`.
|
||||
* @brief Compare if two `Agent`s are not the same `Agent`.
|
||||
*
|
||||
* @param lhs is the left-hand side of the equality test.
|
||||
* @param rhs is the right-hand side of the equality test.
|
||||
@@ -172,34 +196,38 @@ namespace kami {
|
||||
* Subclasses of `Agent` may chose to extend this operator to tighten
|
||||
* the restrictions on the comparison.
|
||||
*/
|
||||
friend bool operator!=(const Agent &lhs, const Agent &rhs);
|
||||
|
||||
private:
|
||||
const AgentID _agent_id;
|
||||
friend bool operator!=(
|
||||
const Agent& lhs,
|
||||
const Agent& rhs
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* A superclass for all staged agents.
|
||||
*
|
||||
* Staged agents use a two-phase step to allow agents to take actions without
|
||||
* updating the state of the model before all agents have been allowed to
|
||||
* update. All work necessary to advance the `StagedAgent` state should take
|
||||
* place in the `step()` function. However, the `StagedAgent` should not actually
|
||||
* update the state, and instead save the results for later use. Finally,
|
||||
* during the `advance()` stage, the StagedAgent state should update.
|
||||
*
|
||||
* `StagedAgents` must implement both the `step()` and `advance()` functions.
|
||||
*/
|
||||
class LIBKAMI_EXPORT StagedAgent : public Agent {
|
||||
/**
|
||||
* @brief A superclass for all staged agents.
|
||||
*
|
||||
* @details Staged agents use a two-phase step to allow agents to take actions without
|
||||
* updating the state of the model before all agents have been allowed to
|
||||
* update. All work necessary to advance the `StagedAgent` state should take
|
||||
* place in the `step()` function. However, the `StagedAgent` should not actually
|
||||
* update the state, and instead save the results for later use. Finally,
|
||||
* during the `advance()` stage, the StagedAgent state should update.
|
||||
*
|
||||
* `StagedAgents` must implement both the `step()` and `advance()` functions.
|
||||
*/
|
||||
class LIBKAMI_EXPORT StagedAgent
|
||||
: public Agent {
|
||||
public:
|
||||
/**
|
||||
* Post-step advance the agent
|
||||
* @brief Post-step advance the agent
|
||||
*
|
||||
* This method should be called after `step()`. Any updates or cleanups to
|
||||
* @details This method should be called after `step()`. Any updates or cleanups to
|
||||
* the agent that must happen for the `StagedAgent` to complete its step must
|
||||
* happen here.
|
||||
*
|
||||
* @param model a reference copy of the model
|
||||
*
|
||||
*/
|
||||
virtual void advance() = 0;
|
||||
virtual AgentID advance(std::shared_ptr<Model> model) = 0;
|
||||
};
|
||||
|
||||
} // namespace kami
|
||||
|
||||
@@ -29,36 +29,40 @@
|
||||
* In the source code, include "exampleConfig.h" (no .in suffix).
|
||||
* This header file will be generated, and filled with the
|
||||
* right definition values. Change the namings as you wish,
|
||||
* but make sure they match up with whats in CMakeLists.txt.
|
||||
* but make sure they match up with what's in CMakeLists.txt.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef KAMI_CONFIG_H
|
||||
#define KAMI_CONFIG_H
|
||||
|
||||
#include <semver.hpp>
|
||||
|
||||
/**
|
||||
* The major version of the Kami library.
|
||||
*/
|
||||
#define KAMI_VERSION_MAJOR @KAMI_VERSION_MAJOR @
|
||||
#define KAMI_VERSION_MAJOR @VERSION_MAJOR@
|
||||
|
||||
/**
|
||||
* The minor version of the Kami library.
|
||||
*/
|
||||
#define KAMI_VERSION_MINOR @KAMI_VERSION_MINOR @
|
||||
#define KAMI_VERSION_MINOR @VERSION_MINOR@
|
||||
|
||||
/**
|
||||
* The patch level of the Kami library.
|
||||
*/
|
||||
#define KAMI_VERSION_PATCH @KAMI_VERSION_PATCH @
|
||||
#define KAMI_VERSION_PATCH @VERSION_PATCH@
|
||||
|
||||
/**
|
||||
* The version of the Kami library as a `char*` constant.
|
||||
*/
|
||||
#define KAMI_VERSION_STRING "@KAMI_VERSION_STRING@"
|
||||
namespace kami {
|
||||
|
||||
namespace {
|
||||
/**
|
||||
* @brief A reference copy of the current version of Kami
|
||||
*/
|
||||
constexpr auto version = semver::version{KAMI_VERSION_MAJOR, KAMI_VERSION_MINOR, KAMI_VERSION_PATCH};
|
||||
}
|
||||
|
||||
} // namespace kami
|
||||
|
||||
/**
|
||||
* The Kami library identifier as a `char *` constant.
|
||||
*/
|
||||
#define KAMI_VERSION_ID "Kami/@KAMI_VERSION_STRING@"
|
||||
|
||||
#endif // KAMI_CONFIG_H
|
||||
|
||||
@@ -25,7 +25,9 @@
|
||||
|
||||
#pragma once
|
||||
#ifndef KAMI_DOMAIN_H
|
||||
//! @cond SuppressGuard
|
||||
#define KAMI_DOMAIN_H
|
||||
//! @endcond
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -33,45 +35,57 @@
|
||||
|
||||
namespace kami {
|
||||
|
||||
/**
|
||||
* Provides an environment for the agents to participate in.
|
||||
*
|
||||
* Implementations of virtual environments are expected to subclass `Domain`.
|
||||
*/
|
||||
class LIBKAMI_EXPORT Domain {};
|
||||
/**
|
||||
* @brief Provides an environment for the agents to participate in.
|
||||
*
|
||||
* @details Implementations of virtual environments are expected to subclass `Domain`.
|
||||
*/
|
||||
class LIBKAMI_EXPORT Domain {
|
||||
protected:
|
||||
/**
|
||||
* @brief Constructor.
|
||||
*
|
||||
* @details Making this constructor protected makes the class abstract without having
|
||||
* to create any virtual functions.
|
||||
*/
|
||||
Domain() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* Provides a coordinate system for each `Domain`.
|
||||
*
|
||||
* The coordinate system must be able to produce a human-readable version of the
|
||||
* coordinates given. For instance, an integer grid in two dimensions would
|
||||
* provide standard Descartes coordinates like (0, 0) for the origin, or (2, 3)
|
||||
* for the position that is two units "up" and three units to the "right" of the
|
||||
* origin. Implementation of a coordinate system is left up to the user, though
|
||||
* there are several established systems provided.
|
||||
*
|
||||
* @see GridCoord
|
||||
*/
|
||||
/**
|
||||
* @brief Provides a coordinate system for each `Domain`.
|
||||
*
|
||||
* @details The coordinate system must be able to produce a human-readable version of the
|
||||
* coordinates given. For instance, an integer grid in two dimensions would
|
||||
* provide standard Descartes coordinates like (0, 0) for the origin, or (2, 3)
|
||||
* for the position that is two units "up" and three units to the "right" of the
|
||||
* origin. Implementation of a coordinate system is left up to the user, though
|
||||
* there are several established systems provided.
|
||||
*
|
||||
* @see GridCoord
|
||||
*/
|
||||
class LIBKAMI_EXPORT Coord {
|
||||
public:
|
||||
/**
|
||||
* Convert the coordinate to a human-readable string.
|
||||
* @brief Convert the coordinate to a human-readable string.
|
||||
*
|
||||
* @return a human-readable form of the `Coord` as `std::string`.
|
||||
*/
|
||||
[[nodiscard]] virtual std::string to_string() const = 0;
|
||||
|
||||
/**
|
||||
* Output a `Coord` to the specified output stream
|
||||
* @brief Output a `Coord` to the specified output stream
|
||||
*
|
||||
* The form of the output will be the same as that produced by the
|
||||
* @details The form of the output will be the same as that produced by the
|
||||
* `to_string()` member function.
|
||||
*
|
||||
* @param lhs is the stream to output the `Coord` to
|
||||
* @param rhs is the `Coord` to output
|
||||
* @return the output stream for reuse
|
||||
*/
|
||||
friend std::ostream &operator<<(std::ostream &lhs, const Coord &rhs);
|
||||
friend std::ostream& operator<<(
|
||||
std::ostream& lhs,
|
||||
const Coord& rhs
|
||||
);
|
||||
};
|
||||
|
||||
} // namespace kami
|
||||
|
||||
136
include/kami/error.h
Normal file
136
include/kami/error.h
Normal file
@@ -0,0 +1,136 @@
|
||||
//
|
||||
// Created by James Howard on 9/9/22.
|
||||
//
|
||||
|
||||
#ifndef KAMI_ERROR_H
|
||||
//! @cond SuppressGuard
|
||||
#define KAMI_ERROR_H
|
||||
//! @endcond
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace kami::error {
|
||||
|
||||
/**
|
||||
* @brief Agent was not found
|
||||
*/
|
||||
class AgentNotFound
|
||||
: public std::logic_error {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param s text description of the exception
|
||||
*/
|
||||
explicit AgentNotFound(const char* s)
|
||||
:std::logic_error(s) {
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param s text description of the exception
|
||||
*/
|
||||
explicit AgentNotFound(const std::string& s)
|
||||
:std::logic_error(s) {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Location specified is invalid
|
||||
*
|
||||
* @see `LocationUnavailable`
|
||||
*/
|
||||
class LocationInvalid
|
||||
: public std::domain_error {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param s text description of the exception
|
||||
*/
|
||||
explicit LocationInvalid(const char* s)
|
||||
:std::domain_error(s) {
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param s text description of the exception
|
||||
*/
|
||||
explicit LocationInvalid(const std::string& s)
|
||||
:std::domain_error(s) {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Location specified is unavailable
|
||||
*
|
||||
* @see `LocationInvalid`
|
||||
*/
|
||||
class LocationUnavailable
|
||||
: public std::domain_error {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param s text description of the exception
|
||||
*/
|
||||
explicit LocationUnavailable(const char* s)
|
||||
:std::domain_error(s) {
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param s text description of the exception
|
||||
*/
|
||||
explicit LocationUnavailable(const std::string& s)
|
||||
:std::domain_error(s) {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The option given is not valid at this time
|
||||
*/
|
||||
class OptionInvalid
|
||||
: public std::invalid_argument {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param s text description of the exception
|
||||
*/
|
||||
explicit OptionInvalid(const char* s)
|
||||
:std::invalid_argument(s) {
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param s text description of the exception
|
||||
*/
|
||||
explicit OptionInvalid(const std::string& s)
|
||||
:std::invalid_argument(s) {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The resource specified is not available at this time
|
||||
*/
|
||||
class ResourceNotAvailable
|
||||
: public std::logic_error {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param s text description of the exception
|
||||
*/
|
||||
explicit ResourceNotAvailable(const char* s)
|
||||
:std::logic_error(s) {
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param s text description of the exception
|
||||
*/
|
||||
explicit ResourceNotAvailable(const std::string& s)
|
||||
:std::logic_error(s) {
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //KAMI_ERROR_H
|
||||
@@ -25,7 +25,9 @@
|
||||
|
||||
#pragma once
|
||||
#ifndef KAMI_GRID_H
|
||||
//! @cond SuppressGuard
|
||||
#define KAMI_GRID_H
|
||||
//! @endcond
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -34,14 +36,14 @@
|
||||
|
||||
namespace kami {
|
||||
|
||||
/**
|
||||
* @brief Neighborhood types for orthogonal grid domains of cells.
|
||||
*
|
||||
* @details Orthogonal grid domains are those that provide cells equidistant
|
||||
* along a standard Cartesian grid. `GridNeighborhoodType` allows for the
|
||||
* distinction between those neighborhoods that include those cells touching on
|
||||
* the corners or diagonally and those neighborhoods that do not.
|
||||
*/
|
||||
/**
|
||||
* @brief Neighborhood types for orthogonal grid domains of cells.
|
||||
*
|
||||
* @details Orthogonal grid domains are those that provide cells equidistant
|
||||
* along a standard Cartesian grid. `GridNeighborhoodType` allows for the
|
||||
* distinction between those neighborhoods that include those cells touching on
|
||||
* the corners or diagonally and those neighborhoods that do not.
|
||||
*/
|
||||
enum class GridNeighborhoodType {
|
||||
/**
|
||||
* @brief Moore neighborhood
|
||||
@@ -49,7 +51,7 @@ namespace kami {
|
||||
* @details Moore neighborhood types include diagonally-adjacent cells as
|
||||
* neighbors.
|
||||
*/
|
||||
Moore [[maybe_unused]],
|
||||
Moore,
|
||||
|
||||
/**
|
||||
* @brief Von Neumann neighborhood
|
||||
@@ -57,12 +59,12 @@ namespace kami {
|
||||
* @details Von Neumann neighborhood types do not include
|
||||
* diagonally-adjacent cells as neighbors.
|
||||
*/
|
||||
VonNeumann [[maybe_unused]]
|
||||
VonNeumann
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Distance types for orthogonal grid domains
|
||||
*/
|
||||
/**
|
||||
* @brief Distance types for orthogonal grid domains
|
||||
*/
|
||||
enum class GridDistanceType {
|
||||
/**
|
||||
* @brief Euclidean distance.
|
||||
@@ -71,7 +73,7 @@ namespace kami {
|
||||
* connecting two points. This is commonly called a "beeline" or
|
||||
* "as the crow flies."
|
||||
*/
|
||||
Euclidean [[maybe_unused]],
|
||||
Euclidean,
|
||||
|
||||
/**
|
||||
* @brief Manhattan distance.
|
||||
@@ -81,24 +83,58 @@ namespace kami {
|
||||
* "taxicab distance," "rectilinear distance," or many other [formal
|
||||
* names](https://en.wikipedia.org/wiki/Taxicab_geometry).
|
||||
*/
|
||||
Manhattan [[maybe_unused]]
|
||||
Manhattan,
|
||||
|
||||
/**
|
||||
* @brief Chebyshev distance.
|
||||
*
|
||||
* @details The Chebyshev distance, also called the "chessboard" distance
|
||||
* is the number of single point jumps necessary to move from one point to
|
||||
* the next. This can be likened to a king on a chessboard and the number
|
||||
* of moves necessary to move from a given point to any other given point.
|
||||
*/
|
||||
Chebyshev
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An abstract domain based on a gridded environment.
|
||||
*
|
||||
* @details All gridded domains are expected to consist of cells in a
|
||||
* rectilinear grid where the cells are equal size and laid out in an ordered
|
||||
* fashion.
|
||||
*/
|
||||
class LIBKAMI_EXPORT GridDomain : public Domain {};
|
||||
/**
|
||||
* @brief An abstract domain based on a gridded environment.
|
||||
*
|
||||
* @details All gridded domains are expected to consist of cells in a
|
||||
* rectilinear grid where the cells are equal size and laid out in an ordered
|
||||
* fashion.
|
||||
*/
|
||||
class LIBKAMI_EXPORT GridDomain
|
||||
: public Domain {
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An abstract for gridded coordinates.
|
||||
*
|
||||
* @details All gridded coordinates are expected to subclass `GridCoord`.
|
||||
*/
|
||||
class LIBKAMI_EXPORT GridCoord : public Coord {};
|
||||
/**
|
||||
* @brief An abstract for gridded coordinates.
|
||||
*
|
||||
* @details All gridded coordinates are expected to subclass `GridCoord`.
|
||||
*/
|
||||
class LIBKAMI_EXPORT GridCoord
|
||||
: public Coord {
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Find the distance between two points
|
||||
*
|
||||
* @details Find the distance between two points using the
|
||||
* specified metric.
|
||||
*
|
||||
* However, the coordinate class is not aware of the
|
||||
* properties of the `GridDomain` it is operating on. Accordingly,
|
||||
* if the direct path is measured, without accounting for
|
||||
* and toroidal wrapping of the underlying `GridDomain`.
|
||||
*
|
||||
* @param p the point to measure the distance to
|
||||
*
|
||||
* @returns the distance as a `double`
|
||||
*/
|
||||
virtual double distance(std::shared_ptr<Coord>& p) const = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace kami
|
||||
|
||||
|
||||
@@ -25,84 +25,158 @@
|
||||
|
||||
#pragma once
|
||||
#ifndef KAMI_GRID1D_H
|
||||
//! @cond SuppressGuard
|
||||
#define KAMI_GRID1D_H
|
||||
//! @endcond
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include <kami/domain.h>
|
||||
#include <kami/error.h>
|
||||
#include <kami/grid.h>
|
||||
#include <kami/kami.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
/**
|
||||
* One-dimensional coordinates
|
||||
*/
|
||||
class LIBKAMI_EXPORT GridCoord1D : public GridCoord {
|
||||
/**
|
||||
* @brief One-dimensional coordinates
|
||||
*/
|
||||
class LIBKAMI_EXPORT GridCoord1D
|
||||
: public GridCoord {
|
||||
public:
|
||||
/**
|
||||
* Constructor for one-dimensional coordinates
|
||||
* @brief Constructor for one-dimensional coordinates
|
||||
*/
|
||||
explicit GridCoord1D(int x_coord) : _x_coord(x_coord){};
|
||||
explicit GridCoord1D(int x_coord);
|
||||
|
||||
/**
|
||||
* Return the `x` coordinate
|
||||
* @brief Return the `x` coordinate
|
||||
*/
|
||||
[[nodiscard]] int get_x_location() const;
|
||||
[[nodiscard]] int x() const;
|
||||
|
||||
/**
|
||||
* Convert the coordinate to a human-readable string.
|
||||
* @brief Convert the coordinate to a human-readable string.
|
||||
*
|
||||
* @return a human-readable form of the `Coord` as `std::string`.
|
||||
*/
|
||||
[[nodiscard]] std::string to_string() const override;
|
||||
|
||||
/**
|
||||
* Test if two coordinates are equal
|
||||
* @brief Find the distance between two points
|
||||
*
|
||||
* @details Find the distance between two points using the
|
||||
* specified metric. There are three options provided by
|
||||
* the `GridDistanceType` class. However, of the three
|
||||
* distance types provided, all provide the same result so
|
||||
* the value is ignored and the single result is returned.
|
||||
*
|
||||
* However, the coordinate class is not aware of the
|
||||
* properties of the `Grid1D` it is operating on. Accordingly,
|
||||
* if the direct path is measured, without accounting for
|
||||
* and toroidal wrapping of the underlying `Grid1D`.
|
||||
*
|
||||
* @param p the point to measure the distance to
|
||||
*
|
||||
* @returns the distance as a `double`
|
||||
*/
|
||||
friend bool operator==(const GridCoord1D &lhs, const GridCoord1D &rhs);
|
||||
double distance(std::shared_ptr<Coord>& p) const override;
|
||||
|
||||
/**
|
||||
* Test if two coordinates are not equal
|
||||
* @brief Test if two coordinates are equal
|
||||
*/
|
||||
friend bool operator!=(const GridCoord1D &lhs, const GridCoord1D &rhs);
|
||||
friend bool operator==(
|
||||
const GridCoord1D& lhs,
|
||||
const GridCoord1D& rhs
|
||||
);
|
||||
|
||||
/**
|
||||
* Output a given coordinate to the specified stream
|
||||
* @brief Test if two coordinates are not equal
|
||||
*/
|
||||
friend std::ostream &operator<<(std::ostream &lhs, const GridCoord1D &rhs);
|
||||
friend bool operator!=(
|
||||
const GridCoord1D& lhs,
|
||||
const GridCoord1D& rhs
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Output a given coordinate to the specified stream
|
||||
*/
|
||||
friend std::ostream& operator<<(
|
||||
std::ostream& lhs,
|
||||
const GridCoord1D& rhs
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Add two coordinates together
|
||||
*/
|
||||
inline friend GridCoord1D operator+(
|
||||
const GridCoord1D& lhs,
|
||||
const GridCoord1D& rhs
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Subtract one coordinate from another
|
||||
*/
|
||||
inline friend GridCoord1D operator-(
|
||||
const GridCoord1D& lhs,
|
||||
const GridCoord1D& rhs
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Multiply a coordinate by a scalar
|
||||
*
|
||||
* @details If any component of the resulting value is not a whole number, it is
|
||||
* truncated following the same rules as `int`.
|
||||
*/
|
||||
inline friend GridCoord1D operator*(
|
||||
const GridCoord1D& lhs,
|
||||
double rhs
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Multiply a coordinate by a scalar
|
||||
*
|
||||
* @details If any component of the resulting value is not a whole number, it is
|
||||
* truncated following the same rules as `int`.
|
||||
*/
|
||||
inline friend GridCoord1D operator*(
|
||||
double lhs,
|
||||
const GridCoord1D& rhs
|
||||
);
|
||||
|
||||
private:
|
||||
int _x_coord;
|
||||
};
|
||||
|
||||
/**
|
||||
* A one-dimensional grid where each cell may contain agents
|
||||
*
|
||||
* The grid is linear and may wrap around in its only dimension.
|
||||
*
|
||||
* @see `MultiGrid1D`
|
||||
* @see `SoloGrid1D`
|
||||
*/
|
||||
class LIBKAMI_EXPORT Grid1D : public GridDomain {
|
||||
/**
|
||||
* @brief A one-dimensional grid where each cell may contain agents
|
||||
*
|
||||
* @details The grid is linear and may wrap around in its only dimension.
|
||||
*
|
||||
* @see `MultiGrid1D`
|
||||
* @see `SoloGrid1D`
|
||||
*/
|
||||
class LIBKAMI_EXPORT Grid1D
|
||||
: public GridDomain {
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* @brief Constructor
|
||||
*
|
||||
* @param[in] maximum_x the length of the grid.
|
||||
* @param[in] wrap_x should the grid wrap around on itself.
|
||||
*/
|
||||
explicit Grid1D(unsigned int maximum_x, bool wrap_x = false);
|
||||
explicit Grid1D(
|
||||
unsigned int maximum_x,
|
||||
bool wrap_x = false
|
||||
);
|
||||
|
||||
/**
|
||||
* Deconstructor
|
||||
*/
|
||||
virtual ~Grid1D();
|
||||
|
||||
/**
|
||||
* Place agent on the grid at the specified location.
|
||||
* @brief Place agent on the grid at the specified location.
|
||||
*
|
||||
* @param[in] agent_id the `AgentID` of the agent to add.
|
||||
* @param[in] coord the coordinates of the agent.
|
||||
@@ -110,37 +184,46 @@ namespace kami {
|
||||
* @returns false if the agent is not placed at the specified
|
||||
* location, otherwise, true.
|
||||
*/
|
||||
virtual bool add_agent(AgentID agent_id, GridCoord1D coord) = 0;
|
||||
virtual AgentID add_agent(
|
||||
AgentID agent_id,
|
||||
const GridCoord1D& coord
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Remove agent from the grid.
|
||||
* @brief Remove agent from the grid.
|
||||
*
|
||||
* @param[in] agent_id the `AgentID` of the agent to remove.
|
||||
*
|
||||
* @returns false if the agent is not removed, otherwise, true.
|
||||
* @returns the `AgentID` of the `Agent` deleted
|
||||
*/
|
||||
[[maybe_unused]] [[maybe_unused]] bool delete_agent(AgentID agent_id);
|
||||
AgentID delete_agent(AgentID agent_id);
|
||||
|
||||
/**
|
||||
* Remove agent from the grid at the specified location
|
||||
* @brief Remove agent from the grid at the specified location
|
||||
*
|
||||
* @param[in] agent_id the `AgentID` of the agent to remove.
|
||||
* @param[in] coord the coordinates of the agent.
|
||||
*
|
||||
* @returns false if the agent is not removed, otherwise, true.
|
||||
* @returns the `AgentID` of the `Agent` deleted
|
||||
*/
|
||||
bool delete_agent(AgentID agent_id, const GridCoord1D &coord);
|
||||
AgentID delete_agent(
|
||||
AgentID agent_id,
|
||||
const GridCoord1D& coord
|
||||
);
|
||||
|
||||
/**
|
||||
* Move an agent to the specified location.
|
||||
* @brief Move an agent to the specified location.
|
||||
*
|
||||
* @param[in] agent_id the `AgentID` of the agent to move.
|
||||
* @param[in] coord the coordinates of the agent.
|
||||
*/
|
||||
bool move_agent(AgentID agent_id, GridCoord1D coord);
|
||||
AgentID move_agent(
|
||||
AgentID agent_id,
|
||||
const GridCoord1D& coord
|
||||
);
|
||||
|
||||
/**
|
||||
* Inquire if the specified location is empty.
|
||||
* @brief Inquire if the specified location is empty.
|
||||
*
|
||||
* @param[in] coord the coordinates of the query.
|
||||
*
|
||||
@@ -150,7 +233,7 @@ namespace kami {
|
||||
[[nodiscard]] bool is_location_empty(const GridCoord1D& coord) const;
|
||||
|
||||
/**
|
||||
* Inquire if the specified location is valid within the grid.
|
||||
* @brief Inquire if the specified location is valid within the grid.
|
||||
*
|
||||
* @param[in] coord the coordinates of the query.
|
||||
*
|
||||
@@ -159,78 +242,98 @@ namespace kami {
|
||||
[[nodiscard]] bool is_location_valid(const GridCoord1D& coord) const;
|
||||
|
||||
/**
|
||||
* Get the location of the specified agent.
|
||||
* @brief Get the location of the specified agent.
|
||||
*
|
||||
* @param[in] agent_id the `AgentID` of the agent in question.
|
||||
*
|
||||
* @return the location of the specified `Agent`
|
||||
*/
|
||||
[[nodiscard]] GridCoord1D get_location_by_agent(AgentID agent_id) const;
|
||||
[[nodiscard]] GridCoord1D get_location_by_agent(const AgentID& agent_id) const;
|
||||
|
||||
/**
|
||||
* Get the contents of the specified location.
|
||||
* @brief Get the contents of the specified location.
|
||||
*
|
||||
* @param[in] coord the coordinates of the query.
|
||||
*
|
||||
* @return a pointer to a `vector` of `AgentID`s. The pointer is to the
|
||||
* @return a pointer to a `set` of `AgentID`s. The pointer is to the
|
||||
* internal copy of the agent list at the location, therefore, any changes
|
||||
* to that object will update the state of the gird. Further, the pointer
|
||||
* should not be deleted when no longer used.
|
||||
*/
|
||||
[[nodiscard]] std::vector<AgentID> *get_location_contents(const GridCoord1D& coord) const;
|
||||
[[nodiscard]] std::shared_ptr<std::set<AgentID>>
|
||||
get_location_contents(const GridCoord1D& coord) const;
|
||||
|
||||
/**
|
||||
* Inquire to whether the grid wraps in the `x` dimension.
|
||||
* @brief Inquire to whether the grid wraps in the `x` dimension.
|
||||
*
|
||||
* @return true if the grid wraps, and false otherwise
|
||||
*/
|
||||
[[maybe_unused]] [[maybe_unused]] [[nodiscard]] bool get_wrap_x() const;
|
||||
[[nodiscard]] bool get_wrap_x() const;
|
||||
|
||||
/**
|
||||
* Return the neighborhood of the specified Agent
|
||||
* @brief Return the neighborhood of the specified Agent
|
||||
*
|
||||
* @param[in] agent_id the `AgentID` of the agent in question
|
||||
* @param[in] include_center should the center-point, occupied by the agent,
|
||||
* be in the list.
|
||||
*
|
||||
* @return a vector of `GridCoord1D` that includes all of the coordinates
|
||||
* @return an `unordered_set` of `GridCoord1D` that includes all of the coordinates
|
||||
* for all adjacent points.
|
||||
*/
|
||||
[[nodiscard]] std::vector<GridCoord1D> get_neighborhood(AgentID agent_id, bool include_center) const;
|
||||
[[nodiscard]] std::shared_ptr<std::unordered_set<GridCoord1D>>
|
||||
get_neighborhood(
|
||||
AgentID agent_id,
|
||||
bool include_center
|
||||
) const;
|
||||
|
||||
/**
|
||||
* Return the neighborhood of the specified location
|
||||
* @brief Return the neighborhood of the specified location
|
||||
*
|
||||
* @param[in] coord the coordinates of the specified location.
|
||||
* @param[in] include_center should the center-point, occupied by the agent,
|
||||
* be in the list.
|
||||
*
|
||||
* @return a vector of `GridCoord1D` that includes all of the coordinates
|
||||
* @return an `unordered_set` of `GridCoord1D` that includes all of the coordinates
|
||||
* for all adjacent points.
|
||||
*/
|
||||
[[nodiscard]] std::vector<GridCoord1D> get_neighborhood(const GridCoord1D& coord, bool include_center) const;
|
||||
[[nodiscard]] std::shared_ptr<std::unordered_set<GridCoord1D>>
|
||||
get_neighborhood(
|
||||
const GridCoord1D& coord,
|
||||
bool include_center
|
||||
) const;
|
||||
|
||||
/**
|
||||
* Get the size of the grid in the `x` dimension.
|
||||
* @brief Get the size of the grid in the `x` dimension.
|
||||
*
|
||||
* @return the length of the grid in the `x` dimension
|
||||
*/
|
||||
[[maybe_unused]] [[nodiscard]] unsigned int get_maximum_x() const;
|
||||
[[nodiscard]] unsigned int get_maximum_x() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* A vector containing the `AgentID`s of all agents assigned to this
|
||||
* @brief Direction coordinates
|
||||
*
|
||||
* @details This can be used for addition to coordinates. Direction
|
||||
* `0` is the first direction clockwise from "vertical." In this
|
||||
* case, it can be on a vertically-oriented column, upwards, or to
|
||||
* the right on a horizontally-oriented column. Then the additional
|
||||
* directions are enumerated clockwise.
|
||||
*/
|
||||
const std::vector<GridCoord1D> directions = {GridCoord1D(1), GridCoord1D(-1)};
|
||||
|
||||
/**
|
||||
* @brief An `unordered_set` containing the `AgentID`s of all agents assigned to this
|
||||
* grid.
|
||||
*/
|
||||
std::vector<AgentID> *_agent_grid;
|
||||
std::unique_ptr<std::unordered_multimap<GridCoord1D, AgentID>> _agent_grid;
|
||||
|
||||
/**
|
||||
* A map containing the grid location of each agent.
|
||||
* @brief A map containing the grid location of each agent.
|
||||
*/
|
||||
std::map<AgentID, GridCoord1D> *_agent_index;
|
||||
std::unique_ptr<std::map<AgentID, GridCoord1D>> _agent_index;
|
||||
|
||||
/**
|
||||
* Automatically adjust a coordinate location for wrapping.
|
||||
* @brief Automatically adjust a coordinate location for wrapping.
|
||||
*
|
||||
* @param[in] coord the coordinates of the specified location.
|
||||
*
|
||||
@@ -245,4 +348,16 @@ namespace kami {
|
||||
|
||||
} // namespace kami
|
||||
|
||||
//! @cond SuppressHashMethod
|
||||
#define KAMI_GRID1D_H
|
||||
namespace std {
|
||||
template<>
|
||||
struct hash<kami::GridCoord1D> {
|
||||
size_t operator()(const kami::GridCoord1D& key) const {
|
||||
return (hash<int>()(key.x()));
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
//! @endcond
|
||||
|
||||
#endif // KAMI_GRID1D_H
|
||||
|
||||
@@ -25,10 +25,18 @@
|
||||
|
||||
#pragma once
|
||||
#ifndef KAMI_GRID2D_H
|
||||
//! @cond SuppressGuard
|
||||
#define KAMI_GRID2D_H
|
||||
//! @endcond
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include <kami/domain.h>
|
||||
@@ -37,65 +45,184 @@
|
||||
|
||||
namespace kami {
|
||||
|
||||
/**
|
||||
* Two-dimensional coordinates
|
||||
*/
|
||||
class LIBKAMI_EXPORT GridCoord2D : public GridCoord {
|
||||
/**
|
||||
* @brief Two-dimensional coordinates
|
||||
*/
|
||||
class LIBKAMI_EXPORT GridCoord2D
|
||||
: public GridCoord {
|
||||
public:
|
||||
/**
|
||||
* Constructor for two-dimensional coordinates
|
||||
* @brief Constructor for two-dimensional coordinates
|
||||
*/
|
||||
GridCoord2D(int x_coord, int y_coord)
|
||||
: _x_coord(x_coord), _y_coord(y_coord){};
|
||||
GridCoord2D(
|
||||
int x_coord,
|
||||
int y_coord
|
||||
);
|
||||
|
||||
/**
|
||||
* Get the coordinate in the first dimension or `x`.
|
||||
* @brief Get the coordinate in the first dimension or `x`.
|
||||
*/
|
||||
[[nodiscard]] int get_x_location() const;
|
||||
[[nodiscard]] int x() const;
|
||||
|
||||
/**
|
||||
* Get the coordinate in the second dimension or `y`.
|
||||
* @brief Get the coordinate in the second dimension or `y`.
|
||||
*/
|
||||
[[nodiscard]] int get_y_location() const;
|
||||
[[nodiscard]] int y() const;
|
||||
|
||||
/**
|
||||
* Convert the coordinate to a human-readable string.
|
||||
* @brief Convert the coordinate to a human-readable string.
|
||||
*
|
||||
* @return a human-readable form of the `Coord` as `std::string`.
|
||||
*/
|
||||
[[nodiscard]] std::string to_string() const override;
|
||||
|
||||
/**
|
||||
* Test if two coordinates are equal
|
||||
* @brief Find the distance between two points
|
||||
*
|
||||
* @details Find the distance between two points using the
|
||||
* specified metric.
|
||||
*
|
||||
* However, the coordinate class is not aware of the
|
||||
* properties of the `Grid2D` it is operating on. Accordingly,
|
||||
* if the direct path is measured, without accounting for
|
||||
* and toroidal wrapping of the underlying `Grid2D`.
|
||||
*
|
||||
* @param p the point to measure the distance to
|
||||
*
|
||||
* @returns the distance as a `double`
|
||||
*/
|
||||
friend bool operator==(const GridCoord2D &, const GridCoord2D &);
|
||||
double distance(std::shared_ptr<Coord>& p) const override;
|
||||
|
||||
/**
|
||||
* Test if two coordinates are not equal
|
||||
* @brief Find the distance between two points
|
||||
*
|
||||
* @details Find the distance between two points using the
|
||||
* specified metric. There are three options provided by
|
||||
* the `GridDistanceType` class.
|
||||
*
|
||||
* However, the coordinate class is not aware of the
|
||||
* properties of the `Grid2D` it is operating on. Accordingly,
|
||||
* if the direct path is measured, without accounting for
|
||||
* and toroidal wrapping of the underlying `Grid2D`.
|
||||
*
|
||||
* @param p the point to measure the distance to
|
||||
* @param distance_type specify the distance type
|
||||
*
|
||||
* @returns the distance as a `double`
|
||||
*/
|
||||
friend bool operator!=(const GridCoord2D &, const GridCoord2D &);
|
||||
double
|
||||
distance(
|
||||
std::shared_ptr<GridCoord2D>& p,
|
||||
GridDistanceType distance_type = GridDistanceType::Euclidean
|
||||
) const;
|
||||
|
||||
/**
|
||||
* Output a given coordinate to the specified stream
|
||||
* @brief Test if two coordinates are equal
|
||||
*/
|
||||
friend std::ostream &operator<<(std::ostream &, const GridCoord2D &);
|
||||
friend bool operator==(
|
||||
const GridCoord2D&,
|
||||
const GridCoord2D&
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Test if two coordinates are not equal
|
||||
*/
|
||||
friend bool operator!=(
|
||||
const GridCoord2D&,
|
||||
const GridCoord2D&
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Output a given coordinate to the specified stream
|
||||
*/
|
||||
friend std::ostream& operator<<(
|
||||
std::ostream&,
|
||||
const GridCoord2D&
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Add two coordinates together
|
||||
*/
|
||||
inline friend GridCoord2D operator+(
|
||||
const GridCoord2D& lhs,
|
||||
const GridCoord2D& rhs
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Subtract one coordinate from another
|
||||
*/
|
||||
inline friend GridCoord2D operator-(
|
||||
const GridCoord2D& lhs,
|
||||
const GridCoord2D& rhs
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Multiply a coordinate by a scalar
|
||||
*
|
||||
* @details If any component of the resulting value is not a whole number, it is
|
||||
* truncated following the same rules as `int`.
|
||||
*/
|
||||
inline friend GridCoord2D operator*(
|
||||
const GridCoord2D& lhs,
|
||||
const double rhs
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Multiply a coordinate by a scalar
|
||||
*
|
||||
* @details If any component of the resulting value is not a whole number, it is
|
||||
* truncated following the same rules as `int`.
|
||||
*/
|
||||
inline friend GridCoord2D operator*(
|
||||
const double lhs,
|
||||
const GridCoord2D& rhs
|
||||
);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Find the distance between two points using the Chebyshev metric
|
||||
*
|
||||
* @param p the point to measure the distance to
|
||||
*
|
||||
* @returns the distance as a `double`
|
||||
*/
|
||||
inline double distance_chebyshev(std::shared_ptr<GridCoord2D>& p) const;
|
||||
|
||||
/**
|
||||
* @brief Find the distance between two points using the Euclidean metric
|
||||
*
|
||||
* @param p the point to measure the distance to
|
||||
*
|
||||
* @returns the distance as a `double`
|
||||
*/
|
||||
inline double distance_euclidean(std::shared_ptr<GridCoord2D>& p) const;
|
||||
|
||||
/**
|
||||
* @brief Find the distance between two points using the Manhattan metric
|
||||
*
|
||||
* @param p the point to measure the distance to
|
||||
*
|
||||
* @returns the distance as a `double`
|
||||
*/
|
||||
inline double distance_manhattan(std::shared_ptr<GridCoord2D>& p) const;
|
||||
|
||||
private:
|
||||
int _x_coord, _y_coord;
|
||||
};
|
||||
|
||||
/**
|
||||
* A two-dimensional grid where each cell may contain agents
|
||||
*
|
||||
* The grid is linear and may wrap around in its only dimension.
|
||||
*
|
||||
* @see `MultiGrid2D`
|
||||
* @see `SoloGrid2D`
|
||||
*/
|
||||
class LIBKAMI_EXPORT Grid2D : public GridDomain {
|
||||
/**
|
||||
* @brief A two-dimensional grid where each cell may contain agents
|
||||
*
|
||||
* @details The grid is linear and may wrap around in its only dimension.
|
||||
*
|
||||
* @see `MultiGrid2D`
|
||||
* @see `SoloGrid2D`
|
||||
*/
|
||||
class LIBKAMI_EXPORT Grid2D
|
||||
: public GridDomain {
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* @brief Constructor
|
||||
*
|
||||
* @param[in] maximum_x the length of the grid in the first dimension
|
||||
* @param[in] maximum_y the length of the grid in the second dimension
|
||||
@@ -104,16 +231,15 @@ namespace kami {
|
||||
* @param[in] wrap_y should the grid wrap around on itself in the second
|
||||
* dimension
|
||||
*/
|
||||
Grid2D(unsigned int maximum_x, unsigned int maximum_y, bool wrap_x = false,
|
||||
bool wrap_y = false);
|
||||
explicit Grid2D(
|
||||
unsigned int maximum_x,
|
||||
unsigned int maximum_y,
|
||||
bool wrap_x = false,
|
||||
bool wrap_y = false
|
||||
);
|
||||
|
||||
/**
|
||||
* Deconstructor
|
||||
*/
|
||||
virtual ~Grid2D();
|
||||
|
||||
/**
|
||||
* Place agent on the grid at the specified location.
|
||||
* @brief Place agent on the grid at the specified location.
|
||||
*
|
||||
* @param[in] agent_id the `AgentID` of the agent to add.
|
||||
* @param[in] coord the coordinates of the agent.
|
||||
@@ -121,37 +247,46 @@ namespace kami {
|
||||
* @returns false if the agent is not placed at the specified
|
||||
* location, otherwise, true.
|
||||
*/
|
||||
virtual bool add_agent(AgentID agent_id, GridCoord2D coord) = 0;
|
||||
virtual AgentID add_agent(
|
||||
AgentID agent_id,
|
||||
const GridCoord2D& coord
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Remove agent from the grid.
|
||||
* @brief Remove agent from the grid.
|
||||
*
|
||||
* @param[in] agent_id the `AgentID` of the agent to remove.
|
||||
*
|
||||
* @returns false if the agent is not removed, otherwise, true.
|
||||
*/
|
||||
[[maybe_unused]] bool delete_agent(AgentID agent_id);
|
||||
AgentID delete_agent(AgentID agent_id);
|
||||
|
||||
/**
|
||||
* Remove agent from the grid at the specified location
|
||||
* @brief Remove agent from the grid at the specified location
|
||||
*
|
||||
* @param[in] agent_id the `AgentID` of the agent to remove.
|
||||
* @param[in] coord the coordinates of the agent.
|
||||
*
|
||||
* @returns false if the agent is not removed, otherwise, true.
|
||||
*/
|
||||
bool delete_agent(AgentID agent_id, const GridCoord2D &coord);
|
||||
AgentID delete_agent(
|
||||
AgentID agent_id,
|
||||
const GridCoord2D& coord
|
||||
);
|
||||
|
||||
/**
|
||||
* Move an agent to the specified location.
|
||||
* @brief Move an agent to the specified location.
|
||||
*
|
||||
* @param[in] agent_id the `AgentID` of the agent to move.
|
||||
* @param[in] coord the coordinates of the agent.
|
||||
*/
|
||||
bool move_agent(AgentID agent_id, GridCoord2D coord);
|
||||
AgentID move_agent(
|
||||
AgentID agent_id,
|
||||
const GridCoord2D& coord
|
||||
);
|
||||
|
||||
/**
|
||||
* Inquire if the specified location is empty.
|
||||
* @brief Inquire if the specified location is empty.
|
||||
*
|
||||
* @param[in] coord the coordinates of the query.
|
||||
*
|
||||
@@ -161,7 +296,7 @@ namespace kami {
|
||||
[[nodiscard]] bool is_location_empty(const GridCoord2D& coord) const;
|
||||
|
||||
/**
|
||||
* Inquire if the specified location is valid within the grid.
|
||||
* @brief Inquire if the specified location is valid within the grid.
|
||||
*
|
||||
* @param[in] coord the coordinates of the query.
|
||||
*
|
||||
@@ -169,99 +304,132 @@ namespace kami {
|
||||
*/
|
||||
[[nodiscard]] bool is_location_valid(const GridCoord2D& coord) const;
|
||||
|
||||
/**
|
||||
* Get the location of the specified agent.
|
||||
virtual /**
|
||||
* @brief Get the location of the specified agent.
|
||||
*
|
||||
* @param[in] agent_id the `AgentID` of the agent in question.
|
||||
*
|
||||
* @return the location of the specified `Agent`
|
||||
*/
|
||||
[[nodiscard]] GridCoord2D get_location_by_agent(AgentID agent_id) const;
|
||||
GridCoord2D get_location_by_agent(const AgentID& agent_id) const;
|
||||
|
||||
/**
|
||||
* Get the contents of the specified location.
|
||||
* @brief Get the contents of the specified location.
|
||||
*
|
||||
* @param[in] coord the coordinates of the query.
|
||||
*
|
||||
* @return a pointer to a `vector` of `AgentID`s. The pointer is to the
|
||||
* @return a pointer to a `set` of `AgentID`s. The pointer is to the
|
||||
* internal copy of the agent list at the location, therefore, any changes
|
||||
* to that object will update the state of the gird. Further, the pointer
|
||||
* should not be deleted when no longer used.
|
||||
*/
|
||||
[[nodiscard]] std::vector<AgentID> *get_location_contents(const GridCoord2D& coord) const;
|
||||
[[nodiscard]] std::shared_ptr<std::set<AgentID>>
|
||||
get_location_contents(const GridCoord2D& coord) const;
|
||||
|
||||
/**
|
||||
* Inquire to whether the grid wraps in the `x` dimension.
|
||||
* @brief Inquire to whether the grid wraps in the `x` dimension.
|
||||
*
|
||||
* @return true if the grid wraps, and false otherwise
|
||||
*/
|
||||
[[maybe_unused]] [[nodiscard]] bool get_wrap_x() const;
|
||||
[[nodiscard]] bool get_wrap_x() const;
|
||||
|
||||
/**
|
||||
* Inquire to whether the grid wraps in the `y` dimension.
|
||||
* @brief Inquire to whether the grid wraps in the `y` dimension.
|
||||
*
|
||||
* @return true if the grid wraps, and false otherwise
|
||||
*/
|
||||
[[maybe_unused]] [[nodiscard]] bool get_wrap_y() const;
|
||||
[[nodiscard]] bool get_wrap_y() const;
|
||||
|
||||
/**
|
||||
* Return the neighborhood of the specified Agent
|
||||
virtual /**
|
||||
* @brief Return the neighborhood of the specified Agent
|
||||
*
|
||||
* @param[in] agent_id the `AgentID` of the agent in question.
|
||||
* @param[in] neighborhood_type the neighborhood type.
|
||||
* @param[in] include_center should the center-point, occupied by the agent,
|
||||
* be in the list.
|
||||
*
|
||||
* @return a vector of `GridCoord1D` that includes all of the coordinates
|
||||
* @return a set of `GridCoord2D` that includes all of the coordinates
|
||||
* for all adjacent points.
|
||||
*
|
||||
* @see `NeighborhoodType`
|
||||
*/
|
||||
[[nodiscard]] std::vector<GridCoord2D> get_neighborhood(AgentID agent_id, GridNeighborhoodType neighborhood_type, bool include_center) const;
|
||||
std::shared_ptr<std::unordered_set<GridCoord2D>>
|
||||
get_neighborhood(
|
||||
AgentID agent_id,
|
||||
bool include_center,
|
||||
GridNeighborhoodType neighborhood_type
|
||||
) const;
|
||||
|
||||
/**
|
||||
* Return the neighborhood of the specified location
|
||||
* @brief Return the neighborhood of the specified location
|
||||
*
|
||||
* @param[in] coord the coordinates of the specified location.
|
||||
* @param[in] neighborhood_type the neighborhood type.
|
||||
* @param[in] include_center should the center-point, occupied by the agent,
|
||||
* be in the list.
|
||||
*
|
||||
* @return a vector of `GridCoord1D` that includes all of the coordinates
|
||||
* @return a set of `GridCoord2D` that includes all of the coordinates
|
||||
* for all adjacent points.
|
||||
*
|
||||
* @see `NeighborhoodType`
|
||||
*/
|
||||
[[nodiscard]] std::vector<GridCoord2D> get_neighborhood(const GridCoord2D& coord, GridNeighborhoodType neighborhood_type, bool include_center) const;
|
||||
[[nodiscard]] std::shared_ptr<std::unordered_set<GridCoord2D>>
|
||||
get_neighborhood(
|
||||
const GridCoord2D& coord,
|
||||
bool include_center,
|
||||
GridNeighborhoodType neighborhood_type
|
||||
) const;
|
||||
|
||||
/**
|
||||
* Get the size of the grid in the `x` dimension.
|
||||
* @brief Get the size of the grid in the `x` dimension.
|
||||
*
|
||||
* @return the length of the grid in the `x` dimension
|
||||
*/
|
||||
[[maybe_unused]] [[nodiscard]] unsigned int get_maximum_x() const;
|
||||
[[nodiscard]] unsigned int get_maximum_x() const;
|
||||
|
||||
/**
|
||||
* Get the size of the grid in the `y` dimension.
|
||||
* @brief Get the size of the grid in the `y` dimension.
|
||||
*
|
||||
* @return the length of the grid in the `xy dimension
|
||||
*/
|
||||
[[maybe_unused]] [[nodiscard]] unsigned int get_maximum_y() const;
|
||||
[[nodiscard]] unsigned int get_maximum_y() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* A vector containing the `AgentID`s of all agents assigned to this
|
||||
* @brief von Neumann neighborhood coordinates
|
||||
*
|
||||
* @details This can be used for addition to coordinates. Direction
|
||||
* `0` is the first direction clockwise from "vertical." Then the additional
|
||||
* directions are enumerated clockwise.
|
||||
*/
|
||||
const std::vector<GridCoord2D> directions_vonneumann = {GridCoord2D(0, 1), GridCoord2D(1, 0),
|
||||
GridCoord2D(0, -1), GridCoord2D(-1, 0)};
|
||||
|
||||
/**
|
||||
* @brief Moore neighborhood coordinates
|
||||
*
|
||||
* @details This can be used for addition to coordinates. Direction
|
||||
* `0` is the first direction clockwise from "vertical." Then the additional
|
||||
* directions are enumerated clockwise.
|
||||
*/
|
||||
const std::vector<GridCoord2D> directions_moore = {GridCoord2D(0, 1), GridCoord2D(1, 1),
|
||||
GridCoord2D(1, 0), GridCoord2D(1, -1),
|
||||
GridCoord2D(0, -1), GridCoord2D(-1, -1),
|
||||
GridCoord2D(-1, 0), GridCoord2D(-1, 1)};
|
||||
|
||||
/**
|
||||
* @brief A map containing the `AgentID`s of all agents assigned to this
|
||||
* grid.
|
||||
*/
|
||||
std::vector<AgentID> **_agent_grid;
|
||||
std::unique_ptr<std::unordered_multimap<GridCoord2D, AgentID>> _agent_grid;
|
||||
|
||||
/**
|
||||
* A map containing the grid location of each agent.
|
||||
* @brief A map containing the grid location of each agent.
|
||||
*/
|
||||
std::map<AgentID, GridCoord2D> *_agent_index;
|
||||
std::unique_ptr<std::map<AgentID, GridCoord2D>> _agent_index;
|
||||
|
||||
/**
|
||||
* Automatically adjust a coordinate location for wrapping.
|
||||
* @brief Automatically adjust a coordinate location for wrapping.
|
||||
*
|
||||
* @param[in] coord the coordinates of the specified location.
|
||||
*
|
||||
@@ -276,4 +444,15 @@ namespace kami {
|
||||
|
||||
} // namespace kami
|
||||
|
||||
//! @cond SuppressHashMethod
|
||||
namespace std {
|
||||
template<>
|
||||
struct hash<kami::GridCoord2D> {
|
||||
size_t operator()(const kami::GridCoord2D& key) const {
|
||||
return ((hash<int>()(key.x()) ^ (hash<int>()(key.y()) << 1)) >> 1);
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
//! @endcond
|
||||
|
||||
#endif // KAMI_GRID2D_H
|
||||
|
||||
@@ -25,9 +25,65 @@
|
||||
|
||||
#pragma once
|
||||
#ifndef KAMI_KAMI_H
|
||||
//! @cond SuppressGuard
|
||||
#define KAMI_KAMI_H
|
||||
//! @endcond
|
||||
|
||||
#include <semver.hpp>
|
||||
|
||||
#include <kami/KAMI_EXPORT.h>
|
||||
#include <kami/config.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
// Forward declarations to clean up a lot of include-file madness
|
||||
class Agent;
|
||||
|
||||
class AgentID;
|
||||
|
||||
class Domain;
|
||||
|
||||
class Model;
|
||||
|
||||
class Population;
|
||||
|
||||
class ReporterAgent;
|
||||
|
||||
class ReporterModel;
|
||||
|
||||
class Scheduler;
|
||||
|
||||
/**
|
||||
* @brief Get the current version of Kami
|
||||
*
|
||||
* @return a `semver::version` object containing version information
|
||||
*/
|
||||
inline semver::version get_version() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A catalog of handy constants, mostly useful for seeding
|
||||
* a random number generator
|
||||
*/
|
||||
class Constants {
|
||||
public:
|
||||
/**
|
||||
* @brief Life, the Universe, and Everything!
|
||||
*/
|
||||
static constexpr auto ADAMS_CONSTANT = 42u;
|
||||
|
||||
/**
|
||||
* @brief Jenny, I've got your number
|
||||
*/
|
||||
static constexpr auto JENNYS_NUMBER = 8675309u;
|
||||
|
||||
/**
|
||||
* @brief $(7^(e - 1/e) - 9) * pi^2$
|
||||
*/
|
||||
static constexpr auto JENNYS_CONSTANT = 867.530901981;
|
||||
};
|
||||
|
||||
} // namespace kami
|
||||
|
||||
#endif // KAMI_KAMI_H
|
||||
|
||||
@@ -25,43 +25,105 @@
|
||||
|
||||
#pragma once
|
||||
#ifndef KAMI_MODEL_H
|
||||
//! @cond SuppressGuard
|
||||
#define KAMI_MODEL_H
|
||||
//! @endcond
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <memory>
|
||||
|
||||
#include <kami/domain.h>
|
||||
#include <kami/kami.h>
|
||||
#include <kami/population.h>
|
||||
#include <kami/scheduler.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
/**
|
||||
* An abstract for generic models
|
||||
*/
|
||||
class LIBKAMI_EXPORT Model {
|
||||
/**
|
||||
* @brief An abstract for generic models
|
||||
*
|
||||
* @see `ReporterModel`
|
||||
*/
|
||||
class LIBKAMI_EXPORT Model
|
||||
: public std::enable_shared_from_this<Model> {
|
||||
|
||||
public:
|
||||
/**
|
||||
* Get a reference to an `Agent` by `AgentID`
|
||||
* @brief Get the `Domain` associated with this model
|
||||
*
|
||||
* @param[in] agent_id the `AgentID` to search for.
|
||||
*
|
||||
* @return a reference to the desired `Agent` or `nullptr` if not found.
|
||||
* @returns a shared pointer to the `Domain`
|
||||
*/
|
||||
[[nodiscard]] virtual Agent *get_agent_by_id(AgentID agent_id) const = 0;
|
||||
std::shared_ptr<Domain> get_domain();
|
||||
|
||||
/**
|
||||
* Execute a fixed number of time-steps for the model.
|
||||
* @brief Add a `Domain` to this scheduler
|
||||
*
|
||||
* This function should execute a fixed number of time-steps for the model.
|
||||
* @details This method will associate a model with the
|
||||
* scheduler.
|
||||
*
|
||||
* @param[in] n the number of time steps to execute.
|
||||
* @returns a shared pointer to the `Domain`
|
||||
*/
|
||||
[[maybe_unused]] virtual void run(unsigned int n) = 0;
|
||||
std::shared_ptr<Domain> set_domain(std::shared_ptr<Domain> domain);
|
||||
|
||||
/**
|
||||
* Execute a single time-step for the model.
|
||||
* @brief Get the `Population` associated with this model
|
||||
*
|
||||
* This function should step the model instance. Any activities that the
|
||||
* model should perform as part of its time step should be in this function.
|
||||
* @returns a shared pointer to the `Population`
|
||||
*/
|
||||
virtual void step() = 0;
|
||||
std::shared_ptr<Population> get_population();
|
||||
|
||||
/**
|
||||
* @brief Add a `Model` to this scheduler
|
||||
*
|
||||
* @details This method will associate a model with the
|
||||
* scheduler.
|
||||
*
|
||||
* @returns a shared pointer to the `Population`
|
||||
*/
|
||||
std::shared_ptr<Population> set_population(std::shared_ptr<Population> population);
|
||||
|
||||
/**
|
||||
* @brief Get the `Scheduler` associated with this model
|
||||
*
|
||||
* @returns a shared pointer to the `Scheduler`
|
||||
*/
|
||||
std::shared_ptr<Scheduler> get_scheduler();
|
||||
|
||||
/**
|
||||
* @brief Add a `Model` to this scheduler
|
||||
*
|
||||
* @details This method will associate a model with the
|
||||
* scheduler.
|
||||
*
|
||||
* @returns a shared pointer to the `Scheduler`
|
||||
*/
|
||||
std::shared_ptr<Scheduler> set_scheduler(std::shared_ptr<Scheduler> scheduler);
|
||||
|
||||
/**
|
||||
* @brief Execute a single time step of the model
|
||||
*
|
||||
* @details This method will collect all the `Agent`s in the `Population` associated
|
||||
* with model and pass them to the associated `Scheduler` for stepping.
|
||||
*
|
||||
* @returns a shared pointer to the model instance
|
||||
*/
|
||||
virtual std::shared_ptr<Model> step();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Reference copy of the `Domain`
|
||||
*/
|
||||
std::shared_ptr<Domain> _domain = nullptr;
|
||||
|
||||
/**
|
||||
* @brief Reference copy of the `Population`
|
||||
*/
|
||||
std::shared_ptr<Population> _pop = nullptr;
|
||||
|
||||
/**
|
||||
* @brief Reference copy of the `Scheduler`
|
||||
*/
|
||||
std::shared_ptr<Scheduler> _sched = nullptr;
|
||||
|
||||
};
|
||||
|
||||
} // namespace kami
|
||||
|
||||
@@ -25,7 +25,9 @@
|
||||
|
||||
#pragma once
|
||||
#ifndef KAMI_MULTIGRID1D_H
|
||||
//! @cond SuppressGuard
|
||||
#define KAMI_MULTIGRID1D_H
|
||||
//! @endcond
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/domain.h>
|
||||
@@ -35,24 +37,30 @@
|
||||
|
||||
namespace kami {
|
||||
|
||||
/**
|
||||
* @brief A one-dimensional grid where each cell may contain multiple agents
|
||||
*
|
||||
* @details The grid is linear and may wrap around in its only dimension.
|
||||
*/
|
||||
class LIBKAMI_EXPORT MultiGrid1D : public Grid1D {
|
||||
/**
|
||||
* @brief A one-dimensional grid where each cell may contain multiple agents
|
||||
*
|
||||
* @details The grid is linear and may wrap around in its only dimension.
|
||||
*
|
||||
* @see `Grid1D`
|
||||
* @see `SoloGrid1D`
|
||||
*/
|
||||
class LIBKAMI_EXPORT MultiGrid1D
|
||||
: public Grid1D {
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* @brief Constructor
|
||||
*
|
||||
* @param[in] maximum_x the length of the grid.
|
||||
* @param[in] wrap_x should the grid wrap around on itself.
|
||||
*/
|
||||
MultiGrid1D(unsigned int maximum_x, bool wrap_x)
|
||||
: Grid1D(maximum_x, wrap_x) {}
|
||||
MultiGrid1D(
|
||||
unsigned int maximum_x,
|
||||
bool wrap_x
|
||||
);
|
||||
|
||||
/**
|
||||
* Place agent on the grid at the specified location.
|
||||
* @brief Place agent on the grid at the specified location.
|
||||
*
|
||||
* @param[in] agent_id the `AgentID` of the agent to add.
|
||||
* @param[in] coord the coordinates of the agent.
|
||||
@@ -60,7 +68,10 @@ namespace kami {
|
||||
* @returns false if the agent is not placed at the specified
|
||||
* location, otherwise, true
|
||||
*/
|
||||
bool add_agent(AgentID agent_id, GridCoord1D coord) override;
|
||||
AgentID add_agent(
|
||||
AgentID agent_id,
|
||||
const GridCoord1D& coord
|
||||
) override;
|
||||
};
|
||||
|
||||
} // namespace kami
|
||||
|
||||
@@ -25,7 +25,9 @@
|
||||
|
||||
#pragma once
|
||||
#ifndef KAMI_MULTIGRID2D_H
|
||||
//! @cond SuppressGuard
|
||||
#define KAMI_MULTIGRID2D_H
|
||||
//! @endcond
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/domain.h>
|
||||
@@ -35,18 +37,19 @@
|
||||
|
||||
namespace kami {
|
||||
|
||||
/**
|
||||
* @brief A two-dimensional grid where each cell may contain multiple agents
|
||||
*
|
||||
* @details The grid is linear and may wrap around in its only dimension.
|
||||
*
|
||||
* @see `Grid2D`
|
||||
* @see `SoloGrid2D`
|
||||
*/
|
||||
class LIBKAMI_EXPORT MultiGrid2D : public Grid2D {
|
||||
/**
|
||||
* @brief A two-dimensional grid where each cell may contain multiple agents
|
||||
*
|
||||
* @details The grid is linear and may wrap around in either dimension.
|
||||
*
|
||||
* @see `Grid2D`
|
||||
* @see `SoloGrid2D`
|
||||
*/
|
||||
class LIBKAMI_EXPORT MultiGrid2D
|
||||
: public Grid2D {
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* @brief Constructor
|
||||
*
|
||||
* @param[in] maximum_x the length of the grid in the first dimension
|
||||
* @param[in] maximum_y the length of the grid in the second dimension
|
||||
@@ -55,19 +58,25 @@ namespace kami {
|
||||
* @param[in] wrap_y should the grid wrap around on itself in the second
|
||||
* dimension
|
||||
*/
|
||||
MultiGrid2D(unsigned int maximum_x, unsigned int maximum_y, bool wrap_x, bool wrap_y)
|
||||
: Grid2D(maximum_x, maximum_y, wrap_x, wrap_y) {};
|
||||
MultiGrid2D(
|
||||
unsigned int maximum_x,
|
||||
unsigned int maximum_y,
|
||||
bool wrap_x,
|
||||
bool wrap_y
|
||||
);
|
||||
|
||||
/**
|
||||
* Place agent on the grid at the specified location.
|
||||
* @brief Place agent on the grid at the specified location.
|
||||
*
|
||||
* @param[in] agent_id the `AgentID` of the agent to add.
|
||||
* @param[in] coord the coordinates of the agent.
|
||||
*
|
||||
* @returns false if the agent is not placed at the specified
|
||||
* location, otherwise, true
|
||||
* @returns the `AgentID` of the agent added
|
||||
*/
|
||||
bool add_agent(AgentID agent_id, GridCoord2D coord) override;
|
||||
AgentID add_agent(
|
||||
AgentID agent_id,
|
||||
const GridCoord2D& coord
|
||||
) override;
|
||||
};
|
||||
|
||||
} // namespace kami
|
||||
|
||||
92
include/kami/population.h
Normal file
92
include/kami/population.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/*-
|
||||
* Copyright (c) 2022 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef KAMI_POPULATION_H
|
||||
//! @cond SuppressGuard
|
||||
#define KAMI_POPULATION_H
|
||||
//! @endcond
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/kami.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
/**
|
||||
* @brief An abstract for generic models
|
||||
*/
|
||||
class LIBKAMI_EXPORT Population {
|
||||
public:
|
||||
/**
|
||||
* @brief Get a reference to an `Agent` by `AgentID`
|
||||
*
|
||||
* @param[in] agent_id the `AgentID` to search for.
|
||||
*
|
||||
* @return a reference to the desired `Agent` or nothing is not found
|
||||
*/
|
||||
[[nodiscard]] std::shared_ptr<Agent> get_agent_by_id(AgentID agent_id) const;
|
||||
|
||||
/**
|
||||
* @brief Add an Agent to the Population.
|
||||
*
|
||||
* @param agent The Agent to add.
|
||||
*
|
||||
* @returns the ID of the agent added
|
||||
*/
|
||||
AgentID add_agent(const std::shared_ptr<Agent>& agent) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Remove an Agent from the Population.
|
||||
*
|
||||
* @param agent_id The AgentID of the agent to remove.
|
||||
*
|
||||
* @returns a shared pointer to the Agent deleted
|
||||
*/
|
||||
std::shared_ptr<Agent> delete_agent(AgentID agent_id);
|
||||
|
||||
/**
|
||||
* @brief Returns the agent list.
|
||||
*
|
||||
* @returns a `std::vector` of all the `AgentID`'s in the `Population`
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<std::vector<AgentID>> get_agent_list() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief A mapping of `AgentID` to `Agent` pointers
|
||||
*
|
||||
* @details This is the mapping of all `AgentID`s to
|
||||
* pointers to the corresponding `Agent` in this population.
|
||||
* This is left exposed as protected should any subclass
|
||||
* wish to manipulate this mapping directly.
|
||||
*/
|
||||
std::map<kami::AgentID, std::shared_ptr<Agent>> _agent_map;
|
||||
};
|
||||
} // namespace kami
|
||||
|
||||
#endif // KAMI_POPULATION_H
|
||||
46
include/kami/position.h
Normal file
46
include/kami/position.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*-
|
||||
* Copyright (c) 2020 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef KAMI_POSITION_H
|
||||
//! @cond SuppressGuard
|
||||
#define KAMI_POSITION_H
|
||||
//! @endcond
|
||||
|
||||
#include <variant>
|
||||
|
||||
#include <kami/grid1d.h>
|
||||
#include <kami/grid2d.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
typedef std::variant<
|
||||
GridCoord1D,
|
||||
GridCoord2D
|
||||
> Position;
|
||||
|
||||
}
|
||||
|
||||
#endif //KAMI_POSITION_H
|
||||
@@ -25,70 +25,102 @@
|
||||
|
||||
#pragma once
|
||||
#ifndef KAMI_RANDOM_H
|
||||
//! @cond SuppressGuard
|
||||
#define KAMI_RANDOM_H
|
||||
//! @endcond
|
||||
|
||||
#include <kami/kami.h>
|
||||
#include <kami/sequential.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
namespace kami {
|
||||
#include <kami/kami.h>
|
||||
#include <kami/scheduler.h>
|
||||
#include <kami/sequential.h>
|
||||
|
||||
/**
|
||||
* Will execute all agent steps in a random order.
|
||||
*
|
||||
* A random scheduler will iterate over the agents assigned
|
||||
* to the scheduler and call their `step()` function in a random order.
|
||||
* That order should be different for each subsequent call to `step()`,
|
||||
* but is not guaranteed not to repeat.
|
||||
*
|
||||
* @note First create a Model for the scheduler to live in.
|
||||
*/
|
||||
class LIBKAMI_EXPORT RandomScheduler : public SequentialScheduler {
|
||||
namespace kami {
|
||||
/**
|
||||
* @brief Will execute all agent steps in a random order.
|
||||
*
|
||||
* @details A random scheduler will iterate over the agents assigned
|
||||
* to the scheduler and call their `step()` function in a random order.
|
||||
* That order should be different for each subsequent call to `step()`,
|
||||
* but is not guaranteed not to repeat.
|
||||
*/
|
||||
class LIBKAMI_EXPORT RandomScheduler
|
||||
: public SequentialScheduler,
|
||||
std::enable_shared_from_this<RandomScheduler> {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor.
|
||||
*/
|
||||
RandomScheduler() = default;
|
||||
|
||||
/**
|
||||
* @brief Constructor.
|
||||
*
|
||||
* @details The `model` parameter is used by the scheduler to get
|
||||
* access to an `Agent`. The `Model` is presumed to maintain a master
|
||||
* list of all `Agent`s in the `Model` and the `Model` can be queried for
|
||||
* a reference to any particular `Agent` at `step()` time.
|
||||
*
|
||||
* @param model [in] A reference to the model the scheduler is timing.
|
||||
* @param rng [in] A uniform random number generator of type
|
||||
* `std::mt19937`, used as the source of randomness.
|
||||
*/
|
||||
RandomScheduler(Model *model, std::shared_ptr<std::mt19937> rng);
|
||||
explicit RandomScheduler(std::shared_ptr<std::mt19937> rng);
|
||||
|
||||
/**
|
||||
* @brief Execute a single time step.
|
||||
*
|
||||
* @details This method will randomize the list of Agents in the scheduler's
|
||||
* internal queue and then execute the `Agent::step()` method for every
|
||||
* Agent assigned to this scheduler in the randomized order.
|
||||
* @details This method will randomize the list of Agents provided
|
||||
* then execute the `Agent::step()` method for every Agent listed.
|
||||
*
|
||||
* @param model a reference copy of the model
|
||||
* @param agent_list list of agents to execute the step
|
||||
*
|
||||
* @returns returns vector of agents successfully stepped
|
||||
*/
|
||||
void step() override;
|
||||
std::unique_ptr<std::vector<AgentID>>
|
||||
step(
|
||||
std::shared_ptr<Model> model,
|
||||
std::unique_ptr<std::vector<AgentID>> agent_list
|
||||
) override;
|
||||
|
||||
/**
|
||||
* Set the random number generator used to randomize the order of agent
|
||||
* @brief Execute a single time step for a `ReporterModel`
|
||||
*
|
||||
* @details This method will randomize the list of Agents provided
|
||||
* then execute the `Agent::step()` method for every Agent listed.
|
||||
*
|
||||
* @param model a reference copy of the `ReporterModel`
|
||||
* @param agent_list list of agents to execute the step
|
||||
*
|
||||
* @returns returns vector of agents successfully stepped
|
||||
*/
|
||||
std::unique_ptr<std::vector<AgentID>>
|
||||
step(
|
||||
std::shared_ptr<ReporterModel> model,
|
||||
std::unique_ptr<std::vector<AgentID>> agent_list
|
||||
) override;
|
||||
|
||||
/**
|
||||
* @brief Set the RNG
|
||||
*
|
||||
* @details Set the random number generator used to randomize the order of agent
|
||||
* stepping.
|
||||
*
|
||||
* @param rng [in] A uniform random number generator of type `std::mt19937`,
|
||||
* used as the source of randomness.
|
||||
*
|
||||
* @returns a shared pointer to the random number generator
|
||||
*/
|
||||
void set_rng(std::shared_ptr<std::mt19937> rng);
|
||||
std::shared_ptr<std::mt19937> set_rng(std::shared_ptr<std::mt19937> rng);
|
||||
|
||||
/**
|
||||
* Get a reference to the random number generator used to randomize
|
||||
* @brief Get the RNG
|
||||
*
|
||||
* @details Get a reference to the random number generator used to randomize
|
||||
* the order of agent stepping.
|
||||
*/
|
||||
[[maybe_unused]] std::shared_ptr<std::mt19937> get_rng();
|
||||
std::shared_ptr<std::mt19937> get_rng();
|
||||
|
||||
private:
|
||||
std::shared_ptr<std::mt19937> _rng;
|
||||
std::shared_ptr<std::mt19937> _rng = nullptr;
|
||||
|
||||
};
|
||||
|
||||
} // namespace kami
|
||||
|
||||
259
include/kami/reporter.h
Normal file
259
include/kami/reporter.h
Normal file
@@ -0,0 +1,259 @@
|
||||
/*-
|
||||
* Copyright (c) 2022 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef KAMI_REPORTER_H
|
||||
//! @cond SuppressGuard
|
||||
#define KAMI_REPORTER_H
|
||||
//! @endcond
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/model.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
class Reporter;
|
||||
|
||||
class ReporterAgent;
|
||||
|
||||
class ReporterModel;
|
||||
|
||||
/**
|
||||
* @brief A superclass for all reporting agents
|
||||
*
|
||||
* @details All reporting agents should subclass the `ReportingAgent` class. At a minimum,
|
||||
* subclasses must implement the `step()` function, to execute a single time step for each
|
||||
* agent.
|
||||
*
|
||||
* @see `Agent`, `StagedAgent`
|
||||
*/
|
||||
class LIBKAMI_EXPORT ReporterAgent
|
||||
: public Agent {
|
||||
public:
|
||||
/**
|
||||
* @brief Collect the current state of the agent
|
||||
*
|
||||
* @details This function should collect the agent's
|
||||
* current state. The agent, notably, does not need
|
||||
* to collect historical state, as the historical state
|
||||
* is retained by the `Reporter` instance until such
|
||||
* time as `Reporter::report()` is called. However,
|
||||
* the implementation of the collect() function is
|
||||
* up to the end user who can, ultimately, return whatever
|
||||
* they want.
|
||||
*
|
||||
* The only restriction on collect is that it must return
|
||||
* its data as a [nlohmann::json](https://json.nlohmann.me/)
|
||||
* JSON object. See `Reporter` for additional details.
|
||||
*/
|
||||
virtual std::unique_ptr<nlohmann::json> collect() = 0;
|
||||
|
||||
/**
|
||||
* @brief Execute a time-step for the agent
|
||||
*
|
||||
* @details This function should step the agent instance. Any activities that the
|
||||
* agent should perform as part of its time step should be in this function.
|
||||
*
|
||||
* @param model a reference copy of the model
|
||||
*
|
||||
* @returns a copy of the AgentID
|
||||
*/
|
||||
virtual AgentID step(std::shared_ptr<ReporterModel> model) = 0;
|
||||
|
||||
private:
|
||||
// This should be uncallable, but knocks out the inherited method.
|
||||
AgentID step(std::shared_ptr<Model> model) override;;
|
||||
|
||||
int _step_counter = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An abstract for generic models with a reporting capability
|
||||
*
|
||||
* @see `Model`
|
||||
*/
|
||||
class LIBKAMI_EXPORT ReporterModel
|
||||
: public Model {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
ReporterModel();
|
||||
|
||||
/**
|
||||
* @brief Collect the current state of the model
|
||||
*
|
||||
* @details This function should collect the model's
|
||||
* current state. The model, notably, does not need
|
||||
* to collect historical state, as the historical state
|
||||
* is retained by the `Reporter` instance until such
|
||||
* time as `Reporter::report()` is called. However,
|
||||
* the implementation of the collect() function is
|
||||
* up to the end user who can, ultimately, return whatever
|
||||
* they want.
|
||||
*
|
||||
* This is not expected to return agent data collection,
|
||||
* as the agents' information is collected separately.
|
||||
*/
|
||||
virtual std::unique_ptr<nlohmann::json> collect() = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the step id of the model
|
||||
*
|
||||
* @details The step_id should probably be a monotonically
|
||||
* incrementing integer.
|
||||
*/
|
||||
virtual unsigned int get_step_id();
|
||||
|
||||
/**
|
||||
* @brief Execute a single time step of the model
|
||||
*
|
||||
* @details This method will collect all the `Agent`s in the `Population` associated
|
||||
* with the model and pass them to the associated `Scheduler` for stepping. After scheduling,
|
||||
* this method will run the collect() for the `Reporter` associated with this model.
|
||||
*
|
||||
* @returns a shared pointer to the model instance
|
||||
*/
|
||||
virtual std::shared_ptr<Model> step() override;
|
||||
|
||||
/**
|
||||
* @brief Get the current report
|
||||
*
|
||||
* @details This method will return an object containg the data collected to that
|
||||
* point in the simulation.
|
||||
*
|
||||
* @returns a unique pointer to a `nlohmann::json` object representing the current report
|
||||
*/
|
||||
std::unique_ptr<nlohmann::json> report();
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* @brief The current report
|
||||
*/
|
||||
std::shared_ptr<Reporter> _rpt;
|
||||
|
||||
/**
|
||||
* @brief The model's current step count
|
||||
*/
|
||||
unsigned int _step_count{};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A `Reporter` is a module that works with `ReporterAgent` and `ReporterModel`
|
||||
* to collect information about the state of the model for later analysis
|
||||
*/
|
||||
class LIBKAMI_EXPORT Reporter
|
||||
: public std::enable_shared_from_this<Reporter> {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
Reporter();
|
||||
|
||||
/**
|
||||
* @brief Empty the report
|
||||
*
|
||||
* @details Clear all entries from the report; new collection
|
||||
* operations begin with a blank slate.
|
||||
*
|
||||
* @returns a reference copy of the `Reporter`
|
||||
*/
|
||||
std::shared_ptr<Reporter> clear();
|
||||
|
||||
/**
|
||||
* @brief Collect the current state of the model
|
||||
*
|
||||
* @details This will collect the current state of
|
||||
* each agent associated with the population returned
|
||||
* by the `Model`'s `get_population()`.
|
||||
*
|
||||
* @param model reference copy of the model
|
||||
*
|
||||
* @returns a copy of the current report
|
||||
*/
|
||||
std::unique_ptr<nlohmann::json> collect(const std::shared_ptr<ReporterModel>& model);
|
||||
|
||||
/**
|
||||
* @brief Collect the current state of the model
|
||||
*
|
||||
* @details This will collect the current state of
|
||||
* each agent associated with the `Population`.
|
||||
*
|
||||
* @param model reference copy of the model
|
||||
* @param pop Population to collect on
|
||||
*
|
||||
* @returns a copy of the current report
|
||||
*/
|
||||
std::unique_ptr<nlohmann::json>
|
||||
collect(
|
||||
const std::shared_ptr<ReporterModel>& model,
|
||||
const std::shared_ptr<Population>& pop
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Collect the current state of the model
|
||||
*
|
||||
* @details This will collect the current state of
|
||||
* each agent given
|
||||
*
|
||||
* @param model reference copy of the model
|
||||
* @param agent_list a vector agents to report on
|
||||
*
|
||||
* @returns a copy of the current report
|
||||
*/
|
||||
std::unique_ptr<nlohmann::json>
|
||||
collect(
|
||||
const std::shared_ptr<ReporterModel>& model,
|
||||
const std::unique_ptr<std::vector<AgentID>>& agent_list
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Collect the report
|
||||
*
|
||||
* @details This will return the aggregate report
|
||||
* of all the data collected by this `Reporter`.
|
||||
*
|
||||
* @param model reference copy of the model
|
||||
*
|
||||
* @returns a copy of the current report
|
||||
*/
|
||||
std::unique_ptr<nlohmann::json> report(const std::shared_ptr<ReporterModel>& model);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief A vector of the the report collected so far
|
||||
*/
|
||||
std::unique_ptr<std::vector<nlohmann::json>> _report_data = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //KAMI_REPORTER_H
|
||||
@@ -25,13 +25,18 @@
|
||||
|
||||
#pragma once
|
||||
#ifndef KAMI_SCHEDULER_H
|
||||
//! @cond SuppressGuard
|
||||
#define KAMI_SCHEDULER_H
|
||||
//! @endcond
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <kami/KAMI_EXPORT.h>
|
||||
#include <kami/agent.h>
|
||||
#include <kami/kami.h>
|
||||
#include <kami/model.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
/**
|
||||
* Create a Kami scheduler.
|
||||
*
|
||||
@@ -42,25 +47,76 @@ namespace kami {
|
||||
class LIBKAMI_EXPORT Scheduler {
|
||||
public:
|
||||
/**
|
||||
* Add an Agent to the Scheduler.
|
||||
* @brief Execute a single time step.
|
||||
*
|
||||
* @param agent_id The AgentID of the agent to add.
|
||||
* @details This method will step through the list of Agents in the
|
||||
* `Population` associated with `model` and then execute the `Agent::step()`
|
||||
* method for every Agent assigned to this scheduler in the order
|
||||
* assigned.
|
||||
*
|
||||
* @param model a reference copy of the model
|
||||
*
|
||||
* @returns returns vector of agents successfully stepped
|
||||
*/
|
||||
virtual void add_agent(AgentID agent_id) = 0;
|
||||
virtual std::unique_ptr<std::vector<AgentID>> step(std::shared_ptr<Model> model) = 0;
|
||||
|
||||
/**
|
||||
* Remove an Agent from the Scheduler.
|
||||
* @brief Execute a single time step for a `ReporterModel`
|
||||
*
|
||||
* @param agent_id The AgentID of the agent to remove.
|
||||
* @details This method will step through the list of Agents in the
|
||||
* scheduler's internal queue and then execute the `Agent::step()`
|
||||
* method for every Agent assigned to this scheduler in the order
|
||||
* assigned.
|
||||
*
|
||||
* @param model a reference copy of the `ReporterModel`
|
||||
*
|
||||
* @returns returns vector of agents successfully stepped
|
||||
*/
|
||||
[[maybe_unused]] virtual void delete_agent(AgentID agent_id) = 0;
|
||||
virtual std::unique_ptr<std::vector<AgentID>> step(std::shared_ptr<ReporterModel> model) = 0;
|
||||
|
||||
/**
|
||||
* Step the Scheduler.
|
||||
* @brief Execute a single time step.
|
||||
*
|
||||
* A generic step function that executes a single time step.
|
||||
* @details This method will step through the list of Agents
|
||||
* provided and then execute the `Agent::step()`
|
||||
* method for every Agent assigned to this scheduler in the order
|
||||
* assigned.
|
||||
*
|
||||
* @param model a reference copy of the model
|
||||
* @param agent_list list of agents to execute the step
|
||||
*
|
||||
* @returns returns vector of agents successfully stepped
|
||||
*/
|
||||
virtual void step() = 0;
|
||||
virtual std::unique_ptr<std::vector<AgentID>>
|
||||
step(
|
||||
std::shared_ptr<Model> model,
|
||||
std::unique_ptr<std::vector<AgentID>> agent_list
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* @brief Execute a single time step for a `ReporterModel`
|
||||
*
|
||||
* @details This method will step through the list of Agents in the
|
||||
* scheduler's internal queue and then execute the `Agent::step()`
|
||||
* method for every Agent assigned to this scheduler in the order
|
||||
* assigned.
|
||||
*
|
||||
* @param model a reference copy of the `ReporterModel`
|
||||
* @param agent_list list of agents to execute the step
|
||||
*
|
||||
* @returns returns vector of agents successfully stepped
|
||||
*/
|
||||
virtual std::unique_ptr<std::vector<AgentID>>
|
||||
step(
|
||||
std::shared_ptr<ReporterModel> model,
|
||||
std::unique_ptr<std::vector<AgentID>> agent_list
|
||||
) = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Counter to increment on each step
|
||||
*/
|
||||
int _step_counter = 0;
|
||||
};
|
||||
|
||||
} // namespace kami
|
||||
|
||||
@@ -25,56 +25,31 @@
|
||||
|
||||
#pragma once
|
||||
#ifndef KAMI_SEQUENTIAL_H
|
||||
//! @cond SuppressGuard
|
||||
#define KAMI_SEQUENTIAL_H
|
||||
//! @endcond
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <kami/KAMI_EXPORT.h>
|
||||
#include <kami/agent.h>
|
||||
#include <kami/kami.h>
|
||||
#include <kami/model.h>
|
||||
#include <kami/scheduler.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
/**
|
||||
* @brief Will execute all agent steps in a sequential order.
|
||||
*
|
||||
* @details A sequential scheduler will iterate over the agents assigned
|
||||
* to the scheduler and call their `step()` function in a sequential order.
|
||||
* That order is preserved between calls to `step()` but may be modified by
|
||||
* `addAgent()` or `deleteAgent()`.
|
||||
*
|
||||
* \pre First create a Model for the scheduler to live in.
|
||||
*/
|
||||
class LIBKAMI_EXPORT SequentialScheduler : public Scheduler {
|
||||
/**
|
||||
* @brief Will execute all agent steps in a sequential order.
|
||||
*
|
||||
* @details A sequential scheduler will iterate over the agents assigned
|
||||
* to the scheduler and call their `step()` function in a sequential order.
|
||||
* That order is preserved between calls to `step()` but may be modified by
|
||||
* `addAgent()` or `deleteAgent()`.
|
||||
*/
|
||||
class LIBKAMI_EXPORT SequentialScheduler
|
||||
: public Scheduler {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor.
|
||||
*
|
||||
* @details The Model parameter is used by the scheduler to get
|
||||
* access to an Agent. The Model is presumed to maintain a master
|
||||
* list of all Agents in the Model and the Model can be queried for
|
||||
* a reference to any particular Agent at `step()` time.
|
||||
*/
|
||||
explicit SequentialScheduler(Model *model);
|
||||
|
||||
/**
|
||||
* @brief Add an agent to the scheduler.
|
||||
*
|
||||
* @details The scheduler maintains a list of all AgentIDs currently
|
||||
* assigned. This function adds a new Agent to the list.
|
||||
*/
|
||||
void add_agent(AgentID agent_id) override;
|
||||
|
||||
/**
|
||||
* @brief Remove an agent from the scheduler.
|
||||
*
|
||||
* @details The scheduler maintains a list of all AgentIDs currently
|
||||
* assigned. This function removes an Agent from the list.
|
||||
*/
|
||||
void delete_agent(AgentID agent_id) override;
|
||||
|
||||
/**
|
||||
* @brief Execute a single time step.
|
||||
*
|
||||
@@ -82,25 +57,64 @@ namespace kami {
|
||||
* scheduler's internal queue and then execute the `Agent::step()`
|
||||
* method for every Agent assigned to this scheduler in the order
|
||||
* assigned.
|
||||
*
|
||||
* @param model a reference copy of the model
|
||||
*
|
||||
* @returns returns vector of agents successfully stepped
|
||||
*/
|
||||
void step() override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* A vector containing the `AgentID`s of all agents assigned to this
|
||||
* scheduler
|
||||
*/
|
||||
std::vector<AgentID> _agent_list;
|
||||
std::unique_ptr<std::vector<AgentID>> step(std::shared_ptr<Model> model) override;
|
||||
|
||||
/**
|
||||
* A pointer to the `Model` this scheduler belongs to
|
||||
* @brief Execute a single time step for a `ReporterModel`
|
||||
*
|
||||
* @details This method will step through the list of Agents in the
|
||||
* scheduler's internal queue and then execute the `Agent::step()`
|
||||
* method for every Agent assigned to this scheduler in the order
|
||||
* assigned.
|
||||
*
|
||||
* @param model a reference copy of the `ReporterModel`
|
||||
*
|
||||
* @returns returns vector of agents successfully stepped
|
||||
*/
|
||||
Model *_model;
|
||||
std::unique_ptr<std::vector<AgentID>> step(std::shared_ptr<ReporterModel> model) override;
|
||||
|
||||
/**
|
||||
* Counter to increment on each step
|
||||
* @brief Execute a single time step.
|
||||
*
|
||||
* @details This method will step through the list of Agents
|
||||
* provided and then execute the `Agent::step()`
|
||||
* method for every Agent assigned to this scheduler in the order
|
||||
* assigned.
|
||||
*
|
||||
* @param model a reference copy of the model
|
||||
* @param agent_list list of agents to execute the step
|
||||
*
|
||||
* @returns returns vector of agents successfully stepped
|
||||
*/
|
||||
int _step_counter;
|
||||
std::unique_ptr<std::vector<AgentID>>
|
||||
step(
|
||||
std::shared_ptr<Model> model,
|
||||
std::unique_ptr<std::vector<AgentID>> agent_list
|
||||
) override;
|
||||
|
||||
/**
|
||||
* @brief Execute a single time step for a `ReporterModel`
|
||||
*
|
||||
* @details This method will step through the list of Agents in the
|
||||
* scheduler's internal queue and then execute the `Agent::step()`
|
||||
* method for every Agent assigned to this scheduler in the order
|
||||
* assigned.
|
||||
*
|
||||
* @param model a reference copy of the `ReporterModel`
|
||||
* @param agent_list list of agents to execute the step
|
||||
*
|
||||
* @returns returns vector of agents successfully stepped
|
||||
*/
|
||||
std::unique_ptr<std::vector<AgentID>>
|
||||
step(
|
||||
std::shared_ptr<ReporterModel> model,
|
||||
std::unique_ptr<std::vector<AgentID>> agent_list
|
||||
) override;
|
||||
};
|
||||
|
||||
} // namespace kami
|
||||
|
||||
@@ -25,7 +25,9 @@
|
||||
|
||||
#pragma once
|
||||
#ifndef KAMI_SOLOGRID1D_H
|
||||
//! @cond SuppressGuard
|
||||
#define KAMI_SOLOGRID1D_H
|
||||
//! @endcond
|
||||
|
||||
#include <kami/KAMI_EXPORT.h>
|
||||
#include <kami/agent.h>
|
||||
@@ -34,24 +36,30 @@
|
||||
|
||||
namespace kami {
|
||||
|
||||
/**
|
||||
* @brief A one-dimensional grid where each cell may contain only one Agent
|
||||
*
|
||||
* @details The grid is linear and may wrap around in its only dimension.
|
||||
*/
|
||||
class LIBKAMI_EXPORT SoloGrid1D : public Grid1D {
|
||||
/**
|
||||
* @brief A one-dimensional grid where each cell may contain one agents
|
||||
*
|
||||
* @details The grid is linear and may wrap around in its only dimension.
|
||||
*
|
||||
* @see `Grid1D`
|
||||
* @see `MultiGrid1D`
|
||||
*/
|
||||
class LIBKAMI_EXPORT SoloGrid1D
|
||||
: public Grid1D {
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* @brief Constructor
|
||||
*
|
||||
* @param[in] maximum_x the length of the grid.
|
||||
* @param[in] wrap_x should the grid wrap around on itself.
|
||||
*/
|
||||
SoloGrid1D(unsigned int maximum_x, bool wrap_x)
|
||||
: Grid1D(maximum_x, wrap_x) {}
|
||||
SoloGrid1D(
|
||||
unsigned int maximum_x,
|
||||
bool wrap_x
|
||||
);
|
||||
|
||||
/**
|
||||
* Place agent on the grid at the specified location.
|
||||
* @brief Place agent on the grid at the specified location.
|
||||
*
|
||||
* @param[in] agent_id the `AgentID` of the agent to add.
|
||||
* @param[in] coord the coordinates of the agent.
|
||||
@@ -59,7 +67,10 @@ namespace kami {
|
||||
* @returns false if the agent is not placed at the specified
|
||||
* location, otherwise, true
|
||||
*/
|
||||
bool add_agent(AgentID agent_id, GridCoord1D coord) override;
|
||||
AgentID add_agent(
|
||||
AgentID agent_id,
|
||||
const GridCoord1D& coord
|
||||
) override;
|
||||
};
|
||||
|
||||
} // namespace kami
|
||||
|
||||
@@ -25,7 +25,9 @@
|
||||
|
||||
#pragma once
|
||||
#ifndef KAMI_SOLOGRID2D_H
|
||||
//! @cond SuppressGuard
|
||||
#define KAMI_SOLOGRID2D_H
|
||||
//! @endcond
|
||||
|
||||
#include <kami/KAMI_EXPORT.h>
|
||||
#include <kami/agent.h>
|
||||
@@ -34,29 +36,34 @@
|
||||
|
||||
namespace kami {
|
||||
|
||||
/**
|
||||
* @brief A two-dimensional grid where each cell may contain only one Agent
|
||||
*
|
||||
* @details The grid is linear and may wrap around in its only dimension.
|
||||
*
|
||||
* @see `MultiGrid2D`
|
||||
* @see `Grid2D`
|
||||
*/
|
||||
class LIBKAMI_EXPORT SoloGrid2D : public Grid2D {
|
||||
/**
|
||||
* @brief A two-dimensional grid where each cell may contain multiple agents
|
||||
*
|
||||
* @details The grid is linear and may wrap around in either dimension.
|
||||
*
|
||||
* @see `Grid2D`
|
||||
* @see `MultiGrid2D`
|
||||
*/
|
||||
class LIBKAMI_EXPORT SoloGrid2D
|
||||
: public Grid2D {
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* @details Constructor
|
||||
*
|
||||
* @param[in] maximum_x the length of the grid in the first dimension
|
||||
* @param[in] maximum_y the length of the grid in the second dimension
|
||||
* @param[in] wrap_x should the grid wrap around on itself in the first dimension
|
||||
* @param[in] wrap_y should the grid wrap around on itself in the second dimension
|
||||
*/
|
||||
SoloGrid2D(unsigned int maximum_x, unsigned int maximum_y, bool wrap_x, bool wrap_y)
|
||||
: Grid2D(maximum_x, maximum_y, wrap_x, wrap_y) {};
|
||||
SoloGrid2D(
|
||||
unsigned int maximum_x,
|
||||
unsigned int maximum_y,
|
||||
bool wrap_x,
|
||||
bool wrap_y
|
||||
);;
|
||||
|
||||
/**
|
||||
* Place agent on the grid at the specified location.
|
||||
* @details Place agent on the grid at the specified location.
|
||||
*
|
||||
* @param[in] agent_id the `AgentID` of the agent to add.
|
||||
* @param[in] coord the coordinates of the agent.
|
||||
@@ -64,7 +71,11 @@ namespace kami {
|
||||
* @returns false if the agent is not placed at the specified
|
||||
* location, otherwise, true
|
||||
*/
|
||||
bool add_agent(AgentID agent_id, GridCoord2D coord) override;
|
||||
AgentID add_agent(
|
||||
AgentID agent_id,
|
||||
const GridCoord2D& coord
|
||||
) override;
|
||||
|
||||
};
|
||||
|
||||
} // namespace kami
|
||||
|
||||
@@ -25,73 +25,90 @@
|
||||
|
||||
#pragma once
|
||||
#ifndef KAMI_STAGED_H
|
||||
//! @cond SuppressGuard
|
||||
#define KAMI_STAGED_H
|
||||
//! @endcond
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/model.h>
|
||||
#include <kami/scheduler.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <kami/sequential.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
/**
|
||||
* Will execute all agent steps in a sequential order.
|
||||
*
|
||||
* A sequential scheduler will iterate over the agents assigned to the scheduler
|
||||
* and call their `step()` function in a sequential order. That order is
|
||||
* preserved between calls to `step()` but may be modified by `add_agent()` or
|
||||
* `delete_agent()`.
|
||||
*
|
||||
* @note First create a Model for the scheduler to live in.
|
||||
*/
|
||||
class LIBKAMI_EXPORT StagedScheduler : public Scheduler {
|
||||
/**
|
||||
* @brief Will execute all agent steps in a sequential order.
|
||||
*
|
||||
* @details A sequential scheduler will iterate over the agents assigned to the scheduler
|
||||
* and call their `step()` function in a sequential order. That order is
|
||||
* preserved between calls to `step()` but may be modified by `add_agent()` or
|
||||
* `delete_agent()`.
|
||||
*/
|
||||
class LIBKAMI_EXPORT StagedScheduler
|
||||
: public SequentialScheduler {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* The Model parameter is used by the scheduler to get access to an Agent.
|
||||
* The Model is presumed to maintain a master list of all Agents in the
|
||||
* Model and the Model can be queried for a reference to any particular
|
||||
* Agent at `step()` time.
|
||||
*/
|
||||
explicit StagedScheduler(Model *);
|
||||
|
||||
/**
|
||||
* A deconstructor.
|
||||
*/
|
||||
~StagedScheduler() = default;
|
||||
|
||||
/**
|
||||
* Add an agent to the scheduler.
|
||||
* @brief Execute a single time step
|
||||
*
|
||||
* The scheduler maintains a list of all AgentIDs currently assigned. This
|
||||
* function adds a new Agent to the list.
|
||||
*/
|
||||
void add_agent(AgentID agent_id) override;
|
||||
|
||||
/**
|
||||
* Remove an agent from the scheduler.
|
||||
*
|
||||
* The scheduler maintains a list of all AgentIDs currently assigned. This
|
||||
* function removes an Agent from the list.
|
||||
*/
|
||||
void delete_agent(AgentID agent_id) override;
|
||||
|
||||
/**
|
||||
* Execute a single time step.
|
||||
*
|
||||
* This method will step through the list of Agents in the scheduler's
|
||||
* @details This method will step through the list of Agents in the scheduler's
|
||||
* internal queue and execute the `Agent::step()` method for each `Agent`
|
||||
* in the same order. Finally, it will execute the `Agent::advance()`
|
||||
* method for each Agent in the same order.
|
||||
*
|
||||
* @param model a reference copy of the model
|
||||
* @param agent_list list of agents to execute the step
|
||||
*
|
||||
* @returns returns vector of agents successfully stepped
|
||||
*/
|
||||
void step() override;
|
||||
std::unique_ptr<std::vector<AgentID>>
|
||||
step(
|
||||
std::shared_ptr<Model> model,
|
||||
std::unique_ptr<std::vector<AgentID>> agent_list
|
||||
) override;
|
||||
|
||||
std::unique_ptr<std::vector<AgentID>>
|
||||
step(
|
||||
std::shared_ptr<ReporterModel> model,
|
||||
std::unique_ptr<std::vector<AgentID>> agent_list
|
||||
) override;
|
||||
|
||||
private:
|
||||
std::vector<AgentID> _agent_list;
|
||||
Model *_model;
|
||||
int _step_counter;
|
||||
/**
|
||||
* @brief Advance a single time step.
|
||||
*
|
||||
* @details This method will step through the list of StagedAgent in the
|
||||
* scheduler's internal queue and then execute the `StagedAgent::step()`
|
||||
* method for every StagedAgent assigned to this scheduler in the order
|
||||
* assigned.
|
||||
*
|
||||
* @param model a reference copy of the model
|
||||
*
|
||||
* @returns returns vector of agents successfully advanced
|
||||
*/
|
||||
std::unique_ptr<std::vector<AgentID>> advance(std::shared_ptr<Model> model);
|
||||
|
||||
std::unique_ptr<std::vector<AgentID>> advance(std::shared_ptr<ReporterModel> model);
|
||||
|
||||
/**
|
||||
* @brief Advance a single time step.
|
||||
*
|
||||
* @details This method will step through the list of StagedAgent
|
||||
* provided and then execute the `StagedAgent::advance()`
|
||||
* method for every StagedAgent assigned to this scheduler in the order
|
||||
* assigned.
|
||||
*
|
||||
* @param model a reference copy of the model
|
||||
* @param agent_list list of agents to execute the step
|
||||
*
|
||||
* @returns returns vector of agents successfully advanced
|
||||
*/
|
||||
std::unique_ptr<std::vector<AgentID>>
|
||||
advance(
|
||||
std::shared_ptr<Model> model,
|
||||
std::unique_ptr<std::vector<AgentID>> agent_list
|
||||
);
|
||||
};
|
||||
|
||||
} // namespace kami
|
||||
|
||||
@@ -2,63 +2,61 @@
|
||||
# Set minimum version of CMake.
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
project(libkami VERSION ${KAMI_VERSION_STRING}
|
||||
LANGUAGES CXX)
|
||||
set(LIBRARY_NAME "libkami")
|
||||
|
||||
create_library(NAME libkami
|
||||
NAMESPACE libkami
|
||||
SOURCES
|
||||
agent.cc
|
||||
domain.cc
|
||||
grid1d.cc
|
||||
grid2d.cc
|
||||
multigrid1d.cc
|
||||
multigrid2d.cc
|
||||
random.cc
|
||||
sequential.cc
|
||||
sologrid1d.cc
|
||||
sologrid2d.cc
|
||||
staged.cc
|
||||
PUBLIC_INCLUDE_PATHS
|
||||
"$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>"
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/generated_headers>"
|
||||
PRIVATE_LINKED_TARGETS
|
||||
${COVERAGE_TARGET}
|
||||
EXPORT_FILE_PATH
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/generated_headers/kami/KAMI_EXPORT.h"
|
||||
)
|
||||
project(${LIBRARY_NAME} LANGUAGES CXX)
|
||||
|
||||
project(${LIBRARY_NAME}
|
||||
VERSION ${VERSION_STRING}
|
||||
LANGUAGES CXX)
|
||||
|
||||
file(GLOB LIBRARY_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cc")
|
||||
|
||||
create_library(
|
||||
NAME ${LIBRARY_NAME}
|
||||
NAMESPACE ${LIBRARY_NAME}
|
||||
SOURCES ${LIBRARY_SOURCES}
|
||||
PUBLIC_INCLUDE_PATHS "$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>" "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/generated_headers>"
|
||||
PRIVATE_LINKED_TARGETS ${COVERAGE_TARGET}
|
||||
PUBLIC_LINKED_TARGETS fmt
|
||||
EXPORT_FILE_PATH "${CMAKE_CURRENT_BINARY_DIR}/generated_headers/kami/KAMI_EXPORT.h"
|
||||
)
|
||||
|
||||
configure_file(
|
||||
"${CMAKE_SOURCE_DIR}/include/kami/config.h.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/generated_headers/kami/config.h")
|
||||
"${CMAKE_SOURCE_DIR}/include/kami/config.h.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/generated_headers/kami/config.h"
|
||||
)
|
||||
|
||||
set_target_properties(libkami PROPERTIES VERSION ${KAMI_VERSION_STRING}
|
||||
SOVERSION ${KAMI_VERSION_MAJOR}
|
||||
OUTPUT_NAME kami)
|
||||
set_target_properties(
|
||||
${LIBRARY_NAME}
|
||||
PROPERTIES VERSION ${VERSION_STRING}
|
||||
SOVERSION ${VERSION_MAJOR}
|
||||
OUTPUT_NAME kami
|
||||
)
|
||||
|
||||
# Introduce variables:
|
||||
# * CMAKE_INSTALL_LIBDIR
|
||||
# * CMAKE_INSTALL_BINDIR
|
||||
# * CMAKE_INSTALL_INCLUDEDIR
|
||||
include(GNUInstallDirs)
|
||||
# Introduce variables:
|
||||
# * CMAKE_INSTALL_LIBDIR
|
||||
# * CMAKE_INSTALL_BINDIR
|
||||
# * CMAKE_INSTALL_INCLUDEDIR
|
||||
include(GNUInstallDirs)
|
||||
|
||||
# Headers:
|
||||
# * include/foo/bar/bar.h -> <prefix>/include/NAMESPACE/LIBRARY_NAME/*.h
|
||||
# * include/foo/bar/bar.h -> <prefix>/include/foo/bar/bar.h
|
||||
install(
|
||||
DIRECTORY "${CMAKE_SOURCE_DIR}/include/kami"
|
||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||
FILES_MATCHING PATTERN "*"
|
||||
)
|
||||
# Headers:
|
||||
# * include/foo/bar/bar.h -> <prefix>/include/NAMESPACE/LIBRARY_NAME/*.h
|
||||
# * include/foo/bar/bar.h -> <prefix>/include/foo/bar/bar.h
|
||||
install(
|
||||
DIRECTORY "${CMAKE_SOURCE_DIR}/include/kami"
|
||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||
FILES_MATCHING PATTERN "*"
|
||||
)
|
||||
|
||||
# Export headers:
|
||||
# The export header will be stored in:
|
||||
# <prefix>/include/${NAMESPACE}/LIBRARY_NAME/LIBRARY_NAME_export.h
|
||||
install(
|
||||
FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/generated_headers/kami/KAMI_EXPORT.h"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/generated_headers/kami/config.h"
|
||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/kami"
|
||||
)
|
||||
# Export headers:
|
||||
# The export header will be stored in:
|
||||
# <prefix>/include/${NAMESPACE}/LIBRARY_NAME/LIBRARY_NAME_export.h
|
||||
install(
|
||||
FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/generated_headers/kami/KAMI_EXPORT.h"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/generated_headers/kami/config.h"
|
||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/kami"
|
||||
)
|
||||
|
||||
add_library(kami::libkami ALIAS libkami)
|
||||
add_library(kami::libkami ALIAS libkami)
|
||||
@@ -23,35 +23,65 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <kami/agent.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <kami/agent.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
bool operator==(const AgentID &lhs, const AgentID &rhs) {
|
||||
AgentID::AgentID()
|
||||
:_id(_id_next++) {
|
||||
}
|
||||
|
||||
std::string AgentID::to_string() const {
|
||||
return std::to_string(_id);
|
||||
}
|
||||
|
||||
bool operator==(
|
||||
const AgentID& lhs,
|
||||
const AgentID& rhs
|
||||
) {
|
||||
return lhs._id == rhs._id;
|
||||
}
|
||||
|
||||
bool operator!=(const AgentID &lhs, const AgentID &rhs) {
|
||||
bool operator!=(
|
||||
const AgentID& lhs,
|
||||
const AgentID& rhs
|
||||
) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
bool operator<(const AgentID &lhs, const AgentID &rhs) {
|
||||
bool operator<(
|
||||
const AgentID& lhs,
|
||||
const AgentID& rhs
|
||||
) {
|
||||
return lhs._id < rhs._id;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &lhs, const AgentID &rhs) {
|
||||
std::ostream& operator<<(
|
||||
std::ostream& lhs,
|
||||
const AgentID& rhs
|
||||
) {
|
||||
return lhs << rhs.to_string();
|
||||
}
|
||||
|
||||
AgentID Agent::get_agent_id() const { return this->_agent_id; }
|
||||
AgentID Agent::get_agent_id() const {
|
||||
return this->_agent_id;
|
||||
}
|
||||
|
||||
bool operator==(const Agent &lhs, const Agent &rhs) {
|
||||
bool operator==(
|
||||
const Agent& lhs,
|
||||
const Agent& rhs
|
||||
) {
|
||||
return lhs._agent_id == rhs._agent_id;
|
||||
}
|
||||
|
||||
bool operator!=(const Agent &lhs, const Agent &rhs) { return !(lhs == rhs); }
|
||||
bool operator!=(
|
||||
const Agent& lhs,
|
||||
const Agent& rhs
|
||||
) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
} // namespace kami
|
||||
|
||||
@@ -23,14 +23,17 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <kami/domain.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <kami/domain.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
std::ostream &operator<<(std::ostream &lhs, const Coord &rhs) {
|
||||
std::ostream& operator<<(
|
||||
std::ostream& lhs,
|
||||
const Coord& rhs
|
||||
) {
|
||||
return lhs << rhs.to_string();
|
||||
}
|
||||
|
||||
|
||||
@@ -23,132 +23,202 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/domain.h>
|
||||
#include <kami/error.h>
|
||||
#include <kami/grid1d.h>
|
||||
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace kami {
|
||||
|
||||
int GridCoord1D::get_x_location() const { return _x_coord; }
|
||||
GridCoord1D::GridCoord1D(int x_coord)
|
||||
:_x_coord(x_coord) {
|
||||
}
|
||||
|
||||
int GridCoord1D::x() const {
|
||||
return _x_coord;
|
||||
}
|
||||
|
||||
std::string GridCoord1D::to_string() const {
|
||||
return std::string("(" + std::to_string(_x_coord) + ")");
|
||||
}
|
||||
|
||||
bool operator==(const GridCoord1D &lhs, const GridCoord1D &rhs) {
|
||||
double GridCoord1D::distance(std::shared_ptr<Coord>& p) const {
|
||||
auto p1d = std::static_pointer_cast<GridCoord1D>(p);
|
||||
return static_cast<double>(abs(_x_coord - p1d->_x_coord));
|
||||
}
|
||||
|
||||
bool operator==(
|
||||
const GridCoord1D& lhs,
|
||||
const GridCoord1D& rhs
|
||||
) {
|
||||
return (lhs._x_coord == rhs._x_coord);
|
||||
}
|
||||
|
||||
bool operator!=(const GridCoord1D &lhs, const GridCoord1D &rhs) {
|
||||
bool operator!=(
|
||||
const GridCoord1D& lhs,
|
||||
const GridCoord1D& rhs
|
||||
) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &lhs, const GridCoord1D &rhs) {
|
||||
std::ostream& operator<<(
|
||||
std::ostream& lhs,
|
||||
const GridCoord1D& rhs
|
||||
) {
|
||||
return lhs << rhs.to_string();
|
||||
}
|
||||
|
||||
Grid1D::Grid1D(unsigned int maximum_x, bool wrap_x) {
|
||||
_agent_grid = new std::vector<AgentID>[maximum_x];
|
||||
_agent_index = new std::map<AgentID, GridCoord1D>;
|
||||
GridCoord1D operator+(
|
||||
const GridCoord1D& lhs,
|
||||
const GridCoord1D& rhs
|
||||
) {
|
||||
return GridCoord1D(lhs._x_coord + rhs._x_coord);
|
||||
}
|
||||
|
||||
GridCoord1D operator-(
|
||||
const GridCoord1D& lhs,
|
||||
const GridCoord1D& rhs
|
||||
) {
|
||||
return GridCoord1D(lhs._x_coord - rhs._x_coord);
|
||||
}
|
||||
|
||||
GridCoord1D operator*(
|
||||
const GridCoord1D& lhs,
|
||||
const double rhs
|
||||
) {
|
||||
return GridCoord1D(static_cast<int>(lhs._x_coord * rhs));
|
||||
}
|
||||
|
||||
GridCoord1D operator*(
|
||||
const double lhs,
|
||||
const GridCoord1D& rhs
|
||||
) {
|
||||
return GridCoord1D(static_cast<int>(rhs._x_coord * lhs));
|
||||
}
|
||||
|
||||
|
||||
Grid1D::Grid1D(
|
||||
unsigned int maximum_x,
|
||||
bool wrap_x
|
||||
) {
|
||||
_maximum_x = maximum_x;
|
||||
_wrap_x = wrap_x;
|
||||
|
||||
_agent_grid = std::make_unique<std::unordered_multimap<GridCoord1D, AgentID>>();
|
||||
_agent_index = std::make_unique<std::map<AgentID, GridCoord1D>>();
|
||||
}
|
||||
|
||||
Grid1D::~Grid1D() {
|
||||
delete _agent_index;
|
||||
delete[] _agent_grid;
|
||||
AgentID Grid1D::delete_agent(AgentID agent_id) {
|
||||
return delete_agent(agent_id, get_location_by_agent(agent_id));
|
||||
}
|
||||
|
||||
[[maybe_unused]] bool Grid1D::delete_agent(AgentID agent_id) {
|
||||
GridCoord1D coord = get_location_by_agent(agent_id);
|
||||
AgentID Grid1D::delete_agent(
|
||||
AgentID agent_id,
|
||||
const GridCoord1D& coord
|
||||
) {
|
||||
for (auto test_agent_id = _agent_grid->find(coord); test_agent_id != _agent_grid->end(); test_agent_id++)
|
||||
if (test_agent_id->second == agent_id) {
|
||||
_agent_grid->erase(test_agent_id);
|
||||
_agent_index->erase(agent_id);
|
||||
return agent_id;
|
||||
}
|
||||
|
||||
return delete_agent(agent_id, coord);
|
||||
throw error::AgentNotFound(
|
||||
fmt::format("Agent {} not found at location {}", agent_id.to_string(), coord.to_string()));
|
||||
}
|
||||
|
||||
bool Grid1D::is_location_valid(const GridCoord1D& coord) const {
|
||||
auto x = coord.get_x_location();
|
||||
auto x = coord.x();
|
||||
|
||||
return (x >= 0 && x < static_cast<int>(_maximum_x));
|
||||
}
|
||||
|
||||
bool Grid1D::is_location_empty(const GridCoord1D &coord) const {
|
||||
auto x = coord.get_x_location();
|
||||
|
||||
return _agent_grid[x].empty();
|
||||
bool Grid1D::is_location_empty(const GridCoord1D& coord) const {
|
||||
auto grid_location = _agent_grid->equal_range(coord);
|
||||
return grid_location.first == grid_location.second;
|
||||
}
|
||||
|
||||
bool Grid1D::delete_agent(AgentID agent_id, const GridCoord1D &coord) {
|
||||
auto agent_list = _agent_grid[static_cast<int>(coord.get_x_location())];
|
||||
|
||||
for (auto test_agent_id = agent_list.begin();
|
||||
test_agent_id < agent_list.end(); test_agent_id++) {
|
||||
if (*test_agent_id == agent_id) {
|
||||
agent_list.erase(test_agent_id);
|
||||
_agent_index->erase(agent_id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
AgentID Grid1D::move_agent(
|
||||
const AgentID agent_id,
|
||||
const GridCoord1D& coord
|
||||
) {
|
||||
return add_agent(delete_agent(agent_id, get_location_by_agent(agent_id)), coord);
|
||||
}
|
||||
|
||||
bool Grid1D::move_agent(AgentID agent_id, GridCoord1D coord) {
|
||||
GridCoord1D coord_current = get_location_by_agent(agent_id);
|
||||
|
||||
if (delete_agent(agent_id, coord_current))
|
||||
return add_agent(agent_id, std::move(coord));
|
||||
return false;
|
||||
std::shared_ptr<std::unordered_set<GridCoord1D>>
|
||||
Grid1D::get_neighborhood(
|
||||
const AgentID agent_id,
|
||||
const bool include_center
|
||||
) const {
|
||||
return std::move(get_neighborhood(get_location_by_agent(agent_id), include_center));
|
||||
}
|
||||
|
||||
std::vector<GridCoord1D> Grid1D::get_neighborhood(AgentID agent_id, bool include_center) const {
|
||||
GridCoord1D coord = get_location_by_agent(agent_id);
|
||||
|
||||
return get_neighborhood(coord, include_center);
|
||||
}
|
||||
|
||||
std::vector<GridCoord1D> Grid1D::get_neighborhood(const GridCoord1D& coord, bool include_center) const {
|
||||
std::vector<GridCoord1D> neighborhood;
|
||||
auto x = coord.get_x_location();
|
||||
std::shared_ptr<std::unordered_set<GridCoord1D>>
|
||||
Grid1D::get_neighborhood(
|
||||
const GridCoord1D& coord,
|
||||
const bool include_center
|
||||
) const {
|
||||
auto neighborhood = std::make_shared<std::unordered_set<GridCoord1D>>();
|
||||
|
||||
// We assume our starting position is valid
|
||||
if (include_center) neighborhood.push_back(coord);
|
||||
if (include_center)
|
||||
neighborhood->insert(coord);
|
||||
|
||||
for (auto& direction : directions) {
|
||||
auto new_location = coord_wrap(coord + direction);
|
||||
|
||||
// E, W
|
||||
{
|
||||
auto new_location = coord_wrap(GridCoord1D(x + 1));
|
||||
if (is_location_valid(new_location))
|
||||
neighborhood.push_back(coord_wrap(new_location));
|
||||
}
|
||||
{
|
||||
auto new_location = coord_wrap(GridCoord1D(x - 1));
|
||||
if (is_location_valid(new_location))
|
||||
neighborhood.push_back(coord_wrap(new_location));
|
||||
neighborhood->insert(new_location);
|
||||
}
|
||||
|
||||
return neighborhood;
|
||||
return std::move(neighborhood);
|
||||
}
|
||||
|
||||
std::vector<AgentID> *Grid1D::get_location_contents(const GridCoord1D& coord) const {
|
||||
if (is_location_valid(coord)) return &_agent_grid[coord.get_x_location()];
|
||||
return nullptr;
|
||||
std::shared_ptr<std::set<AgentID>> Grid1D::get_location_contents(const GridCoord1D& coord) const {
|
||||
auto agent_ids = std::make_shared<std::set<AgentID>>();
|
||||
|
||||
if (!is_location_valid(coord))
|
||||
throw error::LocationUnavailable(fmt::format("Coordinates {} are invalid", coord.to_string()));
|
||||
if (is_location_empty(coord))
|
||||
return agent_ids;
|
||||
|
||||
auto agent_range = _agent_grid->equal_range(coord);
|
||||
if (agent_range.first == agent_range.second)
|
||||
return agent_ids;
|
||||
|
||||
for (auto i = agent_range.first; i != agent_range.second; i++)
|
||||
agent_ids->insert(i->second);
|
||||
return agent_ids;
|
||||
}
|
||||
|
||||
[[maybe_unused]] bool Grid1D::get_wrap_x() const { return _wrap_x; }
|
||||
bool Grid1D::get_wrap_x() const {
|
||||
return _wrap_x;
|
||||
}
|
||||
|
||||
[[maybe_unused]] unsigned int Grid1D::get_maximum_x() const { return _maximum_x; }
|
||||
unsigned int Grid1D::get_maximum_x() const {
|
||||
return _maximum_x;
|
||||
}
|
||||
|
||||
GridCoord1D Grid1D::get_location_by_agent(AgentID agent_id) const {
|
||||
return _agent_index->at(agent_id);
|
||||
GridCoord1D Grid1D::get_location_by_agent(const AgentID& agent_id) const {
|
||||
auto coord = _agent_index->find(agent_id);
|
||||
if (coord == _agent_index->end())
|
||||
throw error::AgentNotFound(fmt::format("Agent {} not found on grid", agent_id.to_string()));
|
||||
return coord->second;
|
||||
}
|
||||
|
||||
GridCoord1D Grid1D::coord_wrap(const GridCoord1D& coord) const {
|
||||
auto x = coord.get_x_location();
|
||||
auto x = coord.x();
|
||||
|
||||
if(_wrap_x)
|
||||
if (_wrap_x)
|
||||
x = (x + static_cast<int>(_maximum_x)) % static_cast<int>(_maximum_x);
|
||||
return GridCoord1D(x);
|
||||
}
|
||||
|
||||
@@ -24,184 +24,260 @@
|
||||
*/
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/domain.h>
|
||||
#include <kami/grid.h>
|
||||
#include <kami/error.h>
|
||||
#include <kami/grid2d.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
int GridCoord2D::get_x_location() const {
|
||||
int GridCoord2D::x() const {
|
||||
return _x_coord;
|
||||
}
|
||||
|
||||
int GridCoord2D::get_y_location() const {
|
||||
int GridCoord2D::y() const {
|
||||
return _y_coord;
|
||||
}
|
||||
|
||||
std::string GridCoord2D::to_string() const {
|
||||
return std::string("(" + std::to_string(_x_coord) + ", " +
|
||||
std::to_string(_y_coord) + ")");
|
||||
return std::string("(" + std::to_string(_x_coord) + ", " + std::to_string(_y_coord) + ")");
|
||||
}
|
||||
|
||||
bool operator==(const GridCoord2D &lhs, const GridCoord2D &rhs) {
|
||||
double GridCoord2D::distance(std::shared_ptr<Coord>& p) const {
|
||||
auto p2d = std::static_pointer_cast<GridCoord2D>(p);
|
||||
return distance(p2d);
|
||||
}
|
||||
|
||||
double GridCoord2D::distance(
|
||||
std::shared_ptr<GridCoord2D>& p,
|
||||
GridDistanceType distance_type
|
||||
) const {
|
||||
switch (distance_type) {
|
||||
case GridDistanceType::Chebyshev:
|
||||
return distance_chebyshev(p);
|
||||
case GridDistanceType::Manhattan:
|
||||
return distance_manhattan(p);
|
||||
case GridDistanceType::Euclidean:
|
||||
return distance_euclidean(p);
|
||||
default:
|
||||
throw error::OptionInvalid("Unknown distance type given");
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(
|
||||
const GridCoord2D& lhs,
|
||||
const GridCoord2D& rhs
|
||||
) {
|
||||
return (lhs._x_coord == rhs._x_coord && lhs._y_coord == rhs._y_coord);
|
||||
}
|
||||
|
||||
bool operator!=(const GridCoord2D &lhs, const GridCoord2D &rhs) {
|
||||
bool operator!=(
|
||||
const GridCoord2D& lhs,
|
||||
const GridCoord2D& rhs
|
||||
) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &lhs, const GridCoord2D &rhs) {
|
||||
std::ostream& operator<<(
|
||||
std::ostream& lhs,
|
||||
const GridCoord2D& rhs
|
||||
) {
|
||||
return lhs << rhs.to_string();
|
||||
}
|
||||
|
||||
Grid2D::Grid2D(unsigned int maximum_x, unsigned int maximum_y, bool wrap_x,
|
||||
bool wrap_y) {
|
||||
GridCoord2D::GridCoord2D(
|
||||
int x_coord,
|
||||
int y_coord
|
||||
)
|
||||
:_x_coord(x_coord), _y_coord(y_coord) {
|
||||
}
|
||||
|
||||
double GridCoord2D::distance_chebyshev(std::shared_ptr<GridCoord2D>& p) const {
|
||||
return static_cast<double>(fmax(abs(_x_coord - p->_x_coord), abs(_x_coord - p->_x_coord)));
|
||||
}
|
||||
|
||||
double GridCoord2D::distance_euclidean(std::shared_ptr<GridCoord2D>& p) const {
|
||||
return sqrt(pow(_x_coord - p->_x_coord, 2) + pow(_x_coord - p->_x_coord, 2));
|
||||
}
|
||||
|
||||
double GridCoord2D::distance_manhattan(std::shared_ptr<GridCoord2D>& p) const {
|
||||
return static_cast<double>(abs(_x_coord - p->_x_coord) + abs(_x_coord - p->_x_coord));
|
||||
}
|
||||
|
||||
GridCoord2D operator+(
|
||||
const GridCoord2D& lhs,
|
||||
const GridCoord2D& rhs
|
||||
) {
|
||||
return {lhs._x_coord + rhs._x_coord, lhs._y_coord + rhs._y_coord};
|
||||
}
|
||||
|
||||
GridCoord2D operator-(
|
||||
const GridCoord2D& lhs,
|
||||
const GridCoord2D& rhs
|
||||
) {
|
||||
return {lhs._x_coord - rhs._x_coord, lhs._y_coord - rhs._y_coord};
|
||||
}
|
||||
|
||||
GridCoord2D operator*(
|
||||
const GridCoord2D& lhs,
|
||||
const double rhs
|
||||
) {
|
||||
return {static_cast<int>(lhs._x_coord * rhs), static_cast<int>(lhs._y_coord * rhs)};
|
||||
}
|
||||
|
||||
GridCoord2D operator*(
|
||||
const double lhs,
|
||||
const GridCoord2D& rhs
|
||||
) {
|
||||
return {static_cast<int>(rhs._x_coord * lhs), static_cast<int>(rhs._y_coord * lhs)};
|
||||
}
|
||||
|
||||
Grid2D::Grid2D(
|
||||
unsigned int maximum_x,
|
||||
unsigned int maximum_y,
|
||||
bool wrap_x,
|
||||
bool wrap_y
|
||||
) {
|
||||
_maximum_x = maximum_x;
|
||||
_maximum_y = maximum_y;
|
||||
_wrap_x = wrap_x;
|
||||
_wrap_y = wrap_y;
|
||||
|
||||
_agent_grid = new std::vector<AgentID> *[_maximum_x];
|
||||
for (auto i = 0; i < _maximum_x; i++)
|
||||
_agent_grid[i] = new std::vector<AgentID>[_maximum_y];
|
||||
|
||||
_agent_index = new std::map<AgentID, GridCoord2D>;
|
||||
_agent_grid = std::make_unique<std::unordered_multimap<GridCoord2D, AgentID>>();
|
||||
_agent_index = std::make_unique<std::map<AgentID, GridCoord2D>>();
|
||||
}
|
||||
|
||||
Grid2D::~Grid2D() {
|
||||
delete _agent_index;
|
||||
|
||||
for (auto i = 0; i < _maximum_x; i++) delete[] _agent_grid[i];
|
||||
delete[] _agent_grid;
|
||||
AgentID Grid2D::delete_agent(const AgentID agent_id) {
|
||||
return delete_agent(agent_id, get_location_by_agent(agent_id));
|
||||
}
|
||||
|
||||
[[maybe_unused]] bool Grid2D::delete_agent(AgentID agent_id) {
|
||||
auto coord = get_location_by_agent(agent_id);
|
||||
|
||||
return delete_agent(agent_id, coord);
|
||||
}
|
||||
|
||||
bool Grid2D::delete_agent(AgentID agent_id, const GridCoord2D &coord) {
|
||||
auto agent_list = _agent_grid[static_cast<int>(coord.get_x_location())][static_cast<int>(coord.get_y_location())];
|
||||
|
||||
for (auto test_agent_id = agent_list.begin();
|
||||
test_agent_id < agent_list.end(); test_agent_id++) {
|
||||
if (*test_agent_id == agent_id) {
|
||||
agent_list.erase(test_agent_id);
|
||||
AgentID Grid2D::delete_agent(
|
||||
const AgentID agent_id,
|
||||
const GridCoord2D& coord
|
||||
) {
|
||||
for (auto test_agent_id = _agent_grid->find(coord); test_agent_id != _agent_grid->end(); test_agent_id++)
|
||||
if (test_agent_id->second == agent_id) {
|
||||
_agent_grid->erase(test_agent_id);
|
||||
_agent_index->erase(agent_id);
|
||||
return true;
|
||||
return agent_id;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
throw error::AgentNotFound("Agent not found on grid");
|
||||
}
|
||||
|
||||
bool Grid2D::is_location_valid(const GridCoord2D& coord) const {
|
||||
auto x = coord.get_x_location();
|
||||
auto y = coord.get_y_location();
|
||||
auto x = coord.x();
|
||||
auto y = coord.y();
|
||||
|
||||
return (x >= 0 && x < static_cast<int>(_maximum_x) && y >= 0 &&
|
||||
y < static_cast<int>(_maximum_y));
|
||||
return (x >= 0 && x < static_cast<int>(_maximum_x) &&
|
||||
y >= 0 && y < static_cast<int>(_maximum_y));
|
||||
}
|
||||
|
||||
GridCoord2D Grid2D::get_location_by_agent(AgentID agent_id) const {
|
||||
return _agent_index->at(agent_id);
|
||||
bool Grid2D::is_location_empty(const GridCoord2D& coord) const {
|
||||
auto grid_location = _agent_grid->equal_range(coord);
|
||||
return grid_location.first == grid_location.second;
|
||||
}
|
||||
|
||||
bool Grid2D::move_agent(AgentID agent_id, GridCoord2D coord) {
|
||||
GridCoord2D current_location = get_location_by_agent(agent_id);
|
||||
|
||||
if (delete_agent(agent_id, current_location))
|
||||
return add_agent(agent_id, std::move(coord));
|
||||
return false;
|
||||
AgentID Grid2D::move_agent(
|
||||
const AgentID agent_id,
|
||||
const GridCoord2D& coord
|
||||
) {
|
||||
return add_agent(delete_agent(agent_id, get_location_by_agent(agent_id)), coord);
|
||||
}
|
||||
|
||||
[[maybe_unused]] bool Grid2D::get_wrap_x() const { return _wrap_x; }
|
||||
[[maybe_unused]] bool Grid2D::get_wrap_y() const { return _wrap_y; }
|
||||
|
||||
std::vector<GridCoord2D> Grid2D::get_neighborhood(
|
||||
AgentID agent_id, GridNeighborhoodType neighborhood_type,
|
||||
bool includeCenter) const {
|
||||
GridCoord2D location = get_location_by_agent(agent_id);
|
||||
|
||||
return get_neighborhood(location, neighborhood_type, includeCenter);
|
||||
std::shared_ptr<std::unordered_set<GridCoord2D>>
|
||||
Grid2D::get_neighborhood(
|
||||
const AgentID agent_id,
|
||||
const bool include_center,
|
||||
const GridNeighborhoodType neighborhood_type
|
||||
) const {
|
||||
return std::move(get_neighborhood(get_location_by_agent(agent_id), include_center, neighborhood_type));
|
||||
}
|
||||
|
||||
std::vector<GridCoord2D> Grid2D::get_neighborhood(const GridCoord2D& location, GridNeighborhoodType neighborhood_type, bool include_center) const {
|
||||
std::vector<GridCoord2D> neighborhood;
|
||||
auto x = location.get_x_location();
|
||||
auto y = location.get_y_location();
|
||||
std::shared_ptr<std::unordered_set<GridCoord2D>>
|
||||
Grid2D::get_neighborhood(
|
||||
const GridCoord2D& coord,
|
||||
const bool include_center,
|
||||
const GridNeighborhoodType neighborhood_type
|
||||
) const {
|
||||
auto neighborhood = std::make_unique<std::unordered_set<GridCoord2D>>();
|
||||
std::vector<GridCoord2D> directions;
|
||||
|
||||
// We assume our starting position is valid
|
||||
if (include_center) neighborhood.push_back(location);
|
||||
switch (neighborhood_type) {
|
||||
case GridNeighborhoodType::VonNeumann:
|
||||
directions = directions_vonneumann;
|
||||
break;
|
||||
case GridNeighborhoodType::Moore:
|
||||
directions = directions_moore;
|
||||
break;
|
||||
default:
|
||||
throw error::OptionInvalid(
|
||||
fmt::format("Invalid neighborhood type {} given", (unsigned int) neighborhood_type));
|
||||
}
|
||||
|
||||
if (include_center and is_location_valid(coord))
|
||||
neighborhood->insert(coord);
|
||||
|
||||
for (auto& direction : directions) {
|
||||
auto new_location = coord_wrap(coord + direction);
|
||||
|
||||
// N, E, S, W
|
||||
{
|
||||
auto new_location = coord_wrap(GridCoord2D(x, y - 1));
|
||||
if (is_location_valid(new_location))
|
||||
neighborhood.push_back(coord_wrap(new_location));
|
||||
}
|
||||
{
|
||||
auto new_location = coord_wrap(GridCoord2D(x, y + 1));
|
||||
if (is_location_valid(new_location))
|
||||
neighborhood.push_back(coord_wrap(new_location));
|
||||
}
|
||||
{
|
||||
auto new_location = coord_wrap(GridCoord2D(x + 1, y));
|
||||
if (is_location_valid(new_location))
|
||||
neighborhood.push_back(coord_wrap(new_location));
|
||||
}
|
||||
{
|
||||
auto new_location = coord_wrap(GridCoord2D(x - 1, y));
|
||||
if (is_location_valid(new_location))
|
||||
neighborhood.push_back(coord_wrap(new_location));
|
||||
neighborhood->insert(new_location);
|
||||
}
|
||||
|
||||
if (neighborhood_type == GridNeighborhoodType::Moore) {
|
||||
// NE, SE, SW, NW
|
||||
{
|
||||
auto new_location = coord_wrap(GridCoord2D(x + 1, y - 1));
|
||||
if (is_location_valid(new_location))
|
||||
neighborhood.push_back(coord_wrap(new_location));
|
||||
}
|
||||
{
|
||||
auto new_location = coord_wrap(GridCoord2D(x + 1, y + 1));
|
||||
if (is_location_valid(new_location))
|
||||
neighborhood.push_back(coord_wrap(new_location));
|
||||
}
|
||||
{
|
||||
auto new_location = coord_wrap(GridCoord2D(x - 1, y + 1));
|
||||
if (is_location_valid(new_location))
|
||||
neighborhood.push_back(coord_wrap(new_location));
|
||||
}
|
||||
{
|
||||
auto new_location = coord_wrap(GridCoord2D(x - 1, y - 1));
|
||||
if (is_location_valid(new_location))
|
||||
neighborhood.push_back(coord_wrap(new_location));
|
||||
}
|
||||
}
|
||||
|
||||
return neighborhood;
|
||||
return std::move(neighborhood);
|
||||
}
|
||||
|
||||
std::vector<AgentID> *Grid2D::get_location_contents(const GridCoord2D& coord) const {
|
||||
if (is_location_valid(coord) && !is_location_empty(coord))
|
||||
return &_agent_grid[coord.get_x_location()][coord.get_y_location()];
|
||||
std::shared_ptr<std::set<AgentID>> Grid2D::get_location_contents(const GridCoord2D& coord) const {
|
||||
auto agent_ids = std::make_shared<std::set<AgentID>>();
|
||||
|
||||
return nullptr;
|
||||
if (!is_location_valid(coord))
|
||||
throw error::LocationUnavailable(fmt::format("Coordinates {} are invalid", coord.to_string()));
|
||||
if (is_location_empty(coord))
|
||||
return agent_ids;
|
||||
|
||||
auto agent_range = _agent_grid->equal_range(coord);
|
||||
if (agent_range.first == agent_range.second)
|
||||
return agent_ids;
|
||||
|
||||
for (auto i = agent_range.first; i != agent_range.second; i++)
|
||||
agent_ids->insert(i->second);
|
||||
return agent_ids;
|
||||
}
|
||||
|
||||
[[maybe_unused]] unsigned int Grid2D::get_maximum_x() const { return _maximum_x; }
|
||||
[[maybe_unused]] unsigned int Grid2D::get_maximum_y() const { return _maximum_y; }
|
||||
bool Grid2D::get_wrap_x() const {
|
||||
return _wrap_x;
|
||||
}
|
||||
|
||||
bool Grid2D::get_wrap_y() const {
|
||||
return _wrap_y;
|
||||
}
|
||||
|
||||
unsigned int Grid2D::get_maximum_x() const {
|
||||
return _maximum_x;
|
||||
}
|
||||
|
||||
unsigned int Grid2D::get_maximum_y() const {
|
||||
return _maximum_y;
|
||||
}
|
||||
|
||||
GridCoord2D Grid2D::get_location_by_agent(const AgentID& agent_id) const {
|
||||
auto coord = _agent_index->find(agent_id);
|
||||
if (coord == _agent_index->end())
|
||||
throw error::AgentNotFound(fmt::format("Agent {} not found on grid", agent_id.to_string()));
|
||||
return coord->second;
|
||||
}
|
||||
|
||||
GridCoord2D Grid2D::coord_wrap(const GridCoord2D& coord) const {
|
||||
auto x = coord.get_x_location();
|
||||
auto y = coord.get_y_location();
|
||||
auto x = coord.x();
|
||||
auto y = coord.y();
|
||||
|
||||
if (_wrap_x)
|
||||
x = (x + static_cast<int>(_maximum_x)) % static_cast<int>(_maximum_x);
|
||||
@@ -210,11 +286,4 @@ namespace kami {
|
||||
return {x, y};
|
||||
}
|
||||
|
||||
bool Grid2D::is_location_empty(const GridCoord2D& coord) const {
|
||||
auto x = coord.get_x_location();
|
||||
auto y = coord.get_y_location();
|
||||
|
||||
return _agent_grid[x][y].empty();
|
||||
}
|
||||
|
||||
} // namespace kami
|
||||
|
||||
73
src/libkami/model.cc
Normal file
73
src/libkami/model.cc
Normal file
@@ -0,0 +1,73 @@
|
||||
/*-
|
||||
* Copyright (c) 2022 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include <kami/error.h>
|
||||
#include <kami/model.h>
|
||||
#include <kami/scheduler.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
std::shared_ptr<Domain> Model::get_domain() {
|
||||
if (_domain == nullptr)
|
||||
throw error::ResourceNotAvailable("Domain not found in model");
|
||||
return _domain;
|
||||
}
|
||||
|
||||
std::shared_ptr<Domain> Model::set_domain(std::shared_ptr<Domain> domain) {
|
||||
_domain = std::move(domain);
|
||||
return _domain;
|
||||
}
|
||||
|
||||
std::shared_ptr<Population> Model::get_population() {
|
||||
if (_pop == nullptr)
|
||||
throw error::ResourceNotAvailable("Population not found in model");
|
||||
return _pop;
|
||||
}
|
||||
|
||||
std::shared_ptr<Population> Model::set_population(std::shared_ptr<Population> population) {
|
||||
_pop = std::move(population);
|
||||
return _pop;
|
||||
}
|
||||
|
||||
std::shared_ptr<Scheduler> Model::get_scheduler() {
|
||||
if (_sched == nullptr)
|
||||
throw error::ResourceNotAvailable("Scheduler not found in model");
|
||||
return _sched;
|
||||
}
|
||||
|
||||
std::shared_ptr<Scheduler> Model::set_scheduler(std::shared_ptr<Scheduler> scheduler) {
|
||||
_sched = std::move(scheduler);
|
||||
return _sched;
|
||||
}
|
||||
|
||||
std::shared_ptr<Model> Model::step() {
|
||||
_sched->step(shared_from_this());
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
} // namespace kami
|
||||
@@ -23,21 +23,33 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/domain.h>
|
||||
#include <kami/error.h>
|
||||
#include <kami/grid1d.h>
|
||||
#include <kami/multigrid1d.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
bool MultiGrid1D::add_agent(AgentID agent_id, GridCoord1D coord) {
|
||||
if (is_location_valid(coord)) {
|
||||
_agent_index->insert(std::pair<AgentID, GridCoord1D>(agent_id, coord));
|
||||
_agent_grid[coord.get_x_location()].push_back(agent_id);
|
||||
return (true);
|
||||
}
|
||||
MultiGrid1D::MultiGrid1D(
|
||||
unsigned int maximum_x,
|
||||
bool wrap_x
|
||||
)
|
||||
:Grid1D(maximum_x, wrap_x) {
|
||||
}
|
||||
|
||||
return (false);
|
||||
AgentID MultiGrid1D::add_agent(
|
||||
const AgentID agent_id,
|
||||
const GridCoord1D& coord
|
||||
) {
|
||||
if (!is_location_valid(coord))
|
||||
throw error::LocationInvalid(fmt::format("Coordinates {} are invalid", coord.to_string()));
|
||||
|
||||
_agent_index->insert(std::pair<AgentID, GridCoord1D>(agent_id, coord));
|
||||
_agent_grid->insert(std::pair<GridCoord1D, AgentID>(coord, agent_id));
|
||||
return agent_id;
|
||||
}
|
||||
|
||||
} // namespace kami
|
||||
|
||||
@@ -23,22 +23,35 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/domain.h>
|
||||
#include <kami/error.h>
|
||||
#include <kami/grid2d.h>
|
||||
#include <kami/multigrid2d.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
bool MultiGrid2D::add_agent(AgentID agent_id, GridCoord2D coord) {
|
||||
if (is_location_valid(coord)) {
|
||||
_agent_index->insert(std::pair<AgentID, GridCoord2D>(agent_id, coord));
|
||||
_agent_grid[coord.get_x_location()][coord.get_y_location()].push_back(
|
||||
agent_id);
|
||||
return true;
|
||||
}
|
||||
MultiGrid2D::MultiGrid2D(
|
||||
unsigned int maximum_x,
|
||||
unsigned int maximum_y,
|
||||
bool wrap_x,
|
||||
bool wrap_y
|
||||
)
|
||||
:Grid2D(maximum_x, maximum_y, wrap_x, wrap_y) {
|
||||
}
|
||||
|
||||
return false;
|
||||
AgentID MultiGrid2D::add_agent(
|
||||
const AgentID agent_id,
|
||||
const GridCoord2D& coord
|
||||
) {
|
||||
if (!is_location_valid(coord))
|
||||
throw error::LocationInvalid(fmt::format("Coordinates {} are invalid", coord.to_string()));
|
||||
|
||||
_agent_index->insert(std::pair<AgentID, GridCoord2D>(agent_id, coord));
|
||||
_agent_grid->insert(std::pair<GridCoord2D, AgentID>(coord, agent_id));
|
||||
return agent_id;
|
||||
}
|
||||
|
||||
} // namespace kami
|
||||
|
||||
73
src/libkami/population.cc
Normal file
73
src/libkami/population.cc
Normal file
@@ -0,0 +1,73 @@
|
||||
/*-
|
||||
* Copyright (c) 2020 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/error.h>
|
||||
#include <kami/population.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
AgentID Population::add_agent(const std::shared_ptr<Agent>& agent) noexcept {
|
||||
auto agent_id = agent->get_agent_id();
|
||||
_agent_map.insert(std::pair<AgentID, std::shared_ptr<Agent>>(agent_id, agent));
|
||||
return agent->get_agent_id();
|
||||
}
|
||||
|
||||
std::shared_ptr<Agent> Population::delete_agent(const AgentID agent_id) {
|
||||
auto agent_it = _agent_map.find(agent_id);
|
||||
|
||||
if (agent_it == _agent_map.end())
|
||||
throw error::ResourceNotAvailable("Agent not found in population");
|
||||
|
||||
auto agent = agent_it->second;
|
||||
_agent_map.erase(agent_it);
|
||||
return std::move(agent);
|
||||
}
|
||||
|
||||
std::shared_ptr<Agent> Population::get_agent_by_id(const AgentID agent_id) const {
|
||||
auto agent_it = _agent_map.find(agent_id);
|
||||
|
||||
if (agent_it == _agent_map.end())
|
||||
throw error::AgentNotFound("Agent not found in population");
|
||||
|
||||
return agent_it->second;
|
||||
}
|
||||
|
||||
std::unique_ptr<std::vector<AgentID>> Population::get_agent_list() const {
|
||||
auto key_selector = [](auto pair)
|
||||
{
|
||||
return pair.first;
|
||||
};
|
||||
auto agent_ids = std::make_unique<std::vector<AgentID>>(_agent_map.size());
|
||||
|
||||
transform(_agent_map.begin(), _agent_map.end(), agent_ids->begin(), key_selector);
|
||||
return std::move(agent_ids);
|
||||
}
|
||||
|
||||
} // namespace kami
|
||||
@@ -23,29 +23,54 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <kami/error.h>
|
||||
#include <kami/model.h>
|
||||
#include <kami/random.h>
|
||||
#include <kami/sequential.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
RandomScheduler::RandomScheduler(Model *model, std::shared_ptr<std::mt19937> rng)
|
||||
: SequentialScheduler(model) {
|
||||
this->set_rng(std::move(rng));
|
||||
}
|
||||
|
||||
void RandomScheduler::step() {
|
||||
shuffle(_agent_list.begin(), _agent_list.end(), *_rng);
|
||||
this->SequentialScheduler::step();
|
||||
}
|
||||
|
||||
void RandomScheduler::set_rng(std::shared_ptr<std::mt19937> rng) {
|
||||
RandomScheduler::RandomScheduler(std::shared_ptr<std::mt19937> rng) {
|
||||
this->_rng = std::move(rng);
|
||||
}
|
||||
|
||||
[[maybe_unused]] std::shared_ptr<std::mt19937> RandomScheduler::get_rng() { return (_rng); }
|
||||
std::unique_ptr<std::vector<AgentID>>
|
||||
RandomScheduler::step(
|
||||
std::shared_ptr<Model> model,
|
||||
std::unique_ptr<std::vector<AgentID>> agent_list
|
||||
) {
|
||||
if (_rng == nullptr)
|
||||
throw error::ResourceNotAvailable("No random number generator available");
|
||||
|
||||
shuffle(agent_list->begin(), agent_list->end(), *_rng);
|
||||
return std::move(this->SequentialScheduler::step(model, std::move(agent_list)));
|
||||
}
|
||||
|
||||
std::unique_ptr<std::vector<AgentID>>
|
||||
RandomScheduler::step(
|
||||
std::shared_ptr<ReporterModel> model,
|
||||
std::unique_ptr<std::vector<AgentID>> agent_list
|
||||
) {
|
||||
if (_rng == nullptr)
|
||||
throw error::ResourceNotAvailable("No random number generator available");
|
||||
|
||||
shuffle(agent_list->begin(), agent_list->end(), *_rng);
|
||||
return std::move(this->SequentialScheduler::step(model, std::move(agent_list)));
|
||||
}
|
||||
|
||||
std::shared_ptr<std::mt19937> RandomScheduler::set_rng(std::shared_ptr<std::mt19937> rng) {
|
||||
this->_rng = std::move(rng);
|
||||
return _rng;
|
||||
}
|
||||
|
||||
std::shared_ptr<std::mt19937> RandomScheduler::get_rng() {
|
||||
return (this->_rng);
|
||||
}
|
||||
|
||||
} // namespace kami
|
||||
|
||||
130
src/libkami/reporter.cc
Normal file
130
src/libkami/reporter.cc
Normal file
@@ -0,0 +1,130 @@
|
||||
/*-
|
||||
* Copyright (c) 2022 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <kami/population.h>
|
||||
#include <kami/reporter.h>
|
||||
#include <kami/scheduler.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
ReporterModel::ReporterModel() {
|
||||
_step_count = 0;
|
||||
_rpt = std::make_shared<Reporter>();
|
||||
}
|
||||
|
||||
std::shared_ptr<Model> ReporterModel::step() {
|
||||
_step_count++;
|
||||
|
||||
auto ret = _sched->step(std::static_pointer_cast<ReporterModel>(shared_from_this()));
|
||||
auto rpt = _rpt->collect(std::static_pointer_cast<ReporterModel>(shared_from_this()));
|
||||
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
std::unique_ptr<nlohmann::json> ReporterModel::report() {
|
||||
return std::move(_rpt->report(std::static_pointer_cast<ReporterModel>(shared_from_this())));
|
||||
}
|
||||
|
||||
inline unsigned int ReporterModel::get_step_id() {
|
||||
return _step_count;
|
||||
}
|
||||
|
||||
Reporter::Reporter() {
|
||||
_report_data = std::make_unique<std::vector<nlohmann::json>>();
|
||||
}
|
||||
|
||||
std::shared_ptr<Reporter> Reporter::clear() {
|
||||
// I _can_ do this in one line, but I won't
|
||||
_report_data.reset();
|
||||
_report_data = std::make_unique<std::vector<nlohmann::json>>();
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
std::unique_ptr<nlohmann::json>
|
||||
Reporter::collect(const std::shared_ptr<ReporterModel>& model) {
|
||||
auto pop = model->get_population();
|
||||
return collect(model, pop);
|
||||
}
|
||||
|
||||
std::unique_ptr<nlohmann::json>
|
||||
Reporter::collect(
|
||||
const std::shared_ptr<ReporterModel>& model,
|
||||
const std::shared_ptr<Population>& pop
|
||||
) {
|
||||
auto agent_list = pop->get_agent_list();
|
||||
return collect(model, agent_list);
|
||||
}
|
||||
|
||||
std::unique_ptr<nlohmann::json>
|
||||
Reporter::collect(
|
||||
const std::shared_ptr<ReporterModel>& model,
|
||||
const std::unique_ptr<std::vector<AgentID>>& agent_list
|
||||
) {
|
||||
auto collection_array = std::vector<nlohmann::json>();
|
||||
|
||||
for (auto& agent_id : *agent_list) {
|
||||
auto agent_data = nlohmann::json();
|
||||
auto agent = std::static_pointer_cast<ReporterAgent>(model->get_population()->get_agent_by_id(agent_id));
|
||||
|
||||
agent_data["agent_id"] = agent_id.to_string();
|
||||
|
||||
auto agent_collection = agent->collect();
|
||||
if (agent_collection)
|
||||
agent_data["data"] = *agent_collection;
|
||||
|
||||
collection_array.push_back(agent_data);
|
||||
}
|
||||
auto model_data = model->collect();
|
||||
auto agent_collection = std::make_unique<nlohmann::json>(collection_array);
|
||||
auto collection = std::make_unique<nlohmann::json>();
|
||||
|
||||
(*collection)["step_id"] = model->get_step_id();
|
||||
if (model_data)
|
||||
(*collection)["model_data"] = *model_data;
|
||||
(*collection)["agent_data"] = *agent_collection;
|
||||
|
||||
_report_data->push_back(*collection);
|
||||
|
||||
return std::move(collection);
|
||||
}
|
||||
|
||||
std::unique_ptr<nlohmann::json> Reporter::report(const std::shared_ptr<ReporterModel>& model) {
|
||||
auto json_data = std::make_unique<nlohmann::json>(*_report_data);
|
||||
return std::move(json_data);
|
||||
}
|
||||
|
||||
AgentID ReporterAgent::step(std::shared_ptr<Model> model) {
|
||||
return get_agent_id();
|
||||
}
|
||||
|
||||
|
||||
} // namespace kami
|
||||
@@ -23,37 +23,61 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/model.h>
|
||||
#include <kami/reporter.h>
|
||||
#include <kami/sequential.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
SequentialScheduler::SequentialScheduler(Model *model) {
|
||||
_step_counter = 0;
|
||||
_model = model;
|
||||
std::unique_ptr<std::vector<AgentID>> SequentialScheduler::step(std::shared_ptr<Model> model) {
|
||||
auto population = model->get_population();
|
||||
return std::move(this->step(model, population->get_agent_list()));
|
||||
}
|
||||
|
||||
void SequentialScheduler::add_agent(AgentID agent_id) {
|
||||
_agent_list.push_back(agent_id);
|
||||
std::unique_ptr<std::vector<AgentID>> SequentialScheduler::step(std::shared_ptr<ReporterModel> model) {
|
||||
auto population = model->get_population();
|
||||
return std::move(this->step(model, population->get_agent_list()));
|
||||
}
|
||||
|
||||
void SequentialScheduler::delete_agent(AgentID agent_id) {
|
||||
for (auto agent_list_iter = _agent_list.begin(); agent_list_iter < _agent_list.end(); agent_list_iter++)
|
||||
if (*agent_list_iter == agent_id) _agent_list.erase(agent_list_iter);
|
||||
// ERROR HERE
|
||||
}
|
||||
std::unique_ptr<std::vector<AgentID>>
|
||||
SequentialScheduler::step(
|
||||
std::shared_ptr<Model> model,
|
||||
std::unique_ptr<std::vector<AgentID>> agent_list
|
||||
) {
|
||||
auto return_agent_list = std::make_unique<std::vector<AgentID>>();
|
||||
auto population = model->get_population();
|
||||
|
||||
void SequentialScheduler::step() {
|
||||
_step_counter++;
|
||||
Scheduler::_step_counter++;
|
||||
for (auto& agent_id : *agent_list) {
|
||||
auto agent = population->get_agent_by_id(agent_id);
|
||||
|
||||
for (auto agent_list_iter = _agent_list.begin();
|
||||
agent_list_iter < _agent_list.end(); agent_list_iter++) {
|
||||
Agent *agent = _model->get_agent_by_id(*agent_list_iter);
|
||||
if (agent != nullptr) agent->step();
|
||||
agent->step(model);
|
||||
return_agent_list->push_back(agent_id);
|
||||
}
|
||||
|
||||
return std::move(return_agent_list);
|
||||
}
|
||||
|
||||
std::unique_ptr<std::vector<AgentID>>
|
||||
SequentialScheduler::step(
|
||||
std::shared_ptr<ReporterModel> model,
|
||||
std::unique_ptr<std::vector<AgentID>> agent_list
|
||||
) {
|
||||
auto return_agent_list = std::make_unique<std::vector<AgentID>>();
|
||||
auto population = model->get_population();
|
||||
|
||||
Scheduler::_step_counter++;
|
||||
for (auto& agent_id : *agent_list) {
|
||||
auto agent = population->get_agent_by_id(agent_id);
|
||||
|
||||
agent->step(model);
|
||||
return_agent_list->push_back(agent_id);
|
||||
}
|
||||
|
||||
return std::move(return_agent_list);
|
||||
}
|
||||
|
||||
} // namespace kami
|
||||
|
||||
@@ -23,19 +23,33 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/error.h>
|
||||
#include <kami/sologrid1d.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
bool SoloGrid1D::add_agent(AgentID agent_id, GridCoord1D coord) {
|
||||
if (is_location_valid(coord) & is_location_empty(coord)) {
|
||||
_agent_index->insert(std::pair<AgentID, GridCoord1D>(agent_id, coord));
|
||||
_agent_grid[coord.get_x_location()].push_back(agent_id);
|
||||
return true;
|
||||
}
|
||||
SoloGrid1D::SoloGrid1D(
|
||||
unsigned int maximum_x,
|
||||
bool wrap_x
|
||||
)
|
||||
:Grid1D(maximum_x, wrap_x) {
|
||||
}
|
||||
|
||||
return false;
|
||||
AgentID SoloGrid1D::add_agent(
|
||||
const AgentID agent_id,
|
||||
const GridCoord1D& coord
|
||||
) {
|
||||
if (!is_location_valid(coord))
|
||||
throw error::LocationInvalid(fmt::format("Coordinates {} are invalid", coord.to_string()));
|
||||
if (!is_location_empty(coord))
|
||||
throw error::LocationUnavailable(fmt::format("Coordinates {} already occupied", coord.to_string()));
|
||||
|
||||
_agent_index->insert(std::pair<AgentID, GridCoord1D>(agent_id, coord));
|
||||
_agent_grid->insert(std::pair<GridCoord1D, AgentID>(coord, agent_id));
|
||||
return agent_id;
|
||||
}
|
||||
|
||||
} // namespace kami
|
||||
|
||||
@@ -23,21 +23,37 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/sologrid2d.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/error.h>
|
||||
#include <kami/sologrid2d.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
bool SoloGrid2D::add_agent(AgentID agent_id, GridCoord2D coord) {
|
||||
if (is_location_valid(coord) & is_location_empty(coord)) {
|
||||
_agent_index->insert(std::pair<AgentID, GridCoord2D>(agent_id, coord));
|
||||
_agent_grid[coord.get_x_location()][coord.get_y_location()].push_back(agent_id);
|
||||
return true;
|
||||
}
|
||||
SoloGrid2D::SoloGrid2D(
|
||||
unsigned int maximum_x,
|
||||
unsigned int maximum_y,
|
||||
bool wrap_x,
|
||||
bool wrap_y
|
||||
)
|
||||
:Grid2D(maximum_x, maximum_y, wrap_x, wrap_y) {
|
||||
}
|
||||
|
||||
return false;
|
||||
AgentID SoloGrid2D::add_agent(
|
||||
const AgentID agent_id,
|
||||
const GridCoord2D& coord
|
||||
) {
|
||||
if (!is_location_valid(coord))
|
||||
throw error::LocationInvalid(fmt::format("Coordinates {} are invalid", coord.to_string()));
|
||||
if (!is_location_empty(coord))
|
||||
throw error::LocationUnavailable(fmt::format("Coordinates {} already occupied", coord.to_string()));
|
||||
|
||||
_agent_index->insert(std::pair<AgentID, GridCoord2D>(agent_id, coord));
|
||||
_agent_grid->insert(std::pair<GridCoord2D, AgentID>(coord, agent_id));
|
||||
return agent_id;
|
||||
}
|
||||
|
||||
} // namespace kami
|
||||
|
||||
@@ -23,40 +23,61 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/model.h>
|
||||
#include <kami/reporter.h>
|
||||
#include <kami/sequential.h>
|
||||
#include <kami/staged.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
StagedScheduler::StagedScheduler(Model *model) {
|
||||
_step_counter = 0;
|
||||
_model = model;
|
||||
std::unique_ptr<std::vector<AgentID>>
|
||||
StagedScheduler::step(
|
||||
std::shared_ptr<Model> model,
|
||||
std::unique_ptr<std::vector<AgentID>> agent_list
|
||||
) {
|
||||
auto stepped_agent_list = this->SequentialScheduler::step(model, std::move(agent_list));
|
||||
return std::move(this->advance(model, std::move(stepped_agent_list)));
|
||||
}
|
||||
|
||||
void StagedScheduler::add_agent(AgentID agent_id) {
|
||||
_agent_list.push_back(agent_id);
|
||||
std::unique_ptr<std::vector<AgentID>>
|
||||
StagedScheduler::step(
|
||||
std::shared_ptr<ReporterModel> model,
|
||||
std::unique_ptr<std::vector<AgentID>> agent_list
|
||||
) {
|
||||
auto stepped_agent_list = this->SequentialScheduler::step(model, std::move(agent_list));
|
||||
return std::move(this->advance(model, std::move(stepped_agent_list)));
|
||||
}
|
||||
|
||||
void StagedScheduler::delete_agent(AgentID agent_id) {
|
||||
for (auto agent_list_iter = _agent_list.begin(); agent_list_iter < _agent_list.end(); agent_list_iter++)
|
||||
if (*agent_list_iter == agent_id) _agent_list.erase(agent_list_iter);
|
||||
std::unique_ptr<std::vector<AgentID>> StagedScheduler::advance(std::shared_ptr<Model> model) {
|
||||
auto population = model->get_population();
|
||||
return std::move(this->advance(model, population->get_agent_list()));
|
||||
}
|
||||
|
||||
void StagedScheduler::step() {
|
||||
_step_counter++;
|
||||
std::unique_ptr<std::vector<AgentID>> StagedScheduler::advance(std::shared_ptr<ReporterModel> model) {
|
||||
auto population = model->get_population();
|
||||
return std::move(this->advance(model, population->get_agent_list()));
|
||||
}
|
||||
|
||||
for (auto agent_list_iter = _agent_list.begin(); agent_list_iter < _agent_list.end(); agent_list_iter++) {
|
||||
auto *agent = dynamic_cast<StagedAgent *>(_model->get_agent_by_id(*agent_list_iter));
|
||||
if (agent != nullptr) agent->step();
|
||||
std::unique_ptr<std::vector<AgentID>>
|
||||
StagedScheduler::advance(
|
||||
std::shared_ptr<Model> model,
|
||||
std::unique_ptr<std::vector<AgentID>> agent_list
|
||||
) {
|
||||
auto return_agent_list = std::make_unique<std::vector<AgentID>>();
|
||||
auto population = model->get_population();
|
||||
|
||||
for (auto& agent_id : *agent_list) {
|
||||
auto agent = std::static_pointer_cast<StagedAgent>(population->get_agent_by_id(agent_id));
|
||||
|
||||
agent->advance(model);
|
||||
return_agent_list->push_back(agent_id);
|
||||
}
|
||||
|
||||
for (auto agent_list_iter = _agent_list.begin(); agent_list_iter < _agent_list.end(); agent_list_iter++) {
|
||||
auto *agent = dynamic_cast<StagedAgent *>(_model->get_agent_by_id(*agent_list_iter));
|
||||
if (agent != nullptr) agent->advance();
|
||||
}
|
||||
return std::move(return_agent_list);
|
||||
}
|
||||
|
||||
} // namespace kami
|
||||
|
||||
@@ -2,34 +2,18 @@
|
||||
# This CMakeLists.txt contains the build descriptions for unit tests
|
||||
################################################################################
|
||||
|
||||
cmake_minimum_required(VERSION 3.13.0 FATAL_ERROR)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
find_package(nlohmann_json 3.11.1 REQUIRED)
|
||||
|
||||
find_package(spdlog)
|
||||
|
||||
create_test( NAME
|
||||
unit-kami-agentid
|
||||
SOURCES
|
||||
unit-kami-agentid.cc
|
||||
PUBLIC_LINKED_TARGETS
|
||||
kami::libkami
|
||||
fmt
|
||||
spdlog::spdlog
|
||||
${COVERAGE_LIBS}
|
||||
COMMAND
|
||||
unit-kami-agentid
|
||||
PUBLIC_COMPILE_FEATURES
|
||||
${COVERAGE_FLAGS})
|
||||
|
||||
create_test( NAME
|
||||
unit-kami-agent
|
||||
SOURCES
|
||||
unit-kami-agent.cc
|
||||
PUBLIC_LINKED_TARGETS
|
||||
kami::libkami
|
||||
fmt
|
||||
spdlog::spdlog
|
||||
${COVERAGE_LIBS}
|
||||
COMMAND
|
||||
unit-kami-agent
|
||||
PUBLIC_COMPILE_FEATURES
|
||||
${COVERAGE_FLAGS})
|
||||
file(GLOB test_modules "${CMAKE_CURRENT_SOURCE_DIR}/*.cc")
|
||||
FOREACH (test_module ${test_modules})
|
||||
cmake_path(GET test_module STEM test_src)
|
||||
create_test(
|
||||
NAME ${test_src}
|
||||
SOURCES ${test_src}.cc
|
||||
PUBLIC_LINKED_TARGETS gmock gtest kami::libkami Threads::Threads nlohmann_json::nlohmann_json
|
||||
COMMAND ${test_src}
|
||||
PUBLIC_COMPILE_FEATURES ${COVERAGE_FLAGS}
|
||||
)
|
||||
ENDFOREACH ()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*-
|
||||
* Copyright (c) 2020 The Johns Hopkins University Applied Physics
|
||||
* Copyright (c) 2022 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
@@ -23,37 +23,78 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/config.h>
|
||||
#include <kami/model.h>
|
||||
|
||||
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <CLI/App.hpp>
|
||||
#include <CLI/Config.hpp>
|
||||
#include <CLI/Formatter.hpp>
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace kami;
|
||||
using namespace std;
|
||||
|
||||
class TestAgent : public Agent {
|
||||
void step() override {};
|
||||
class TestAgent
|
||||
: public Agent {
|
||||
public:
|
||||
AgentID step(shared_ptr<Model> model) override {
|
||||
return get_agent_id();
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
string ident = "unit-kami-agent";
|
||||
CLI::App app{ident};
|
||||
auto console = spdlog::stdout_color_st(ident);
|
||||
string logLevelOption = "info";
|
||||
class TestModel
|
||||
: public Model {
|
||||
};
|
||||
|
||||
app.add_option("-l", logLevelOption, "Set the logging level")->check(CLI::IsMember(SPDLOG_LEVEL_NAMES));
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
class AgentTest
|
||||
: public ::testing::Test {
|
||||
protected:
|
||||
TestAgent agent_foo;
|
||||
TestAgent agent_bar;
|
||||
shared_ptr<TestModel> model_world = nullptr;
|
||||
|
||||
console->set_level(spdlog::level::from_str(logLevelOption));
|
||||
console->info("Compiled with Kami/{}, log level {}", KAMI_VERSION_STRING, logLevelOption);
|
||||
void SetUp() override {
|
||||
model_world = make_shared<TestModel>();
|
||||
}
|
||||
};
|
||||
|
||||
TestAgent test_agent;
|
||||
console->debug("Successfully created Agent with ID {}", test_agent.get_agent_id().to_string());
|
||||
TEST(Agent, DefaultConstructor) {
|
||||
EXPECT_NO_THROW(
|
||||
const TestAgent agent_baz;
|
||||
const TestAgent agent_qux;
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(AgentTest, equivalance) {
|
||||
EXPECT_EQ(agent_foo, agent_foo);
|
||||
EXPECT_NE(agent_foo, agent_bar);
|
||||
}
|
||||
|
||||
TEST_F(AgentTest, get_agent_id) {
|
||||
EXPECT_EQ(agent_foo.get_agent_id(), agent_foo.get_agent_id());
|
||||
EXPECT_NE(agent_bar.get_agent_id(), agent_foo.get_agent_id());
|
||||
}
|
||||
|
||||
TEST_F(AgentTest, step) {
|
||||
EXPECT_EQ(agent_foo.get_agent_id(), agent_foo.step(model_world));
|
||||
EXPECT_NE(agent_bar.get_agent_id(), agent_foo.step(model_world));
|
||||
}
|
||||
|
||||
TEST_F(AgentTest, equality) {
|
||||
EXPECT_TRUE(agent_foo == agent_foo);
|
||||
EXPECT_TRUE(agent_bar == agent_bar);
|
||||
}
|
||||
|
||||
TEST_F(AgentTest, inequality) {
|
||||
EXPECT_TRUE(agent_foo != agent_bar);
|
||||
EXPECT_FALSE(agent_bar != agent_bar);
|
||||
}
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*-
|
||||
* Copyright (c) 2020 The Johns Hopkins University Applied Physics
|
||||
* Copyright (c) 2022 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
@@ -23,33 +23,51 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/config.h>
|
||||
|
||||
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <CLI/App.hpp>
|
||||
#include <CLI/Config.hpp>
|
||||
#include <CLI/Formatter.hpp>
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace kami;
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
string ident = "unit-kami-agentid";
|
||||
CLI::App app{ident};
|
||||
auto console = spdlog::stdout_color_st(ident);
|
||||
string logLevelOption = "info";
|
||||
class AgentIDTest
|
||||
: public ::testing::Test {
|
||||
protected:
|
||||
AgentID agent_id_foo;
|
||||
AgentID agent_id_bar;
|
||||
};
|
||||
|
||||
app.add_option("-l", logLevelOption, "Set the logging level")->check(CLI::IsMember(SPDLOG_LEVEL_NAMES));
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
console->set_level(spdlog::level::from_str(logLevelOption));
|
||||
console->info("Compiled with Kami/{}, log level {}", KAMI_VERSION_STRING, logLevelOption);
|
||||
|
||||
AgentID testAgentID;
|
||||
console->debug("Successfully created AgentID with ID {}", testAgentID.to_string());
|
||||
TEST_F(AgentIDTest, DefaultConstructor) {
|
||||
EXPECT_EQ(agent_id_foo, agent_id_foo);
|
||||
EXPECT_NE(agent_id_foo, agent_id_bar);
|
||||
}
|
||||
|
||||
TEST_F(AgentIDTest, to_string) {
|
||||
EXPECT_THAT(agent_id_foo.to_string(), testing::MatchesRegex("[0-9]+"));
|
||||
EXPECT_THAT(agent_id_bar.to_string(), testing::MatchesRegex("[0-9]+"));
|
||||
}
|
||||
|
||||
TEST_F(AgentIDTest, equality) {
|
||||
EXPECT_TRUE(agent_id_foo == agent_id_foo);
|
||||
EXPECT_TRUE(agent_id_bar == agent_id_bar);
|
||||
EXPECT_FALSE(agent_id_foo == agent_id_bar);
|
||||
}
|
||||
|
||||
TEST_F(AgentIDTest, inequality) {
|
||||
EXPECT_TRUE(agent_id_foo != agent_id_bar);
|
||||
EXPECT_FALSE(agent_id_bar != agent_id_bar);
|
||||
}
|
||||
|
||||
TEST_F(AgentIDTest, ordering) {
|
||||
EXPECT_TRUE(agent_id_foo < agent_id_bar);
|
||||
EXPECT_FALSE(agent_id_bar < agent_id_foo);
|
||||
}
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
|
||||
95
test/unit-kami-gridcoord1d.cc
Normal file
95
test/unit-kami-gridcoord1d.cc
Normal file
@@ -0,0 +1,95 @@
|
||||
/*-
|
||||
* Copyright (c) 2022 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <kami/grid1d.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace kami;
|
||||
|
||||
class GridCoord1DTest
|
||||
: public ::testing::Test {
|
||||
protected:
|
||||
GridCoord1D gridcoord1d_foo = GridCoord1D(0);
|
||||
GridCoord1D gridcoord1d_bar = GridCoord1D(1);
|
||||
GridCoord1D gridcoord1d_baz = GridCoord1D(-1);
|
||||
GridCoord1D gridcoord1d_qux = GridCoord1D(0);
|
||||
};
|
||||
|
||||
TEST_F(GridCoord1DTest, DefaultConstructor) {
|
||||
EXPECT_EQ(gridcoord1d_foo, gridcoord1d_foo);
|
||||
EXPECT_EQ(gridcoord1d_foo, gridcoord1d_qux);
|
||||
|
||||
EXPECT_NE(gridcoord1d_foo, gridcoord1d_bar);
|
||||
EXPECT_NE(gridcoord1d_foo, gridcoord1d_baz);
|
||||
EXPECT_NE(gridcoord1d_bar, gridcoord1d_baz);
|
||||
}
|
||||
|
||||
TEST_F(GridCoord1DTest, to_string) {
|
||||
EXPECT_THAT(gridcoord1d_foo.to_string(), "(0)");
|
||||
EXPECT_THAT(gridcoord1d_bar.to_string(), "(1)");
|
||||
EXPECT_THAT(gridcoord1d_baz.to_string(), "(-1)");
|
||||
}
|
||||
|
||||
TEST_F(GridCoord1DTest, equality) {
|
||||
EXPECT_TRUE(gridcoord1d_foo == gridcoord1d_foo);
|
||||
EXPECT_TRUE(gridcoord1d_foo == gridcoord1d_qux);
|
||||
|
||||
EXPECT_FALSE(gridcoord1d_foo == gridcoord1d_bar);
|
||||
EXPECT_FALSE(gridcoord1d_foo == gridcoord1d_baz);
|
||||
EXPECT_FALSE(gridcoord1d_bar == gridcoord1d_baz);
|
||||
}
|
||||
|
||||
TEST_F(GridCoord1DTest, inequality) {
|
||||
EXPECT_FALSE(gridcoord1d_foo != gridcoord1d_foo);
|
||||
EXPECT_FALSE(gridcoord1d_foo != gridcoord1d_qux);
|
||||
|
||||
EXPECT_TRUE(gridcoord1d_foo != gridcoord1d_bar);
|
||||
EXPECT_TRUE(gridcoord1d_foo != gridcoord1d_baz);
|
||||
EXPECT_TRUE(gridcoord1d_bar != gridcoord1d_baz);
|
||||
}
|
||||
|
||||
TEST_F(GridCoord1DTest, x) {
|
||||
EXPECT_TRUE(gridcoord1d_foo.x() == 0);
|
||||
EXPECT_TRUE(gridcoord1d_bar.x() == 1);
|
||||
EXPECT_TRUE(gridcoord1d_baz.x() == -1);
|
||||
|
||||
EXPECT_TRUE(gridcoord1d_foo.x() == gridcoord1d_foo.x());
|
||||
EXPECT_TRUE(gridcoord1d_foo.x() == gridcoord1d_qux.x());
|
||||
|
||||
EXPECT_FALSE(gridcoord1d_foo.x() == gridcoord1d_bar.x());
|
||||
EXPECT_FALSE(gridcoord1d_foo.x() == gridcoord1d_baz.x());
|
||||
EXPECT_FALSE(gridcoord1d_bar.x() == gridcoord1d_baz.x());
|
||||
}
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
155
test/unit-kami-gridcoord2d.cc
Normal file
155
test/unit-kami-gridcoord2d.cc
Normal file
@@ -0,0 +1,155 @@
|
||||
/*-
|
||||
* Copyright (c) 2022 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <kami/grid2d.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace kami;
|
||||
|
||||
class GridCoord2DTest
|
||||
: public ::testing::Test {
|
||||
protected:
|
||||
GridCoord2D gridcoord2d_foo = GridCoord2D(0, 0);
|
||||
GridCoord2D gridcoord2d_bar = GridCoord2D(1, 1);
|
||||
GridCoord2D gridcoord2d_baz = GridCoord2D(-1, -1);
|
||||
GridCoord2D gridcoord2d_qux = GridCoord2D(0, 1);
|
||||
GridCoord2D gridcoord2d_qu2 = GridCoord2D(1, 0);
|
||||
};
|
||||
|
||||
TEST_F(GridCoord2DTest, DefaultConstructor) {
|
||||
EXPECT_EQ(gridcoord2d_foo, gridcoord2d_foo);
|
||||
|
||||
EXPECT_NE(gridcoord2d_foo, gridcoord2d_bar);
|
||||
EXPECT_NE(gridcoord2d_foo, gridcoord2d_baz);
|
||||
EXPECT_NE(gridcoord2d_bar, gridcoord2d_baz);
|
||||
EXPECT_NE(gridcoord2d_foo, gridcoord2d_qux);
|
||||
EXPECT_NE(gridcoord2d_foo, gridcoord2d_qu2);
|
||||
|
||||
}
|
||||
|
||||
TEST_F(GridCoord2DTest, to_string) {
|
||||
const GridCoord2D gridcoord2d_foo(0, 0);
|
||||
const GridCoord2D gridcoord2d_bar(1, 1);
|
||||
const GridCoord2D gridcoord2d_baz(-1, -1);
|
||||
const GridCoord2D gridcoord2d_qux(0, 1);
|
||||
const GridCoord2D gridcoord2d_qu2(1, 0);
|
||||
|
||||
EXPECT_THAT(gridcoord2d_foo.to_string(), "(0, 0)");
|
||||
EXPECT_THAT(gridcoord2d_bar.to_string(), "(1, 1)");
|
||||
EXPECT_THAT(gridcoord2d_baz.to_string(), "(-1, -1)");
|
||||
EXPECT_THAT(gridcoord2d_qux.to_string(), "(0, 1)");
|
||||
EXPECT_THAT(gridcoord2d_qu2.to_string(), "(1, 0)");
|
||||
}
|
||||
|
||||
TEST_F(GridCoord2DTest, Equality) {
|
||||
const GridCoord2D gridcoord2d_foo(0, 0);
|
||||
const GridCoord2D gridcoord2d_bar(1, 1);
|
||||
const GridCoord2D gridcoord2d_baz(-1, -1);
|
||||
const GridCoord2D gridcoord2d_qux(0, 1);
|
||||
const GridCoord2D gridcoord2d_qu2(1, 0);
|
||||
|
||||
EXPECT_TRUE(gridcoord2d_foo == gridcoord2d_foo);
|
||||
|
||||
EXPECT_FALSE(gridcoord2d_foo == gridcoord2d_bar);
|
||||
EXPECT_FALSE(gridcoord2d_foo == gridcoord2d_baz);
|
||||
EXPECT_FALSE(gridcoord2d_bar == gridcoord2d_baz);
|
||||
EXPECT_FALSE(gridcoord2d_foo == gridcoord2d_qux);
|
||||
EXPECT_FALSE(gridcoord2d_qux == gridcoord2d_qu2);
|
||||
EXPECT_FALSE(gridcoord2d_qux == gridcoord2d_qu2);
|
||||
}
|
||||
|
||||
TEST_F(GridCoord2DTest, Inequality) {
|
||||
const GridCoord2D gridcoord2d_foo(0, 0);
|
||||
const GridCoord2D gridcoord2d_bar(1, 1);
|
||||
const GridCoord2D gridcoord2d_baz(-1, -1);
|
||||
const GridCoord2D gridcoord2d_qux(0, 1);
|
||||
const GridCoord2D gridcoord2d_qu2(1, 0);
|
||||
|
||||
EXPECT_FALSE(gridcoord2d_foo != gridcoord2d_foo);
|
||||
|
||||
EXPECT_TRUE(gridcoord2d_foo != gridcoord2d_qux);
|
||||
EXPECT_TRUE(gridcoord2d_foo != gridcoord2d_bar);
|
||||
EXPECT_TRUE(gridcoord2d_foo != gridcoord2d_baz);
|
||||
EXPECT_TRUE(gridcoord2d_bar != gridcoord2d_baz);
|
||||
EXPECT_TRUE(gridcoord2d_qux != gridcoord2d_qu2);
|
||||
}
|
||||
|
||||
TEST_F(GridCoord2DTest, x) {
|
||||
const GridCoord2D gridcoord2d_foo(0, 0);
|
||||
const GridCoord2D gridcoord2d_bar(1, 1);
|
||||
const GridCoord2D gridcoord2d_baz(-1, -1);
|
||||
const GridCoord2D gridcoord2d_qux(0, 1);
|
||||
const GridCoord2D gridcoord2d_qu2(1, 0);
|
||||
|
||||
EXPECT_TRUE(gridcoord2d_foo.x() == 0);
|
||||
EXPECT_TRUE(gridcoord2d_bar.x() == 1);
|
||||
EXPECT_TRUE(gridcoord2d_baz.x() == -1);
|
||||
EXPECT_FALSE(gridcoord2d_qux.x() == -1);
|
||||
EXPECT_FALSE(gridcoord2d_qu2.x() == -1);
|
||||
|
||||
EXPECT_TRUE(gridcoord2d_foo.x() == gridcoord2d_foo.x());
|
||||
EXPECT_TRUE(gridcoord2d_foo.x() == gridcoord2d_qux.x());
|
||||
EXPECT_TRUE(gridcoord2d_bar.x() == gridcoord2d_qu2.x());
|
||||
|
||||
EXPECT_FALSE(gridcoord2d_foo.x() == gridcoord2d_bar.x());
|
||||
EXPECT_FALSE(gridcoord2d_foo.x() == gridcoord2d_baz.x());
|
||||
EXPECT_FALSE(gridcoord2d_bar.x() == gridcoord2d_baz.x());
|
||||
EXPECT_FALSE(gridcoord2d_foo.x() == gridcoord2d_baz.x());
|
||||
EXPECT_FALSE(gridcoord2d_qux.x() == gridcoord2d_qu2.x());
|
||||
}
|
||||
|
||||
TEST_F(GridCoord2DTest, y) {
|
||||
const GridCoord2D gridcoord2d_foo(0, 0);
|
||||
const GridCoord2D gridcoord2d_bar(1, 1);
|
||||
const GridCoord2D gridcoord2d_baz(-1, -1);
|
||||
const GridCoord2D gridcoord2d_qux(0, 1);
|
||||
const GridCoord2D gridcoord2d_qu2(1, 0);
|
||||
|
||||
EXPECT_TRUE(gridcoord2d_foo.y() == 0);
|
||||
EXPECT_TRUE(gridcoord2d_bar.y() == 1);
|
||||
EXPECT_TRUE(gridcoord2d_baz.y() == -1);
|
||||
EXPECT_FALSE(gridcoord2d_qux.y() == -1);
|
||||
EXPECT_FALSE(gridcoord2d_qu2.y() == -1);
|
||||
|
||||
EXPECT_TRUE(gridcoord2d_foo.y() == gridcoord2d_foo.y());
|
||||
EXPECT_TRUE(gridcoord2d_bar.y() == gridcoord2d_qux.y());
|
||||
|
||||
EXPECT_FALSE(gridcoord2d_foo.y() == gridcoord2d_bar.y());
|
||||
EXPECT_FALSE(gridcoord2d_foo.y() == gridcoord2d_baz.y());
|
||||
EXPECT_FALSE(gridcoord2d_bar.y() == gridcoord2d_baz.y());
|
||||
EXPECT_FALSE(gridcoord2d_foo.y() == gridcoord2d_baz.y());
|
||||
EXPECT_FALSE(gridcoord2d_qux.y() == gridcoord2d_qu2.y());
|
||||
EXPECT_FALSE(gridcoord2d_bar.y() == gridcoord2d_qu2.y());
|
||||
}
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
139
test/unit-kami-model.cc
Normal file
139
test/unit-kami-model.cc
Normal file
@@ -0,0 +1,139 @@
|
||||
/*-
|
||||
* Copyright (c) 2022 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/error.h>
|
||||
#include <kami/model.h>
|
||||
#include <kami/multigrid2d.h>
|
||||
#include <kami/population.h>
|
||||
#include <kami/sequential.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace kami;
|
||||
using namespace kami::error;
|
||||
using namespace std;
|
||||
|
||||
class TestAgent
|
||||
: public Agent {
|
||||
public:
|
||||
AgentID step(shared_ptr<Model> model) override {
|
||||
return get_agent_id();
|
||||
}
|
||||
};
|
||||
|
||||
class TestModel
|
||||
: public Model {
|
||||
public:
|
||||
shared_ptr<Model> step() final {
|
||||
return shared_from_this();
|
||||
}
|
||||
};
|
||||
|
||||
TEST(Model, DefaultConstructor) {
|
||||
// There is really no way this can go wrong, but
|
||||
// we add this check anyway in case of future
|
||||
// changes.
|
||||
EXPECT_NO_THROW(
|
||||
const TestModel model_foo
|
||||
);
|
||||
}
|
||||
|
||||
TEST(Model, set_population) {
|
||||
auto model_foo = make_shared<TestModel>();
|
||||
auto pop_foo = make_shared<Population>();
|
||||
|
||||
auto pop_bar = model_foo->set_population(pop_foo);
|
||||
EXPECT_EQ(pop_foo, pop_bar);
|
||||
}
|
||||
|
||||
TEST(Model, get_population) {
|
||||
auto model_foo = make_shared<TestModel>();
|
||||
auto pop_foo = make_shared<Population>();
|
||||
|
||||
EXPECT_THROW(auto pop_nul = model_foo->get_population(), ResourceNotAvailable);
|
||||
|
||||
auto pop_bar = model_foo->set_population(pop_foo);
|
||||
auto pop_baz = model_foo->get_population();
|
||||
|
||||
EXPECT_TRUE(pop_baz);
|
||||
EXPECT_EQ(pop_foo, pop_baz);
|
||||
EXPECT_EQ(pop_bar, pop_baz);
|
||||
}
|
||||
|
||||
TEST(Model, set_scheduler) {
|
||||
auto model_foo = make_shared<TestModel>();
|
||||
auto sched_foo = make_shared<SequentialScheduler>();
|
||||
|
||||
auto sched_bar = model_foo->set_scheduler(sched_foo);
|
||||
EXPECT_EQ(sched_foo, sched_bar);
|
||||
}
|
||||
|
||||
TEST(Model, get_scheduler) {
|
||||
auto model_foo = make_shared<TestModel>();
|
||||
auto sched_foo = make_shared<SequentialScheduler>();
|
||||
|
||||
EXPECT_THROW(auto sched_nul = model_foo->get_scheduler(), ResourceNotAvailable);
|
||||
|
||||
auto sched_bar = model_foo->set_scheduler(sched_foo);
|
||||
auto sched_baz = model_foo->get_scheduler();
|
||||
|
||||
EXPECT_TRUE(sched_baz);
|
||||
EXPECT_EQ(sched_foo, sched_baz);
|
||||
EXPECT_EQ(sched_bar, sched_baz);
|
||||
}
|
||||
|
||||
TEST(Model, set_domain) {
|
||||
auto model_foo = make_shared<TestModel>();
|
||||
auto grid2_foo = make_shared<MultiGrid2D>(10, 10, true, true);
|
||||
|
||||
auto grid2_bar = model_foo->set_domain(grid2_foo);
|
||||
EXPECT_EQ(grid2_foo, grid2_bar);
|
||||
}
|
||||
|
||||
TEST(Model, get_domain) {
|
||||
auto model_foo = make_shared<TestModel>();
|
||||
auto grid2_foo = make_shared<MultiGrid2D>(10, 10, true, true);
|
||||
|
||||
EXPECT_THROW(auto grid2_nul = model_foo->get_domain(), ResourceNotAvailable);
|
||||
|
||||
auto grid2_bar = model_foo->set_domain(grid2_foo);
|
||||
auto grid2_baz = model_foo->get_domain();
|
||||
|
||||
EXPECT_TRUE(grid2_baz);
|
||||
EXPECT_EQ(grid2_foo, grid2_baz);
|
||||
EXPECT_EQ(grid2_bar, grid2_baz);
|
||||
}
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
475
test/unit-kami-multigrid1d.cc
Normal file
475
test/unit-kami-multigrid1d.cc
Normal file
@@ -0,0 +1,475 @@
|
||||
/*-
|
||||
* Copyright (c) 2022 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/multigrid1d.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace kami;
|
||||
using namespace kami::error;
|
||||
using namespace std;
|
||||
|
||||
TEST(MultiGrid1D, DefaultConstructor) {
|
||||
// There is really no way this can go wrong, but
|
||||
// we add this check anyway in case of future
|
||||
// changes.
|
||||
EXPECT_NO_THROW(
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
);
|
||||
}
|
||||
|
||||
TEST(MultiGrid1D, add_agent) {
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
const AgentID agent_id_foo, agent_id_bar;
|
||||
const GridCoord1D coord2(2), coord3(3);
|
||||
|
||||
{
|
||||
auto agent_id_baz = multigrid1d_foo.add_agent(agent_id_foo, coord2);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_foo);
|
||||
}
|
||||
{
|
||||
auto agent_id_baz = multigrid1d_foo.add_agent(agent_id_bar, coord2);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_bar);
|
||||
}
|
||||
{
|
||||
auto agent_id_baz = multigrid1d_foo.add_agent(agent_id_bar, coord3);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_bar);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MultiGrid1D, delete_agent) {
|
||||
const AgentID agent_id_foo, agent_id_bar;
|
||||
const GridCoord1D coord2(2), coord3(3);
|
||||
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
auto agent_id_baz = multigrid1d_foo.delete_agent(agent_id_foo);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_foo);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_bar, coord2));
|
||||
auto agent_id_baz = multigrid1d_foo.delete_agent(agent_id_foo);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_foo);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_bar, coord2));
|
||||
auto agent_id_baz = multigrid1d_foo.delete_agent(agent_id_bar);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_bar);
|
||||
}
|
||||
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
auto agent_id_baz = multigrid1d_foo.delete_agent(agent_id_foo, coord2);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_foo);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_bar, coord2));
|
||||
auto agent_id_baz = multigrid1d_foo.delete_agent(agent_id_foo, coord2);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_foo);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_bar, coord2));
|
||||
auto agent_id_baz = multigrid1d_foo.delete_agent(agent_id_bar, coord2);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_bar);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
EXPECT_THROW(auto agent_id_baz = multigrid1d_foo.delete_agent(agent_id_foo, coord3), AgentNotFound);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_bar, coord2));
|
||||
EXPECT_THROW(auto agent_id_baz = multigrid1d_foo.delete_agent(agent_id_foo, coord3), AgentNotFound);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_bar, coord2));
|
||||
EXPECT_THROW(auto agent_id_baz = multigrid1d_foo.delete_agent(agent_id_bar, coord3), AgentNotFound);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MultiGrid1D, is_location_valid) {
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
const AgentID agent_id_foo, agent_id_bar;
|
||||
const GridCoord1D coordm1(-1), coord0(0), coord2(2), coord3(3), coord10(10), coord100(100);
|
||||
|
||||
{
|
||||
EXPECT_TRUE(multigrid1d_foo.is_location_valid(coord0));
|
||||
EXPECT_TRUE(multigrid1d_foo.is_location_valid(coord2));
|
||||
EXPECT_TRUE(multigrid1d_foo.is_location_valid(coord2));
|
||||
|
||||
EXPECT_FALSE(multigrid1d_foo.is_location_valid(coordm1));
|
||||
EXPECT_FALSE(multigrid1d_foo.is_location_valid(coord10));
|
||||
EXPECT_FALSE(multigrid1d_foo.is_location_valid(coord100));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MultiGrid1D, is_location_empty) {
|
||||
const AgentID agent_id_foo, agent_id_bar;
|
||||
const GridCoord1D coord2(2), coord3(3);
|
||||
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
EXPECT_TRUE(multigrid1d_foo.is_location_empty(coord2));
|
||||
EXPECT_TRUE(multigrid1d_foo.is_location_empty(coord3));
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
EXPECT_FALSE(multigrid1d_foo.is_location_empty(coord2));
|
||||
EXPECT_TRUE(multigrid1d_foo.is_location_empty(coord3));
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_bar, coord2));
|
||||
EXPECT_FALSE(multigrid1d_foo.is_location_empty(coord2));
|
||||
EXPECT_TRUE(multigrid1d_foo.is_location_empty(coord3));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MultiGrid1D, move_agent) {
|
||||
const AgentID agent_id_foo, agent_id_bar;
|
||||
const GridCoord1D coord2(2), coord3(3), coord7(7), coord10(10);
|
||||
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
auto agent_id_baz = multigrid1d_foo.move_agent(agent_id_foo, coord7);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_foo);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
EXPECT_THROW(auto agent_id_baz = multigrid1d_foo.move_agent(agent_id_foo, coord10), LocationInvalid);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_bar, coord2));
|
||||
auto agent_id_baz = multigrid1d_foo.move_agent(agent_id_foo, coord2);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_foo);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_bar, coord2));
|
||||
auto agent_id_baz = multigrid1d_foo.move_agent(agent_id_foo, coord7);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_foo);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MultiGrid1D, get_neighborhood) {
|
||||
const AgentID agent_id_foo;
|
||||
const GridCoord1D coord0(0), coord1(1), coord2(2), coord3(3), coord9(9);
|
||||
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1, coord9
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(coord0, true);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1, coord2
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(coord1, true);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, false);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(coord0, true);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, false);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1, coord2
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(coord1, true);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord1, coord9
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(coord0, false);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord2
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(coord1, false);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, false);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord1
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(coord0, false);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, false);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord2
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(coord1, false);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1, coord9
|
||||
});
|
||||
|
||||
EXPECT_THROW(auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, true), AgentNotFound);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
multigrid1d_foo.add_agent(agent_id_foo, coord0);
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1, coord9
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, true);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
multigrid1d_foo.add_agent(agent_id_foo, coord1);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1, coord2
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, true);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, false);
|
||||
multigrid1d_foo.add_agent(agent_id_foo, coord0);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, true);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, false);
|
||||
multigrid1d_foo.add_agent(agent_id_foo, coord1);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1, coord2
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, true);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
multigrid1d_foo.add_agent(agent_id_foo, coord0);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord1, coord9
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, false);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
multigrid1d_foo.add_agent(agent_id_foo, coord1);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord2
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, false);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, false);
|
||||
multigrid1d_foo.add_agent(agent_id_foo, coord0);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord1
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, false);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, false);
|
||||
multigrid1d_foo.add_agent(agent_id_foo, coord1);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord2
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, false);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MultiGrid1D, get_location_by_agent) {
|
||||
const AgentID agent_id_foo, agent_id_bar;
|
||||
const GridCoord1D coord2(2), coord3(3);
|
||||
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
EXPECT_THROW(auto loc1 = multigrid1d_foo.get_location_by_agent(agent_id_foo), AgentNotFound);
|
||||
EXPECT_THROW(auto loc2 = multigrid1d_foo.get_location_by_agent(agent_id_bar), AgentNotFound);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
auto local = multigrid1d_foo.get_location_by_agent(agent_id_foo);
|
||||
EXPECT_EQ(local, coord2);
|
||||
EXPECT_THROW(auto loc = multigrid1d_foo.get_location_by_agent(agent_id_bar), AgentNotFound);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MultiGrid1D, get_location_contents) {
|
||||
const AgentID agent_id_foo, agent_id_bar, agent_id_baz;
|
||||
const GridCoord1D coord0(0), coord1(1), coord9(9), coord10(10);
|
||||
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
EXPECT_THROW(auto agent_list_foo = multigrid1d_foo.get_location_contents(coord10), LocationUnavailable);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
auto agent_list_foo = multigrid1d_foo.get_location_contents(coord1);
|
||||
|
||||
EXPECT_TRUE(agent_list_foo);
|
||||
EXPECT_TRUE(agent_list_foo->empty());
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_foo, coord1));
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_bar, coord1));
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_baz, coord1));
|
||||
|
||||
auto tval = set < AgentID > ({
|
||||
agent_id_foo, agent_id_bar, agent_id_baz
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_location_contents(coord1);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_foo, coord1));
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_bar, coord1));
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_baz, coord9));
|
||||
|
||||
auto tval = set < AgentID > ({
|
||||
agent_id_foo, agent_id_bar
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_location_contents(coord1);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
}
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
1126
test/unit-kami-multigrid2d.cc
Normal file
1126
test/unit-kami-multigrid2d.cc
Normal file
File diff suppressed because it is too large
Load Diff
204
test/unit-kami-population.cc
Normal file
204
test/unit-kami-population.cc
Normal file
@@ -0,0 +1,204 @@
|
||||
/*-
|
||||
* Copyright (c) 2022 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/error.h>
|
||||
#include <kami/population.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace kami;
|
||||
using namespace kami::error;
|
||||
using namespace std;
|
||||
|
||||
class TestAgent
|
||||
: public Agent {
|
||||
private:
|
||||
int _x;
|
||||
|
||||
public:
|
||||
explicit TestAgent(int x)
|
||||
:_x(x) {
|
||||
};
|
||||
|
||||
AgentID step(shared_ptr<Model> model) override {
|
||||
return get_agent_id();
|
||||
}
|
||||
|
||||
int getval() {
|
||||
return _x;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(Population, DefaultConstructor) {
|
||||
// There is really no way this can go wrong, but
|
||||
// we add this check anyway in case of future
|
||||
// changes.
|
||||
EXPECT_NO_THROW(
|
||||
const Population population_foo;
|
||||
);
|
||||
}
|
||||
|
||||
TEST(Population, add_agent) {
|
||||
Population population_foo;
|
||||
auto agent_foo = make_shared<TestAgent>(8675309);
|
||||
auto agent_bar = make_shared<TestAgent>(1729);
|
||||
|
||||
{
|
||||
auto agent_id_baz = population_foo.add_agent(agent_foo);
|
||||
EXPECT_EQ(agent_id_baz, agent_foo->get_agent_id());
|
||||
}
|
||||
{
|
||||
auto agent_id_baz = population_foo.add_agent(agent_bar);
|
||||
EXPECT_EQ(agent_id_baz, agent_bar->get_agent_id());
|
||||
}
|
||||
{
|
||||
auto agent_id_baz = population_foo.add_agent(agent_foo);
|
||||
EXPECT_EQ(agent_id_baz, agent_foo->get_agent_id());
|
||||
auto agent_id_qux = population_foo.add_agent(agent_bar);
|
||||
EXPECT_EQ(agent_id_qux, agent_bar->get_agent_id());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Population, get_agent_by_id) {
|
||||
auto agent_foo = make_shared<TestAgent>(8675309);
|
||||
auto agent_bar = make_shared<TestAgent>(1729);
|
||||
|
||||
{
|
||||
Population population_foo;
|
||||
static_cast<void>(population_foo.add_agent(agent_foo));
|
||||
auto agent_baz_opt = population_foo.get_agent_by_id(agent_foo->get_agent_id());
|
||||
EXPECT_TRUE(agent_baz_opt);
|
||||
|
||||
auto agent_baz = dynamic_pointer_cast<TestAgent>(agent_baz_opt);
|
||||
EXPECT_EQ(agent_baz->getval(), 8675309);
|
||||
}
|
||||
{
|
||||
Population population_foo;
|
||||
static_cast<void>(population_foo.add_agent(agent_foo));
|
||||
EXPECT_THROW(auto agent_baz_opt = population_foo.get_agent_by_id(agent_bar->get_agent_id()), AgentNotFound);
|
||||
}
|
||||
{
|
||||
Population population_foo;
|
||||
static_cast<void>(population_foo.add_agent(agent_foo));
|
||||
static_cast<void>(population_foo.add_agent(agent_bar));
|
||||
|
||||
auto agent_baz_opt = population_foo.get_agent_by_id(agent_foo->get_agent_id());
|
||||
EXPECT_TRUE(agent_baz_opt);
|
||||
|
||||
auto agent_baz = dynamic_pointer_cast<TestAgent>(agent_baz_opt);
|
||||
EXPECT_EQ(agent_baz->getval(), 8675309);
|
||||
|
||||
auto agent_qux_opt = population_foo.get_agent_by_id(agent_bar->get_agent_id());
|
||||
EXPECT_TRUE(agent_qux_opt);
|
||||
|
||||
auto agent_qux = dynamic_pointer_cast<TestAgent>(agent_qux_opt);
|
||||
EXPECT_EQ(agent_qux->getval(), 1729);
|
||||
}
|
||||
{
|
||||
Population population_foo;
|
||||
static_cast<void>(population_foo.add_agent(agent_foo));
|
||||
static_cast<void>(population_foo.add_agent(agent_bar));
|
||||
|
||||
auto agent_qux_opt = population_foo.get_agent_by_id(agent_bar->get_agent_id());
|
||||
EXPECT_TRUE(agent_qux_opt);
|
||||
|
||||
auto agent_qux = dynamic_pointer_cast<TestAgent>(agent_qux_opt);
|
||||
EXPECT_EQ(agent_qux->getval(), 1729);
|
||||
|
||||
auto agent_baz_opt = population_foo.get_agent_by_id(agent_foo->get_agent_id());
|
||||
EXPECT_TRUE(agent_baz_opt);
|
||||
|
||||
auto agent_baz = dynamic_pointer_cast<TestAgent>(agent_baz_opt);
|
||||
EXPECT_EQ(agent_baz->getval(), 8675309);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Population, get_agent_list) {
|
||||
auto agent_foo = make_shared<TestAgent>(8675309);
|
||||
auto agent_bar = make_shared<TestAgent>(1729);
|
||||
auto agent_baz = make_shared<TestAgent>(4104);
|
||||
auto agent_qux = make_shared<TestAgent>(196);
|
||||
|
||||
{
|
||||
Population population_foo;
|
||||
auto tval = make_shared<vector<AgentID>>();
|
||||
auto rval = population_foo.get_agent_list();
|
||||
|
||||
EXPECT_EQ(*tval, *rval);
|
||||
}
|
||||
{
|
||||
Population population_foo;
|
||||
auto tval = make_shared<vector<AgentID>>();
|
||||
auto rval = population_foo.get_agent_list();
|
||||
|
||||
tval->push_back(agent_foo->get_agent_id());
|
||||
EXPECT_NE(*tval, *rval);
|
||||
}
|
||||
{
|
||||
Population population_foo;
|
||||
static_cast<void>(population_foo.add_agent(agent_foo));
|
||||
static_cast<void>(population_foo.add_agent(agent_bar));
|
||||
|
||||
auto tval = make_shared<vector<AgentID>>();
|
||||
auto uval = make_shared<vector<AgentID>>();
|
||||
auto rval = population_foo.get_agent_list();
|
||||
|
||||
tval->push_back(agent_foo->get_agent_id());
|
||||
tval->push_back(agent_bar->get_agent_id());
|
||||
EXPECT_EQ(*tval, *rval);
|
||||
|
||||
// Ordering matters
|
||||
uval->push_back(agent_bar->get_agent_id());
|
||||
uval->push_back(agent_foo->get_agent_id());
|
||||
EXPECT_NE(*uval, *rval);
|
||||
}
|
||||
{
|
||||
Population population_foo;
|
||||
static_cast<void>(population_foo.add_agent(agent_foo));
|
||||
static_cast<void>(population_foo.add_agent(agent_bar));
|
||||
static_cast<void>(population_foo.add_agent(agent_baz));
|
||||
static_cast<void>(population_foo.add_agent(agent_qux));
|
||||
|
||||
auto tval = make_shared<vector<AgentID>>();
|
||||
auto rval = population_foo.get_agent_list();
|
||||
|
||||
tval->push_back(agent_foo->get_agent_id());
|
||||
tval->push_back(agent_bar->get_agent_id());
|
||||
tval->push_back(agent_baz->get_agent_id());
|
||||
tval->push_back(agent_qux->get_agent_id());
|
||||
EXPECT_EQ(*tval, *rval);
|
||||
}
|
||||
}
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
52
test/unit-kami-position.cc
Normal file
52
test/unit-kami-position.cc
Normal file
@@ -0,0 +1,52 @@
|
||||
/*-
|
||||
* Copyright (c) 2022 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <kami/position.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace kami;
|
||||
|
||||
class PositionTest
|
||||
: public ::testing::Test {
|
||||
protected:
|
||||
Position pos_foo = GridCoord1D(5);
|
||||
Position pos_bar = GridCoord2D(2, 5);
|
||||
};
|
||||
|
||||
TEST_F(PositionTest, DefaultConstructor) {
|
||||
EXPECT_EQ(pos_foo, pos_foo);
|
||||
EXPECT_NE(pos_foo, pos_bar);
|
||||
}
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
170
test/unit-kami-random.cc
Normal file
170
test/unit-kami-random.cc
Normal file
@@ -0,0 +1,170 @@
|
||||
/*-
|
||||
* Copyright (c) 2022 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/population.h>
|
||||
#include <kami/random.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace kami;
|
||||
using namespace std;
|
||||
|
||||
class TestAgent
|
||||
: public Agent {
|
||||
public:
|
||||
AgentID step(shared_ptr<Model> model) override {
|
||||
return get_agent_id();
|
||||
}
|
||||
};
|
||||
|
||||
class TestModel
|
||||
: public Model {
|
||||
public:
|
||||
shared_ptr<vector<AgentID>> retval;
|
||||
|
||||
shared_ptr<Model> step() override {
|
||||
retval = _sched->step(shared_from_this());
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
shared_ptr<Model> step(unique_ptr<vector<AgentID>> agent_list) {
|
||||
retval = _sched->step(shared_from_this(), std::move(agent_list));
|
||||
return shared_from_this();
|
||||
}
|
||||
};
|
||||
|
||||
class RandomSchedulerTest
|
||||
: public ::testing::Test {
|
||||
protected:
|
||||
shared_ptr<TestModel> mod = nullptr;
|
||||
shared_ptr<mt19937> rng = nullptr;
|
||||
|
||||
void SetUp() override {
|
||||
mod = make_shared<TestModel>();
|
||||
rng = make_shared<mt19937>();
|
||||
auto popul_foo = make_shared<Population>();
|
||||
auto sched_foo = make_shared<RandomScheduler>(rng);
|
||||
|
||||
// Domain is not required for this test
|
||||
static_cast<void>(mod->set_population(popul_foo));
|
||||
static_cast<void>(mod->set_scheduler(sched_foo));
|
||||
|
||||
for (auto i = 0; i < 10; i++) {
|
||||
auto agent_foo = make_shared<TestAgent>();
|
||||
static_cast<void>(popul_foo->add_agent(agent_foo));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST(RandomScheduler, DefaultConstructor) {
|
||||
// There is really no way this can go wrong, but
|
||||
// we add this check anyway in case of future
|
||||
// changes.
|
||||
EXPECT_NO_THROW(
|
||||
const RandomScheduler sched_foo;
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(RandomSchedulerTest, step_interface1) {
|
||||
auto tval = mod->get_population()->get_agent_list();
|
||||
auto aval = mod->get_population()->get_agent_list();
|
||||
mod->step(std::move(aval));
|
||||
|
||||
auto rval = mod->retval;
|
||||
|
||||
EXPECT_EQ(rval->size(), 10);
|
||||
|
||||
// Sort both return values and just make sure all of them all the same...
|
||||
// We cannot test permutation since, well, you know...
|
||||
set < AgentID > tval_set = set(tval->begin(), tval->end());
|
||||
set < AgentID > rval_set = set(rval->begin(), rval->end());
|
||||
EXPECT_EQ(tval_set, rval_set);
|
||||
}
|
||||
|
||||
TEST_F(RandomSchedulerTest, step_interface2) {
|
||||
auto tval = mod->get_population()->get_agent_list();
|
||||
auto aval = mod->get_population()->get_agent_list();
|
||||
mod->step(std::move(aval));
|
||||
|
||||
auto rval = mod->retval;
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(rval->size(), 10);
|
||||
|
||||
set < AgentID > tval_set = set(tval->begin(), tval->end());
|
||||
set < AgentID > rval_set = set(rval->begin(), rval->end());
|
||||
EXPECT_EQ(tval_set, rval_set);
|
||||
}
|
||||
|
||||
TEST_F(RandomSchedulerTest, step_10000) {
|
||||
// Do it a lot...
|
||||
for (auto i = 0; i < 10000; i++) {
|
||||
auto tval = mod->get_population()->get_agent_list();
|
||||
auto aval = mod->get_population()->get_agent_list();
|
||||
mod->step(std::move(aval));
|
||||
|
||||
auto rval = mod->retval;
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(rval->size(), 10);
|
||||
|
||||
set < AgentID > tval_set = set(tval->begin(), tval->end());
|
||||
set < AgentID > rval_set = set(rval->begin(), rval->end());
|
||||
EXPECT_EQ(tval_set, rval_set);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(RandomSchedulerTest, get_rng) {
|
||||
auto rval = static_pointer_cast<RandomScheduler>(mod->get_scheduler())->get_rng();
|
||||
|
||||
EXPECT_EQ(rng, rval);
|
||||
}
|
||||
|
||||
TEST_F(RandomSchedulerTest, set_rng) {
|
||||
auto new_rng = make_shared<mt19937>();
|
||||
auto rval1 = static_pointer_cast<RandomScheduler>(mod->get_scheduler())->get_rng();
|
||||
|
||||
static_cast<void>(static_pointer_cast<RandomScheduler>(mod->get_scheduler())->set_rng(new_rng));
|
||||
auto rval2 = static_pointer_cast<RandomScheduler>(mod->get_scheduler())->get_rng();
|
||||
|
||||
EXPECT_EQ(new_rng, rval2);
|
||||
EXPECT_NE(new_rng, rval1);
|
||||
}
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
133
test/unit-kami-reporter.cc
Normal file
133
test/unit-kami-reporter.cc
Normal file
@@ -0,0 +1,133 @@
|
||||
/*-
|
||||
* Copyright (c) 2022 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/population.h>
|
||||
#include <kami/reporter.h>
|
||||
#include <kami/staged.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace kami;
|
||||
using namespace std;
|
||||
|
||||
class TestAgent
|
||||
: public ReporterAgent {
|
||||
public:
|
||||
AgentID step(shared_ptr<ReporterModel> model) override {
|
||||
return get_agent_id();
|
||||
}
|
||||
|
||||
std::unique_ptr<nlohmann::json> collect() override {
|
||||
auto json_ret_val = std::make_unique<nlohmann::json>();
|
||||
|
||||
(*json_ret_val)["fname"] = "Jesse";
|
||||
(*json_ret_val)["lname"] = "Pinkman";
|
||||
|
||||
return json_ret_val;
|
||||
}
|
||||
};
|
||||
|
||||
class TestModel
|
||||
: public ReporterModel {
|
||||
public:
|
||||
shared_ptr<vector<AgentID>> retval;
|
||||
|
||||
std::unique_ptr<nlohmann::json> collect() override {
|
||||
auto json_ret_val = std::make_unique<nlohmann::json>();
|
||||
|
||||
(*json_ret_val)["fname"] = "Walter";
|
||||
(*json_ret_val)["lname"] = "White";
|
||||
|
||||
return json_ret_val;
|
||||
}
|
||||
};
|
||||
|
||||
class ReporterModelTest
|
||||
: public ::testing::Test {
|
||||
protected:
|
||||
shared_ptr<TestModel> mod = nullptr;
|
||||
|
||||
void SetUp() override {
|
||||
mod = make_shared<TestModel>();
|
||||
auto popul_foo = make_shared<Population>();
|
||||
auto sched_foo = make_shared<SequentialScheduler>();
|
||||
|
||||
// Domain is not required for this test
|
||||
static_cast<void>(mod->set_population(popul_foo));
|
||||
static_cast<void>(mod->set_scheduler(sched_foo));
|
||||
|
||||
for (auto i = 0; i < 3; i++) {
|
||||
auto agent_foo = make_shared<TestAgent>();
|
||||
static_cast<void>(popul_foo->add_agent(agent_foo));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST(ReporterModel, DefaultConstructor) {
|
||||
// There is really no way this can go wrong, but
|
||||
// we add this check anyway in case of future
|
||||
// changes.
|
||||
EXPECT_NO_THROW(
|
||||
const TestModel reporter_foo;
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(ReporterModelTest, collect) {
|
||||
auto aval = mod->get_population()->get_agent_list();
|
||||
mod->step();
|
||||
|
||||
auto rval = mod->collect();
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(rval->dump(), "{\"fname\":\"Walter\",\"lname\":\"White\"}");
|
||||
}
|
||||
|
||||
TEST_F(ReporterModelTest, report) {
|
||||
for (auto i = 0; i < 2; i++) {
|
||||
auto aval = mod->get_population()->get_agent_list();
|
||||
|
||||
mod->step();
|
||||
auto rval = mod->collect();
|
||||
}
|
||||
|
||||
auto rval = mod->report();
|
||||
EXPECT_EQ(rval->dump(),
|
||||
"[{\"agent_data\":[{\"agent_id\":\"13\",\"data\":{\"fname\":\"Jesse\",\"lname\":\"Pinkman\"}},{\"agent_id\":\"14\",\"data\":{\"fname\":\"Jesse\",\"lname\":\"Pinkman\"}},{\"agent_id\":\"15\",\"data\":{\"fname\":\"Jesse\",\"lname\":\"Pinkman\"}}],\"model_data\":{\"fname\":\"Walter\",\"lname\":\"White\"},\"step_id\":1},{\"agent_data\":[{\"agent_id\":\"13\",\"data\":{\"fname\":\"Jesse\",\"lname\":\"Pinkman\"}},{\"agent_id\":\"14\",\"data\":{\"fname\":\"Jesse\",\"lname\":\"Pinkman\"}},{\"agent_id\":\"15\",\"data\":{\"fname\":\"Jesse\",\"lname\":\"Pinkman\"}}],\"model_data\":{\"fname\":\"Walter\",\"lname\":\"White\"},\"step_id\":2}]");
|
||||
}
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
119
test/unit-kami-reporteragent.cc
Normal file
119
test/unit-kami-reporteragent.cc
Normal file
@@ -0,0 +1,119 @@
|
||||
/*-
|
||||
* Copyright (c) 2022 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/model.h>
|
||||
#include <kami/reporter.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace kami;
|
||||
using namespace std;
|
||||
|
||||
class TestAgent
|
||||
: public ReporterAgent {
|
||||
public:
|
||||
AgentID step(shared_ptr<ReporterModel> model) override {
|
||||
return get_agent_id();
|
||||
}
|
||||
|
||||
std::unique_ptr<nlohmann::json> collect() override {
|
||||
auto json_ret_val = std::make_unique<nlohmann::json>();
|
||||
|
||||
(*json_ret_val)["fname"] = "Gus";
|
||||
(*json_ret_val)["lname"] = "Fring";
|
||||
|
||||
return json_ret_val;
|
||||
}
|
||||
};
|
||||
|
||||
class TestModel
|
||||
: public ReporterModel {
|
||||
public:
|
||||
std::unique_ptr<nlohmann::json> collect() override {
|
||||
return std::make_unique<nlohmann::json>();
|
||||
}
|
||||
};
|
||||
|
||||
class ReporterAgentTest
|
||||
: public ::testing::Test {
|
||||
protected:
|
||||
TestAgent agent_foo;
|
||||
TestAgent agent_bar;
|
||||
shared_ptr<TestModel> model_world = nullptr;
|
||||
|
||||
void SetUp() override {
|
||||
model_world = make_shared<TestModel>();
|
||||
}
|
||||
};
|
||||
|
||||
TEST(ReporterAgent, DefaultConstructor) {
|
||||
EXPECT_NO_THROW(
|
||||
const TestAgent agent_baz;
|
||||
const TestAgent agent_qux;
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(ReporterAgentTest, equivalance) {
|
||||
EXPECT_EQ(agent_foo, agent_foo);
|
||||
EXPECT_NE(agent_foo, agent_bar);
|
||||
}
|
||||
|
||||
TEST_F(ReporterAgentTest, get_agent_id) {
|
||||
EXPECT_EQ(agent_foo.get_agent_id(), agent_foo.get_agent_id());
|
||||
EXPECT_NE(agent_bar.get_agent_id(), agent_foo.get_agent_id());
|
||||
}
|
||||
|
||||
TEST_F(ReporterAgentTest, step) {
|
||||
EXPECT_EQ(agent_foo.get_agent_id(), agent_foo.step(model_world));
|
||||
EXPECT_NE(agent_bar.get_agent_id(), agent_foo.step(model_world));
|
||||
}
|
||||
|
||||
TEST_F(ReporterAgentTest, collect) {
|
||||
EXPECT_EQ(agent_foo.collect()->dump(), "{\"fname\":\"Gus\",\"lname\":\"Fring\"}");
|
||||
EXPECT_NE(agent_bar.collect()->dump(), "{\"fname\":\"Hank\",\"lname\":\"Schrader\"}");
|
||||
}
|
||||
|
||||
TEST_F(ReporterAgentTest, equality) {
|
||||
EXPECT_TRUE(agent_foo == agent_foo);
|
||||
EXPECT_TRUE(agent_bar == agent_bar);
|
||||
}
|
||||
|
||||
TEST_F(ReporterAgentTest, inequality) {
|
||||
EXPECT_TRUE(agent_foo != agent_bar);
|
||||
EXPECT_FALSE(agent_bar != agent_bar);
|
||||
}
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
140
test/unit-kami-sequential.cc
Normal file
140
test/unit-kami-sequential.cc
Normal file
@@ -0,0 +1,140 @@
|
||||
/*-
|
||||
* Copyright (c) 2022 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/population.h>
|
||||
#include <kami/sequential.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace kami;
|
||||
using namespace std;
|
||||
|
||||
class TestAgent
|
||||
: public Agent {
|
||||
public:
|
||||
AgentID step(shared_ptr<Model> model) override {
|
||||
return get_agent_id();
|
||||
}
|
||||
};
|
||||
|
||||
class TestModel
|
||||
: public Model {
|
||||
public:
|
||||
shared_ptr<vector<AgentID>> retval;
|
||||
|
||||
shared_ptr<Model> step() override {
|
||||
retval = _sched->step(shared_from_this());
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
shared_ptr<Model> step(unique_ptr<vector<AgentID>> agent_list) {
|
||||
retval = _sched->step(shared_from_this(), std::move(agent_list));
|
||||
return shared_from_this();
|
||||
}
|
||||
};
|
||||
|
||||
class SequentialSchedulerTest
|
||||
: public ::testing::Test {
|
||||
protected:
|
||||
shared_ptr<TestModel> mod = nullptr;
|
||||
|
||||
void SetUp() override {
|
||||
mod = make_shared<TestModel>();
|
||||
auto pop_foo = make_shared<Population>();
|
||||
auto sched_foo = make_shared<SequentialScheduler>();
|
||||
|
||||
// Domain is not required for this test
|
||||
static_cast<void>(mod->set_population(pop_foo));
|
||||
static_cast<void>(mod->set_scheduler(sched_foo));
|
||||
|
||||
for (auto i = 0; i < 10; i++) {
|
||||
auto agent_foo = make_shared<TestAgent>();
|
||||
static_cast<void>(pop_foo->add_agent(agent_foo));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST(SequentialScheduler, DefaultConstructor) {
|
||||
// There is really no way this can go wrong, but
|
||||
// we add this check anyway in case of future
|
||||
// changes.
|
||||
EXPECT_NO_THROW(
|
||||
const SequentialScheduler sched_foo;
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(SequentialSchedulerTest, step_interface1) {
|
||||
auto tval = mod->get_population()->get_agent_list();
|
||||
auto aval = mod->get_population()->get_agent_list();
|
||||
mod->step(std::move(aval));
|
||||
|
||||
auto rval = mod->retval;
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(rval->size(), 10);
|
||||
EXPECT_EQ(*rval, *tval);
|
||||
}
|
||||
|
||||
TEST_F(SequentialSchedulerTest, step_interface2) {
|
||||
auto tval = mod->get_population()->get_agent_list();
|
||||
auto aval = mod->get_population()->get_agent_list();
|
||||
mod->step(std::move(aval));
|
||||
|
||||
auto rval = mod->retval;
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(rval->size(), 10);
|
||||
EXPECT_EQ(*rval, *tval);
|
||||
}
|
||||
|
||||
TEST_F(SequentialSchedulerTest, step_10000) {
|
||||
// Do it a lot...
|
||||
for (auto i = 0; i < 10000; i++) {
|
||||
auto tval = mod->get_population()->get_agent_list();
|
||||
auto aval = mod->get_population()->get_agent_list();
|
||||
mod->step(std::move(aval));
|
||||
|
||||
auto rval = mod->retval;
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(rval->size(), 10);
|
||||
EXPECT_EQ(*rval, *tval);
|
||||
}
|
||||
}
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
471
test/unit-kami-sologrid1d.cc
Normal file
471
test/unit-kami-sologrid1d.cc
Normal file
@@ -0,0 +1,471 @@
|
||||
/*-
|
||||
* Copyright (c) 2022 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/error.h>
|
||||
#include <kami/sologrid1d.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace kami;
|
||||
using namespace kami::error;
|
||||
using namespace std;
|
||||
|
||||
TEST(SoloGrid1D, DefaultConstructor) {
|
||||
// There is really no way this can go wrong, but
|
||||
// we add this check anyway in case of future
|
||||
// changes.
|
||||
EXPECT_NO_THROW(
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
);
|
||||
}
|
||||
|
||||
TEST(SoloGrid1D, add_agent) {
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
const AgentID agent_id_foo, agent_id_bar;
|
||||
const GridCoord1D coord2(2), coord3(3);
|
||||
|
||||
{
|
||||
auto agent_id_baz = sologrid1d_foo.add_agent(agent_id_foo, coord2);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_foo);
|
||||
}
|
||||
{
|
||||
EXPECT_THROW(auto agent_id_baz = sologrid1d_foo.add_agent(agent_id_bar, coord2), LocationUnavailable);
|
||||
}
|
||||
{
|
||||
auto agent_id_baz = sologrid1d_foo.add_agent(agent_id_bar, coord3);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_bar);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SoloGrid1D, delete_agent) {
|
||||
const AgentID agent_id_foo, agent_id_bar;
|
||||
const GridCoord1D coord2(2), coord3(3);
|
||||
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_foo);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_foo);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
EXPECT_THROW(static_cast<void>(sologrid1d_foo.add_agent(agent_id_bar, coord2)), LocationUnavailable);
|
||||
auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_foo);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_foo);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
EXPECT_THROW(static_cast<void>(sologrid1d_foo.add_agent(agent_id_bar, coord2)), LocationUnavailable);
|
||||
EXPECT_THROW(auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_bar), AgentNotFound);
|
||||
}
|
||||
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_foo, coord2);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_foo);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
EXPECT_THROW(static_cast<void>(sologrid1d_foo.add_agent(agent_id_bar, coord2)), LocationUnavailable);
|
||||
auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_foo, coord2);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_foo);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
EXPECT_THROW(static_cast<void>(sologrid1d_foo.add_agent(agent_id_bar, coord2)), LocationUnavailable);
|
||||
EXPECT_THROW(auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_bar, coord2), AgentNotFound);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
EXPECT_THROW(auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_foo, coord3), AgentNotFound);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
EXPECT_THROW(static_cast<void>(sologrid1d_foo.add_agent(agent_id_bar, coord2)), LocationUnavailable);
|
||||
EXPECT_THROW(auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_foo, coord3), AgentNotFound);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
EXPECT_THROW(static_cast<void>(sologrid1d_foo.add_agent(agent_id_bar, coord2)), LocationUnavailable);
|
||||
EXPECT_THROW(auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_bar, coord3), AgentNotFound);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SoloGrid1D, is_location_valid) {
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
const AgentID agent_id_foo, agent_id_bar;
|
||||
const GridCoord1D coordm1(-1), coord0(0), coord2(2), coord3(3), coord10(10), coord100(100);
|
||||
|
||||
{
|
||||
EXPECT_TRUE(sologrid1d_foo.is_location_valid(coord0));
|
||||
EXPECT_TRUE(sologrid1d_foo.is_location_valid(coord2));
|
||||
EXPECT_TRUE(sologrid1d_foo.is_location_valid(coord2));
|
||||
|
||||
EXPECT_FALSE(sologrid1d_foo.is_location_valid(coordm1));
|
||||
EXPECT_FALSE(sologrid1d_foo.is_location_valid(coord10));
|
||||
EXPECT_FALSE(sologrid1d_foo.is_location_valid(coord100));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SoloGrid1D, is_location_empty) {
|
||||
const AgentID agent_id_foo, agent_id_bar;
|
||||
const GridCoord1D coord2(2), coord3(3);
|
||||
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
EXPECT_TRUE(sologrid1d_foo.is_location_empty(coord2));
|
||||
EXPECT_TRUE(sologrid1d_foo.is_location_empty(coord3));
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
EXPECT_FALSE(sologrid1d_foo.is_location_empty(coord2));
|
||||
EXPECT_TRUE(sologrid1d_foo.is_location_empty(coord3));
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
EXPECT_THROW(static_cast<void>(sologrid1d_foo.add_agent(agent_id_bar, coord2)), LocationUnavailable);
|
||||
EXPECT_FALSE(sologrid1d_foo.is_location_empty(coord2));
|
||||
EXPECT_TRUE(sologrid1d_foo.is_location_empty(coord3));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SoloGrid1D, move_agent) {
|
||||
const AgentID agent_id_foo, agent_id_bar;
|
||||
const GridCoord1D coord2(2), coord3(3), coord7(7), coord10(10);
|
||||
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
auto agent_id_baz = sologrid1d_foo.move_agent(agent_id_foo, coord7);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_foo);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
EXPECT_THROW(auto agent_id_baz = sologrid1d_foo.move_agent(agent_id_foo, coord10), LocationInvalid);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
EXPECT_THROW(static_cast<void>(sologrid1d_foo.add_agent(agent_id_bar, coord2)), LocationUnavailable);
|
||||
auto agent_id_baz = sologrid1d_foo.move_agent(agent_id_foo, coord2);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_foo);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
EXPECT_THROW(static_cast<void>(sologrid1d_foo.add_agent(agent_id_bar, coord2)), LocationUnavailable);
|
||||
auto agent_id_baz = sologrid1d_foo.move_agent(agent_id_foo, coord7);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_foo);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SoloGrid1D, get_neighborhood) {
|
||||
const AgentID agent_id_foo, agent_id_bar;
|
||||
const GridCoord1D coord0(0), coord1(1), coord2(2), coord3(3), coord9(9);
|
||||
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1, coord9
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(coord0, true);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1, coord2
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(coord1, true);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, false);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(coord0, true);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, false);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1, coord2
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(coord1, true);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord1, coord9
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(coord0, false);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord2
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(coord1, false);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, false);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord1
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(coord0, false);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, false);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord2
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(coord1, false);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1, coord9
|
||||
});
|
||||
EXPECT_THROW(auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, true), AgentNotFound);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
sologrid1d_foo.add_agent(agent_id_foo, coord0);
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1, coord9
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, true);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
sologrid1d_foo.add_agent(agent_id_foo, coord1);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1, coord2
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, true);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, false);
|
||||
sologrid1d_foo.add_agent(agent_id_foo, coord0);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, true);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, false);
|
||||
sologrid1d_foo.add_agent(agent_id_foo, coord1);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1, coord2
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, true);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
sologrid1d_foo.add_agent(agent_id_foo, coord0);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord1, coord9
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, false);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
sologrid1d_foo.add_agent(agent_id_foo, coord1);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord2
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, false);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, false);
|
||||
sologrid1d_foo.add_agent(agent_id_foo, coord0);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord1
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, false);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, false);
|
||||
sologrid1d_foo.add_agent(agent_id_foo, coord1);
|
||||
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord2
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, false);
|
||||
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SoloGrid1D, get_location_by_agent) {
|
||||
const AgentID agent_id_foo, agent_id_bar;
|
||||
const GridCoord1D coord2(2), coord3(3);
|
||||
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
EXPECT_THROW(auto loc1 = sologrid1d_foo.get_location_by_agent(agent_id_foo), AgentNotFound);
|
||||
EXPECT_THROW(auto loc2 = sologrid1d_foo.get_location_by_agent(agent_id_bar), AgentNotFound);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
auto local = sologrid1d_foo.get_location_by_agent(agent_id_foo);
|
||||
EXPECT_EQ(local, coord2);
|
||||
EXPECT_THROW(auto loc = sologrid1d_foo.get_location_by_agent(agent_id_bar), AgentNotFound);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SoloGrid1D, get_location_contents) {
|
||||
const AgentID agent_id_foo, agent_id_bar, agent_id_baz;
|
||||
const GridCoord1D coord0(0), coord1(1), coord9(9), coord10(10);
|
||||
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
EXPECT_THROW(auto agent_list_foo = sologrid1d_foo.get_location_contents(coord10), LocationUnavailable);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
auto agent_list_foo = sologrid1d_foo.get_location_contents(coord1);
|
||||
|
||||
EXPECT_TRUE(agent_list_foo);
|
||||
EXPECT_TRUE(agent_list_foo->empty());
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_foo, coord1));
|
||||
EXPECT_THROW(static_cast<void>(sologrid1d_foo.add_agent(agent_id_bar, coord1)), LocationUnavailable);
|
||||
EXPECT_THROW(static_cast<void>(sologrid1d_foo.add_agent(agent_id_baz, coord1)), LocationUnavailable);
|
||||
|
||||
auto tval = set < AgentID > ({
|
||||
agent_id_foo
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_location_contents(coord1);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_foo, coord1));
|
||||
EXPECT_THROW(static_cast<void>(sologrid1d_foo.add_agent(agent_id_bar, coord1)), LocationUnavailable);
|
||||
EXPECT_THROW(static_cast<void>(sologrid1d_foo.add_agent(agent_id_baz, coord1)), LocationUnavailable);
|
||||
|
||||
auto tval = set < AgentID > ({
|
||||
agent_id_foo
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_location_contents(coord1);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
}
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
1101
test/unit-kami-sologrid2d.cc
Normal file
1101
test/unit-kami-sologrid2d.cc
Normal file
File diff suppressed because it is too large
Load Diff
142
test/unit-kami-staged.cc
Normal file
142
test/unit-kami-staged.cc
Normal file
@@ -0,0 +1,142 @@
|
||||
/*-
|
||||
* Copyright (c) 2022 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/population.h>
|
||||
#include <kami/staged.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace kami;
|
||||
using namespace std;
|
||||
|
||||
class TestAgent
|
||||
: public StagedAgent {
|
||||
public:
|
||||
AgentID step(shared_ptr<Model> model) override {
|
||||
return get_agent_id();
|
||||
}
|
||||
|
||||
AgentID advance(shared_ptr<Model> model) override {
|
||||
return get_agent_id();
|
||||
}
|
||||
};
|
||||
|
||||
class TestModel
|
||||
: public Model {
|
||||
public:
|
||||
shared_ptr<vector<AgentID>> retval;
|
||||
|
||||
shared_ptr<Model> step() override {
|
||||
retval = _sched->step(shared_from_this());
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
shared_ptr<Model> step(unique_ptr<vector<AgentID>> agent_list) {
|
||||
retval = _sched->step(shared_from_this(), std::move(agent_list));
|
||||
return shared_from_this();
|
||||
}
|
||||
};
|
||||
|
||||
class StagedSchedulerTest
|
||||
: public ::testing::Test {
|
||||
protected:
|
||||
shared_ptr<TestModel> mod = nullptr;
|
||||
|
||||
void SetUp() override {
|
||||
mod = make_shared<TestModel>();
|
||||
auto popul_foo = make_shared<Population>();
|
||||
auto sched_foo = make_shared<StagedScheduler>();
|
||||
|
||||
// Domain is not required for this test
|
||||
static_cast<void>(mod->set_population(popul_foo));
|
||||
static_cast<void>(mod->set_scheduler(sched_foo));
|
||||
|
||||
for (auto i = 0; i < 10; i++) {
|
||||
auto agent_foo = make_shared<TestAgent>();
|
||||
static_cast<void>(popul_foo->add_agent(agent_foo));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST(StagedScheduler, DefaultConstructor) {
|
||||
// There is really no way this can go wrong, but
|
||||
// we add this check anyway in case of future
|
||||
// changes.
|
||||
EXPECT_NO_THROW(
|
||||
const StagedScheduler sched_foo;
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(StagedSchedulerTest, step_interface1) {
|
||||
auto tval = mod->get_population()->get_agent_list();
|
||||
auto aval = mod->get_population()->get_agent_list();
|
||||
mod->step(std::move(aval));
|
||||
|
||||
auto rval = mod->retval;
|
||||
|
||||
EXPECT_EQ(rval->size(), 10);
|
||||
EXPECT_EQ(*rval, *tval);
|
||||
}
|
||||
|
||||
TEST_F(StagedSchedulerTest, step_interface2) {
|
||||
auto tval = mod->get_population()->get_agent_list();
|
||||
auto aval = mod->get_population()->get_agent_list();
|
||||
mod->step(std::move(aval));
|
||||
|
||||
auto rval = mod->retval;
|
||||
|
||||
EXPECT_EQ(rval->size(), 10);
|
||||
EXPECT_EQ(*rval, *tval);
|
||||
}
|
||||
|
||||
TEST_F(StagedSchedulerTest, step_10000) {
|
||||
// Do it a lot...
|
||||
for (auto i = 0; i < 10000; i++) {
|
||||
auto tval = mod->get_population()->get_agent_list();
|
||||
auto aval = mod->get_population()->get_agent_list();
|
||||
mod->step(std::move(aval));
|
||||
|
||||
auto rval = mod->retval;
|
||||
|
||||
EXPECT_EQ(rval->size(), 10);
|
||||
EXPECT_EQ(*rval, *tval);
|
||||
}
|
||||
}
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
109
test/unit-kami-stagedagent.cc
Normal file
109
test/unit-kami-stagedagent.cc
Normal file
@@ -0,0 +1,109 @@
|
||||
/*-
|
||||
* Copyright (c) 2022 The Johns Hopkins University Applied Physics
|
||||
* Laboratory LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/model.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace kami;
|
||||
using namespace std;
|
||||
|
||||
class TestAgent
|
||||
: public StagedAgent {
|
||||
public:
|
||||
AgentID advance(shared_ptr<Model> model) override {
|
||||
return get_agent_id();
|
||||
}
|
||||
|
||||
AgentID step(shared_ptr<Model> model) override {
|
||||
return get_agent_id();
|
||||
}
|
||||
};
|
||||
|
||||
class TestModel
|
||||
: public Model {
|
||||
};
|
||||
|
||||
class StagedAgentTest
|
||||
: public ::testing::Test {
|
||||
protected:
|
||||
TestAgent agent_foo;
|
||||
TestAgent agent_bar;
|
||||
shared_ptr<TestModel> model_world = nullptr;
|
||||
|
||||
void SetUp() override {
|
||||
model_world = make_shared<TestModel>();
|
||||
}
|
||||
};
|
||||
|
||||
TEST(StagedAgent, DefaultConstructor) {
|
||||
EXPECT_NO_THROW(
|
||||
const TestAgent agent_baz;
|
||||
const TestAgent agent_qux;
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(StagedAgentTest, equivalance) {
|
||||
EXPECT_EQ(agent_foo, agent_foo);
|
||||
EXPECT_NE(agent_foo, agent_bar);
|
||||
}
|
||||
|
||||
TEST_F(StagedAgentTest, get_agent_id) {
|
||||
EXPECT_EQ(agent_foo.get_agent_id(), agent_foo.get_agent_id());
|
||||
EXPECT_NE(agent_bar.get_agent_id(), agent_foo.get_agent_id());
|
||||
}
|
||||
|
||||
TEST_F(StagedAgentTest, step) {
|
||||
EXPECT_EQ(agent_foo.get_agent_id(), agent_foo.step(model_world));
|
||||
EXPECT_NE(agent_bar.get_agent_id(), agent_foo.step(model_world));
|
||||
}
|
||||
|
||||
TEST_F(StagedAgentTest, advance) {
|
||||
EXPECT_EQ(agent_foo.get_agent_id(), agent_foo.advance(model_world));
|
||||
EXPECT_NE(agent_bar.get_agent_id(), agent_foo.advance(model_world));
|
||||
}
|
||||
|
||||
TEST_F(StagedAgentTest, equality) {
|
||||
EXPECT_TRUE(agent_foo == agent_foo);
|
||||
EXPECT_TRUE(agent_bar == agent_bar);
|
||||
}
|
||||
|
||||
TEST_F(StagedAgentTest, inequality) {
|
||||
EXPECT_TRUE(agent_foo != agent_bar);
|
||||
EXPECT_FALSE(agent_bar != agent_bar);
|
||||
}
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user