mirror of
https://github.com/JHUAPL/kami.git
synced 2026-01-10 07:17:58 -05:00
Compare commits
78 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 | ||
|
|
a801720aa5 |
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
|
||||
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
|
||||
@@ -5,8 +5,8 @@ cmake_minimum_required(VERSION 3.13)
|
||||
set(PROJECT_NAME "kami")
|
||||
|
||||
set(VERSION_MAJOR 0)
|
||||
set(VERSION_MINOR 6)
|
||||
set(VERSION_PATCH 0)
|
||||
set(VERSION_MINOR 7)
|
||||
set(VERSION_PATCH 2)
|
||||
set(VERSION_STRING ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH})
|
||||
|
||||
project(${PROJECT_NAME}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2020-2022 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
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
[](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++
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ from conans import ConanFile, CMake
|
||||
|
||||
class KamiConan(ConanFile):
|
||||
name = "kami"
|
||||
version = "0.6.0"
|
||||
version = "0.7.2"
|
||||
license = "MIT"
|
||||
author = "James P. Howard, II <james.howard@jhu.edu>"
|
||||
url = "https://github.com/jhuapl/kami"
|
||||
@@ -49,3 +49,4 @@ class KamiConan(ConanFile):
|
||||
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,6 +1,27 @@
|
||||
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
|
||||
|
||||
@@ -27,7 +27,7 @@ if read_the_docs_build:
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'Kami'
|
||||
copyright = '2020-2022 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 ---------------------------------------------------
|
||||
@@ -94,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")
|
||||
|
||||
|
||||
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::
|
||||
@@ -15,6 +15,9 @@ Introduction
|
||||
: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++.
|
||||
|
||||
@@ -59,8 +62,10 @@ model.
|
||||
:maxdepth: 2
|
||||
|
||||
installation
|
||||
abm
|
||||
tutorial
|
||||
api/library_root
|
||||
examples/index
|
||||
changelog
|
||||
todo
|
||||
license
|
||||
@@ -1,7 +1,7 @@
|
||||
License
|
||||
=======
|
||||
|
||||
Copyright (c) 2020-2022 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,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
|
||||
|
||||
@@ -7,9 +7,8 @@ The list below is a list of things considered necessary before
|
||||
a 1.0 release. This list is *not* static.
|
||||
|
||||
- Network domain
|
||||
- Data collection process
|
||||
- Demonstration of data collection process
|
||||
- Tutorial write up
|
||||
- Hexgrid domain
|
||||
- Continuous grid domain
|
||||
|
||||
Wishlist
|
||||
--------
|
||||
@@ -17,8 +16,8 @@ The list below is a list of things considered nice to have at
|
||||
any point.
|
||||
|
||||
- Revise unit tests to take advantage of fixtures
|
||||
- Documentation with basic introduction to ABMs
|
||||
- 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::
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -8,9 +8,11 @@ set(EXAMPLE_NAME "boltzmann1d")
|
||||
|
||||
project(${EXAMPLE_NAME} LANGUAGES CXX)
|
||||
|
||||
file(GLOB EXAMPLE_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cc")
|
||||
|
||||
create_executable(
|
||||
NAME ${EXAMPLE_NAME}
|
||||
SOURCES boltzmann1d.cc
|
||||
SOURCES ${EXAMPLE_SOURCES}
|
||||
PUBLIC_DEFINITIONS USE_DOUBLE_PRECISION=1
|
||||
PRIVATE_DEFINITIONS DEBUG_VERBOSE
|
||||
PRIVATE_INCLUDE_PATHS ${CMAKE_SOURCE_DIR}/include
|
||||
|
||||
@@ -25,13 +25,11 @@
|
||||
|
||||
#include "boltzmann1d.h"
|
||||
|
||||
#include <exception>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <CLI/CLI.hpp>
|
||||
|
||||
@@ -46,18 +44,26 @@
|
||||
#include <kami/random.h>
|
||||
|
||||
std::shared_ptr<spdlog::logger> console = nullptr;
|
||||
std::shared_ptr<std::ranlux24> rng = 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) {
|
||||
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::GridCoord1D> : fmt::formatter<std::string> {
|
||||
static auto format(const kami::GridCoord1D &coord, format_context &ctx) {
|
||||
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());
|
||||
}
|
||||
};
|
||||
@@ -84,12 +90,12 @@ std::optional<kami::GridCoord1D> MoneyAgent1D::move_agent(std::shared_ptr<kami::
|
||||
auto domain = model->get_domain();
|
||||
if (!domain)
|
||||
throw (std::domain_error("model is missing domain"));
|
||||
auto world = std::static_pointer_cast<kami::MultiGrid1D>(domain.value());
|
||||
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.value();
|
||||
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));
|
||||
|
||||
@@ -107,26 +113,26 @@ std::optional<kami::AgentID> MoneyAgent1D::give_money(std::shared_ptr<kami::Mode
|
||||
auto domain = model->get_domain();
|
||||
if (!domain)
|
||||
throw (std::domain_error("model is missing domain"));
|
||||
auto world = std::static_pointer_cast<kami::MultiGrid1D>(domain.value());
|
||||
auto world = std::static_pointer_cast<kami::MultiGrid1D>(domain);
|
||||
|
||||
auto agents = model->get_population();
|
||||
if (!agents)
|
||||
throw (std::domain_error("model is missing population"));
|
||||
auto population = std::static_pointer_cast<kami::Population>(agents.value());
|
||||
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.value());
|
||||
auto cell_mates_opt = world->get_location_contents(location);
|
||||
|
||||
if (!cell_mates_opt)
|
||||
return std::nullopt;
|
||||
|
||||
auto cell_mates = cell_mates_opt.value();
|
||||
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).value());
|
||||
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;
|
||||
@@ -136,8 +142,12 @@ std::optional<kami::AgentID> MoneyAgent1D::give_money(std::shared_ptr<kami::Mode
|
||||
return other_agent_id;
|
||||
}
|
||||
|
||||
BoltzmannWealthModel1D::BoltzmannWealthModel1D(unsigned int number_agents, unsigned int length_x, unsigned int new_seed) {
|
||||
rng = std::make_shared<std::ranlux24>();
|
||||
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);
|
||||
@@ -169,7 +179,13 @@ std::shared_ptr<kami::Model> BoltzmannWealthModel1D::step() {
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma ide diagnostic ignored "EmptyDeclOrStmt"
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
std::string ident = "boltzmann1d";
|
||||
std::string log_level_option = "info";
|
||||
CLI::App app{ident};
|
||||
@@ -177,7 +193,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
// This exercise is really stupid.
|
||||
auto levels_list = std::make_unique<std::list<std::string>>();
|
||||
for (auto &level_name: SPDLOG_LEVEL_NAMES)
|
||||
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);
|
||||
@@ -191,8 +207,9 @@ 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.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);
|
||||
console->info(
|
||||
"Starting Boltzmann Wealth Model with {} agents on a {}-unit grid for {} steps", agent_count, x_size,
|
||||
max_steps);
|
||||
|
||||
spdlog::stopwatch sw;
|
||||
|
||||
@@ -202,3 +219,5 @@ int main(int argc, char **argv) {
|
||||
|
||||
console->info("Boltzmann Wealth Model simulation complete, requiring {} seconds", sw);
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
@@ -44,13 +44,16 @@
|
||||
/**
|
||||
* A sample agent for a one-dimensional Boltzmann wealth model
|
||||
*/
|
||||
class MoneyAgent1D : public kami::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
|
||||
@@ -81,7 +84,8 @@ private:
|
||||
/**
|
||||
* The one-dimensional Boltzmann wealth model
|
||||
*/
|
||||
class BoltzmannWealthModel1D : public kami::Model {
|
||||
class BoltzmannWealthModel1D
|
||||
: public kami::Model {
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -91,16 +95,19 @@ 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, unsigned int new_seed = 42);
|
||||
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.
|
||||
*/
|
||||
std::shared_ptr<kami::Model> step();
|
||||
std::shared_ptr<kami::Model> step() final;
|
||||
|
||||
private:
|
||||
unsigned int _step_count;
|
||||
|
||||
};
|
||||
|
||||
#endif // BOLTZMANN1D_H
|
||||
|
||||
@@ -8,9 +8,11 @@ set(EXAMPLE_NAME "boltzmann2d")
|
||||
|
||||
project(${EXAMPLE_NAME} LANGUAGES CXX)
|
||||
|
||||
file(GLOB EXAMPLE_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cc")
|
||||
|
||||
create_executable(
|
||||
NAME ${EXAMPLE_NAME}
|
||||
SOURCES boltzmann2d.cc
|
||||
SOURCES ${EXAMPLE_SOURCES}
|
||||
PUBLIC_DEFINITIONS USE_DOUBLE_PRECISION=1
|
||||
PRIVATE_DEFINITIONS DEBUG_VERBOSE
|
||||
PRIVATE_INCLUDE_PATHS ${CMAKE_SOURCE_DIR}/include
|
||||
|
||||
@@ -25,13 +25,11 @@
|
||||
|
||||
#include "boltzmann2d.h"
|
||||
|
||||
#include <exception>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <CLI/CLI.hpp>
|
||||
|
||||
@@ -46,18 +44,26 @@
|
||||
#include <kami/random.h>
|
||||
|
||||
std::shared_ptr<spdlog::logger> console = nullptr;
|
||||
std::shared_ptr<std::ranlux24> rng = 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) {
|
||||
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) {
|
||||
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());
|
||||
}
|
||||
};
|
||||
@@ -77,19 +83,19 @@ kami::AgentID MoneyAgent2D::step(std::shared_ptr<kami::Model> model) {
|
||||
return this->get_agent_id();
|
||||
}
|
||||
|
||||
std::optional<kami::GridCoord2D> MoneyAgent2D::move_agent(std::shared_ptr<kami::Model> model) {
|
||||
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 domain = model->get_domain();
|
||||
if (!domain)
|
||||
throw (std::domain_error("model is missing domain"));
|
||||
auto world = std::static_pointer_cast<kami::MultiGrid2D>(domain.value());
|
||||
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.value();
|
||||
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));
|
||||
|
||||
@@ -100,33 +106,33 @@ std::optional<kami::GridCoord2D> MoneyAgent2D::move_agent(std::shared_ptr<kami::
|
||||
return new_location;
|
||||
}
|
||||
|
||||
std::optional<kami::AgentID> MoneyAgent2D::give_money(std::shared_ptr<kami::Model> model) {
|
||||
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();
|
||||
|
||||
auto domain = model->get_domain();
|
||||
if (!domain)
|
||||
throw (std::domain_error("model is missing domain"));
|
||||
auto world = std::static_pointer_cast<kami::MultiGrid2D>(domain.value());
|
||||
auto world = std::static_pointer_cast<kami::MultiGrid2D>(domain);
|
||||
|
||||
auto agents = model->get_population();
|
||||
if (!agents)
|
||||
throw (std::domain_error("model is missing population"));
|
||||
auto population = std::static_pointer_cast<kami::Population>(agents.value());
|
||||
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.value());
|
||||
auto cell_mates_opt = world->get_location_contents(location);
|
||||
|
||||
if (!cell_mates_opt)
|
||||
return std::nullopt;
|
||||
|
||||
auto cell_mates = cell_mates_opt.value();
|
||||
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).value());
|
||||
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;
|
||||
@@ -136,8 +142,13 @@ std::optional<kami::AgentID> MoneyAgent2D::give_money(std::shared_ptr<kami::Mode
|
||||
return other_agent_id;
|
||||
}
|
||||
|
||||
BoltzmannWealthModel2D::BoltzmannWealthModel2D(unsigned int number_agents, unsigned int length_x, unsigned int length_y, unsigned int new_seed) {
|
||||
rng = std::make_shared<std::ranlux24>();
|
||||
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);
|
||||
|
||||
auto domain = std::make_shared<kami::MultiGrid2D>(length_x, length_y, true, true);
|
||||
@@ -170,7 +181,13 @@ std::shared_ptr<kami::Model> BoltzmannWealthModel2D::step() {
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma ide diagnostic ignored "EmptyDeclOrStmt"
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
std::string ident = "boltzmann2d";
|
||||
std::string log_level_option = "info";
|
||||
CLI::App app{ident};
|
||||
@@ -178,7 +195,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
// This exercise is really stupid.
|
||||
auto levels_list = std::make_unique<std::list<std::string>>();
|
||||
for (auto &level_name: SPDLOG_LEVEL_NAMES)
|
||||
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);
|
||||
@@ -193,8 +210,9 @@ 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.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);
|
||||
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;
|
||||
|
||||
@@ -204,3 +222,5 @@ int main(int argc, char **argv) {
|
||||
|
||||
console->info("Boltzmann Wealth Model simulation complete, requiring {} seconds", sw);
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
@@ -43,13 +43,16 @@
|
||||
/**
|
||||
* A sample agent for a two-dimensional Boltzmann wealth model
|
||||
*/
|
||||
class MoneyAgent2D : public kami::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
|
||||
@@ -64,23 +67,23 @@ public:
|
||||
/**
|
||||
* Move the agent to a random location on the world
|
||||
*/
|
||||
std::optional<kami::GridCoord2D> move_agent(std::shared_ptr<kami::Model> model);
|
||||
std::optional<kami::GridCoord2D> move_agent(const std::shared_ptr<kami::Model>& model);
|
||||
|
||||
/**
|
||||
* Give money to a random agent
|
||||
*/
|
||||
std::optional<kami::AgentID> give_money(std::shared_ptr<kami::Model> model);
|
||||
std::optional<kami::AgentID> give_money(const std::shared_ptr<kami::Model>& model);
|
||||
|
||||
private:
|
||||
int _step_counter;
|
||||
int _agent_wealth;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* The two-dimensional Boltzmann wealth model
|
||||
*/
|
||||
class BoltzmannWealthModel2D : public kami::Model {
|
||||
class BoltzmannWealthModel2D
|
||||
: public kami::Model {
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -94,16 +97,20 @@ 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);
|
||||
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.
|
||||
*/
|
||||
std::shared_ptr<kami::Model> step();
|
||||
std::shared_ptr<kami::Model> step() final;
|
||||
|
||||
private:
|
||||
unsigned int _step_count;
|
||||
|
||||
};
|
||||
|
||||
#endif // BOLTZMANN2D_H
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -8,9 +8,11 @@ set(EXAMPLE_NAME "starter")
|
||||
|
||||
project(${EXAMPLE_NAME} LANGUAGES CXX)
|
||||
|
||||
file(GLOB EXAMPLE_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cc")
|
||||
|
||||
create_executable(
|
||||
NAME ${EXAMPLE_NAME}
|
||||
SOURCES starter.cc
|
||||
SOURCES ${EXAMPLE_SOURCES}
|
||||
PUBLIC_DEFINITIONS USE_DOUBLE_PRECISION=1
|
||||
PRIVATE_DEFINITIONS DEBUG_VERBOSE
|
||||
PRIVATE_INCLUDE_PATHS ${CMAKE_SOURCE_DIR}/include
|
||||
|
||||
@@ -43,11 +43,15 @@
|
||||
#include <kami/random.h>
|
||||
|
||||
std::shared_ptr<spdlog::logger> console = nullptr;
|
||||
std::shared_ptr<std::ranlux24> rng = 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) {
|
||||
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());
|
||||
}
|
||||
};
|
||||
@@ -65,8 +69,11 @@ kami::AgentID StarterAgent::step(std::shared_ptr<kami::Model> model) {
|
||||
return this->get_agent_id();
|
||||
}
|
||||
|
||||
StarterModel::StarterModel(unsigned int number_agents, unsigned int new_seed) {
|
||||
rng = std::make_shared<std::ranlux24>();
|
||||
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);
|
||||
@@ -93,7 +100,10 @@ std::shared_ptr<kami::Model> StarterModel::step() {
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
std::string ident = "starter";
|
||||
std::string log_level_option = "info";
|
||||
CLI::App app{ident};
|
||||
@@ -101,7 +111,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
// This exercise is really stupid.
|
||||
auto levels_list = std::make_unique<std::list<std::string>>();
|
||||
for (auto &level_name: SPDLOG_LEVEL_NAMES)
|
||||
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);
|
||||
|
||||
@@ -39,7 +39,8 @@
|
||||
/**
|
||||
* A starter agent for a starter model
|
||||
*/
|
||||
class StarterAgent : public kami::Agent {
|
||||
class StarterAgent
|
||||
: public kami::Agent {
|
||||
private:
|
||||
int _step_counter = 0;
|
||||
|
||||
@@ -65,7 +66,8 @@ public:
|
||||
/**
|
||||
* The one-dimensional Boltzmann wealth model
|
||||
*/
|
||||
class StarterModel : public kami::Model {
|
||||
class StarterModel
|
||||
: public kami::Model {
|
||||
private:
|
||||
unsigned int _step_count;
|
||||
|
||||
@@ -77,7 +79,10 @@ public:
|
||||
* @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);
|
||||
explicit StarterModel(
|
||||
unsigned int number_agents = 10,
|
||||
unsigned int new_seed = 42
|
||||
);
|
||||
|
||||
/**
|
||||
* Execute a single time-step for the model.
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <kami/kami.h>
|
||||
#include <kami/model.h>
|
||||
|
||||
namespace kami {
|
||||
@@ -65,14 +64,14 @@ namespace kami {
|
||||
/**
|
||||
* @brief Constructs a new unique identifier.
|
||||
*/
|
||||
AgentID() : _id(_id_next++) {};
|
||||
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;
|
||||
|
||||
/**
|
||||
* @brief Test if two `AgentID` instances are equal.
|
||||
@@ -81,7 +80,10 @@ namespace kami {
|
||||
* @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
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Test if two `AgentID` instances are not equal.
|
||||
@@ -90,7 +92,10 @@ namespace kami {
|
||||
* @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
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Test if one AgentID is less than another.
|
||||
@@ -104,7 +109,10 @@ 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
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Output an AgentID to the specified output stream
|
||||
@@ -116,7 +124,10 @@ namespace kami {
|
||||
* @param rhs is the `AgentID` to output
|
||||
* @return the output stream for reuse
|
||||
*/
|
||||
friend std::ostream &operator<<(std::ostream &lhs, const AgentID &rhs);
|
||||
friend std::ostream& operator<<(
|
||||
std::ostream& lhs,
|
||||
const AgentID& rhs
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -126,7 +137,7 @@ namespace kami {
|
||||
* implement the `step()` function, to execute a single time step for each
|
||||
* agent.
|
||||
*
|
||||
* @see `StagedAgent`
|
||||
* @see `ReporterAgent`, `StagedAgent`
|
||||
*/
|
||||
class LIBKAMI_EXPORT Agent {
|
||||
private:
|
||||
@@ -147,6 +158,8 @@ namespace kami {
|
||||
* 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<Model> model) = 0;
|
||||
|
||||
@@ -164,7 +177,10 @@ 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
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Compare if two `Agent`s are not the same `Agent`.
|
||||
@@ -180,7 +196,10 @@ 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
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -195,7 +214,8 @@ namespace kami {
|
||||
*
|
||||
* `StagedAgents` must implement both the `step()` and `advance()` functions.
|
||||
*/
|
||||
class LIBKAMI_EXPORT StagedAgent : public Agent {
|
||||
class LIBKAMI_EXPORT StagedAgent
|
||||
: public Agent {
|
||||
public:
|
||||
/**
|
||||
* @brief Post-step advance the agent
|
||||
|
||||
@@ -82,7 +82,10 @@ namespace kami {
|
||||
* @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
|
||||
@@ -83,7 +83,17 @@ namespace kami {
|
||||
* "taxicab distance," "rectilinear distance," or many other [formal
|
||||
* names](https://en.wikipedia.org/wiki/Taxicab_geometry).
|
||||
*/
|
||||
Manhattan
|
||||
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
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -93,14 +103,38 @@ namespace kami {
|
||||
* rectilinear grid where the cells are equal size and laid out in an ordered
|
||||
* fashion.
|
||||
*/
|
||||
class LIBKAMI_EXPORT GridDomain : public Domain {};
|
||||
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 {};
|
||||
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
|
||||
|
||||
|
||||
@@ -35,8 +35,10 @@
|
||||
#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>
|
||||
|
||||
@@ -45,17 +47,18 @@ namespace kami {
|
||||
/**
|
||||
* @brief One-dimensional coordinates
|
||||
*/
|
||||
class LIBKAMI_EXPORT GridCoord1D : public GridCoord {
|
||||
class LIBKAMI_EXPORT GridCoord1D
|
||||
: public GridCoord {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor for one-dimensional coordinates
|
||||
*/
|
||||
explicit GridCoord1D(int x_coord) : _x_coord(x_coord){};
|
||||
explicit GridCoord1D(int x_coord);
|
||||
|
||||
/**
|
||||
* @brief Return the `x` coordinate
|
||||
*/
|
||||
[[nodiscard]] int get_x_location() const;
|
||||
[[nodiscard]] int x() const;
|
||||
|
||||
/**
|
||||
* @brief Convert the coordinate to a human-readable string.
|
||||
@@ -64,20 +67,87 @@ namespace kami {
|
||||
*/
|
||||
[[nodiscard]] std::string to_string() const override;
|
||||
|
||||
/**
|
||||
* @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`
|
||||
*/
|
||||
double distance(std::shared_ptr<Coord>& p) const override;
|
||||
|
||||
/**
|
||||
* @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
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Test if two coordinates are not equal
|
||||
*/
|
||||
friend bool operator!=(const GridCoord1D &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);
|
||||
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;
|
||||
@@ -91,7 +161,8 @@ namespace kami {
|
||||
* @see `MultiGrid1D`
|
||||
* @see `SoloGrid1D`
|
||||
*/
|
||||
class LIBKAMI_EXPORT Grid1D : public GridDomain {
|
||||
class LIBKAMI_EXPORT Grid1D
|
||||
: public GridDomain {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
@@ -99,7 +170,10 @@ namespace kami {
|
||||
* @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
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Place agent on the grid at the specified location.
|
||||
@@ -110,7 +184,10 @@ namespace kami {
|
||||
* @returns false if the agent is not placed at the specified
|
||||
* location, otherwise, true.
|
||||
*/
|
||||
virtual std::optional<AgentID> add_agent(const AgentID agent_id, const GridCoord1D &coord) = 0;
|
||||
virtual AgentID add_agent(
|
||||
AgentID agent_id,
|
||||
const GridCoord1D& coord
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* @brief Remove agent from the grid.
|
||||
@@ -119,7 +196,7 @@ namespace kami {
|
||||
*
|
||||
* @returns the `AgentID` of the `Agent` deleted
|
||||
*/
|
||||
std::optional<AgentID> delete_agent(const AgentID agent_id);
|
||||
AgentID delete_agent(AgentID agent_id);
|
||||
|
||||
/**
|
||||
* @brief Remove agent from the grid at the specified location
|
||||
@@ -129,7 +206,10 @@ namespace kami {
|
||||
*
|
||||
* @returns the `AgentID` of the `Agent` deleted
|
||||
*/
|
||||
std::optional<AgentID> delete_agent(const AgentID agent_id, const GridCoord1D &coord);
|
||||
AgentID delete_agent(
|
||||
AgentID agent_id,
|
||||
const GridCoord1D& coord
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Move an agent to the specified location.
|
||||
@@ -137,7 +217,10 @@ namespace kami {
|
||||
* @param[in] agent_id the `AgentID` of the agent to move.
|
||||
* @param[in] coord the coordinates of the agent.
|
||||
*/
|
||||
std::optional<AgentID> move_agent(const AgentID agent_id, const GridCoord1D &coord);
|
||||
AgentID move_agent(
|
||||
AgentID agent_id,
|
||||
const GridCoord1D& coord
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Inquire if the specified location is empty.
|
||||
@@ -165,7 +248,7 @@ namespace kami {
|
||||
*
|
||||
* @return the location of the specified `Agent`
|
||||
*/
|
||||
[[nodiscard]] std::optional<GridCoord1D> get_location_by_agent(const AgentID &agent_id) const;
|
||||
[[nodiscard]] GridCoord1D get_location_by_agent(const AgentID& agent_id) const;
|
||||
|
||||
/**
|
||||
* @brief Get the contents of the specified location.
|
||||
@@ -177,8 +260,8 @@ namespace kami {
|
||||
* to that object will update the state of the gird. Further, the pointer
|
||||
* should not be deleted when no longer used.
|
||||
*/
|
||||
[[nodiscard]] std::optional<std::shared_ptr<std::set<AgentID>>>
|
||||
get_location_contents(const GridCoord1D &coord) const;
|
||||
[[nodiscard]] std::shared_ptr<std::set<AgentID>>
|
||||
get_location_contents(const GridCoord1D& coord) const;
|
||||
|
||||
/**
|
||||
* @brief Inquire to whether the grid wraps in the `x` dimension.
|
||||
@@ -197,8 +280,11 @@ namespace kami {
|
||||
* @return an `unordered_set` of `GridCoord1D` that includes all of the coordinates
|
||||
* for all adjacent points.
|
||||
*/
|
||||
[[nodiscard]] std::optional<std::shared_ptr<std::unordered_set<GridCoord1D>>>
|
||||
get_neighborhood(const AgentID agent_id, const bool include_center) const;
|
||||
[[nodiscard]] std::shared_ptr<std::unordered_set<GridCoord1D>>
|
||||
get_neighborhood(
|
||||
AgentID agent_id,
|
||||
bool include_center
|
||||
) const;
|
||||
|
||||
/**
|
||||
* @brief Return the neighborhood of the specified location
|
||||
@@ -210,8 +296,11 @@ namespace kami {
|
||||
* @return an `unordered_set` of `GridCoord1D` that includes all of the coordinates
|
||||
* for all adjacent points.
|
||||
*/
|
||||
[[nodiscard]] std::optional<std::shared_ptr<std::unordered_set<GridCoord1D>>>
|
||||
get_neighborhood(const GridCoord1D &coord, const bool include_center) const;
|
||||
[[nodiscard]] std::shared_ptr<std::unordered_set<GridCoord1D>>
|
||||
get_neighborhood(
|
||||
const GridCoord1D& coord,
|
||||
bool include_center
|
||||
) const;
|
||||
|
||||
/**
|
||||
* @brief Get the size of the grid in the `x` dimension.
|
||||
@@ -221,6 +310,17 @@ namespace kami {
|
||||
[[nodiscard]] unsigned int get_maximum_x() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @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.
|
||||
@@ -239,7 +339,7 @@ namespace kami {
|
||||
*
|
||||
* @return the adjusted coordinate wrapped if appropriate.
|
||||
*/
|
||||
[[nodiscard]] GridCoord1D coord_wrap(const GridCoord1D &coord) const;
|
||||
[[nodiscard]] GridCoord1D coord_wrap(const GridCoord1D& coord) const;
|
||||
|
||||
private:
|
||||
unsigned int _maximum_x;
|
||||
@@ -248,13 +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.get_x_location()));
|
||||
size_t operator()(const kami::GridCoord1D& key) const {
|
||||
return (hash<int>()(key.x()));
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
//! @endcond
|
||||
|
||||
#endif // KAMI_GRID1D_H
|
||||
|
||||
@@ -29,12 +29,15 @@
|
||||
#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>
|
||||
#include <kami/grid.h>
|
||||
@@ -45,23 +48,26 @@ namespace kami {
|
||||
/**
|
||||
* @brief Two-dimensional coordinates
|
||||
*/
|
||||
class LIBKAMI_EXPORT GridCoord2D : public GridCoord {
|
||||
class LIBKAMI_EXPORT GridCoord2D
|
||||
: public GridCoord {
|
||||
public:
|
||||
/**
|
||||
* @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
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Get the coordinate in the first dimension or `x`.
|
||||
*/
|
||||
[[nodiscard]] int get_x_location() const;
|
||||
[[nodiscard]] int x() const;
|
||||
|
||||
/**
|
||||
* @brief Get the coordinate in the second dimension or `y`.
|
||||
*/
|
||||
[[nodiscard]] int get_y_location() const;
|
||||
[[nodiscard]] int y() const;
|
||||
|
||||
/**
|
||||
* @brief Convert the coordinate to a human-readable string.
|
||||
@@ -70,20 +76,135 @@ namespace kami {
|
||||
*/
|
||||
[[nodiscard]] std::string to_string() const override;
|
||||
|
||||
/**
|
||||
* @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`
|
||||
*/
|
||||
double distance(std::shared_ptr<Coord>& p) const override;
|
||||
|
||||
/**
|
||||
* @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`
|
||||
*/
|
||||
double
|
||||
distance(
|
||||
std::shared_ptr<GridCoord2D>& p,
|
||||
GridDistanceType distance_type = GridDistanceType::Euclidean
|
||||
) const;
|
||||
|
||||
/**
|
||||
* @brief Test if two coordinates are equal
|
||||
*/
|
||||
friend bool operator==(const GridCoord2D &, const GridCoord2D &);
|
||||
friend bool operator==(
|
||||
const GridCoord2D&,
|
||||
const GridCoord2D&
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Test if two coordinates are not equal
|
||||
*/
|
||||
friend bool operator!=(const GridCoord2D &, const GridCoord2D &);
|
||||
friend bool operator!=(
|
||||
const GridCoord2D&,
|
||||
const GridCoord2D&
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Output a given coordinate to the specified stream
|
||||
*/
|
||||
friend std::ostream &operator<<(std::ostream &, const GridCoord2D &);
|
||||
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;
|
||||
@@ -97,7 +218,8 @@ namespace kami {
|
||||
* @see `MultiGrid2D`
|
||||
* @see `SoloGrid2D`
|
||||
*/
|
||||
class LIBKAMI_EXPORT Grid2D : public GridDomain {
|
||||
class LIBKAMI_EXPORT Grid2D
|
||||
: public GridDomain {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
@@ -109,7 +231,12 @@ namespace kami {
|
||||
* @param[in] wrap_y should the grid wrap around on itself in the second
|
||||
* dimension
|
||||
*/
|
||||
explicit 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
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Place agent on the grid at the specified location.
|
||||
@@ -120,7 +247,10 @@ namespace kami {
|
||||
* @returns false if the agent is not placed at the specified
|
||||
* location, otherwise, true.
|
||||
*/
|
||||
virtual std::optional<AgentID> add_agent(const AgentID agent_id, const GridCoord2D &coord) = 0;
|
||||
virtual AgentID add_agent(
|
||||
AgentID agent_id,
|
||||
const GridCoord2D& coord
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* @brief Remove agent from the grid.
|
||||
@@ -129,7 +259,7 @@ namespace kami {
|
||||
*
|
||||
* @returns false if the agent is not removed, otherwise, true.
|
||||
*/
|
||||
std::optional<AgentID> delete_agent(const AgentID agent_id);
|
||||
AgentID delete_agent(AgentID agent_id);
|
||||
|
||||
/**
|
||||
* @brief Remove agent from the grid at the specified location
|
||||
@@ -139,7 +269,10 @@ namespace kami {
|
||||
*
|
||||
* @returns false if the agent is not removed, otherwise, true.
|
||||
*/
|
||||
std::optional<AgentID> delete_agent(const AgentID agent_id, const GridCoord2D &coord);
|
||||
AgentID delete_agent(
|
||||
AgentID agent_id,
|
||||
const GridCoord2D& coord
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Move an agent to the specified location.
|
||||
@@ -147,7 +280,10 @@ namespace kami {
|
||||
* @param[in] agent_id the `AgentID` of the agent to move.
|
||||
* @param[in] coord the coordinates of the agent.
|
||||
*/
|
||||
std::optional<AgentID> move_agent(const AgentID agent_id, const GridCoord2D &coord);
|
||||
AgentID move_agent(
|
||||
AgentID agent_id,
|
||||
const GridCoord2D& coord
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Inquire if the specified location is empty.
|
||||
@@ -168,14 +304,14 @@ namespace kami {
|
||||
*/
|
||||
[[nodiscard]] bool is_location_valid(const GridCoord2D& coord) const;
|
||||
|
||||
/**
|
||||
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]] std::optional<GridCoord2D> get_location_by_agent(const AgentID &agent_id) const;
|
||||
GridCoord2D get_location_by_agent(const AgentID& agent_id) const;
|
||||
|
||||
/**
|
||||
* @brief Get the contents of the specified location.
|
||||
@@ -187,15 +323,15 @@ namespace kami {
|
||||
* to that object will update the state of the gird. Further, the pointer
|
||||
* should not be deleted when no longer used.
|
||||
*/
|
||||
[[nodiscard]] std::optional<std::shared_ptr<std::set<AgentID>>>
|
||||
get_location_contents(const GridCoord2D &coord) const;
|
||||
[[nodiscard]] std::shared_ptr<std::set<AgentID>>
|
||||
get_location_contents(const GridCoord2D& coord) const;
|
||||
|
||||
/**
|
||||
* @brief Inquire to whether the grid wraps in the `x` dimension.
|
||||
*
|
||||
* @return true if the grid wraps, and false otherwise
|
||||
*/
|
||||
[[nodiscard]] bool get_wrap_x() const;
|
||||
[[nodiscard]] bool get_wrap_x() const;
|
||||
|
||||
/**
|
||||
* @brief Inquire to whether the grid wraps in the `y` dimension.
|
||||
@@ -204,7 +340,7 @@ namespace kami {
|
||||
*/
|
||||
[[nodiscard]] bool get_wrap_y() const;
|
||||
|
||||
/**
|
||||
virtual /**
|
||||
* @brief Return the neighborhood of the specified Agent
|
||||
*
|
||||
* @param[in] agent_id the `AgentID` of the agent in question.
|
||||
@@ -212,13 +348,17 @@ namespace kami {
|
||||
* @param[in] include_center should the center-point, occupied by the agent,
|
||||
* be in the list.
|
||||
*
|
||||
* @return a set 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::optional<std::shared_ptr<std::unordered_set<GridCoord2D>>>
|
||||
get_neighborhood(AgentID agent_id, bool include_center, GridNeighborhoodType neighborhood_type) const;
|
||||
std::shared_ptr<std::unordered_set<GridCoord2D>>
|
||||
get_neighborhood(
|
||||
AgentID agent_id,
|
||||
bool include_center,
|
||||
GridNeighborhoodType neighborhood_type
|
||||
) const;
|
||||
|
||||
/**
|
||||
* @brief Return the neighborhood of the specified location
|
||||
@@ -233,8 +373,12 @@ namespace kami {
|
||||
*
|
||||
* @see `NeighborhoodType`
|
||||
*/
|
||||
[[nodiscard]] std::optional<std::shared_ptr<std::unordered_set<GridCoord2D>>>
|
||||
get_neighborhood(const GridCoord2D &coord, bool include_center, GridNeighborhoodType neighborhood_type) const;
|
||||
[[nodiscard]] std::shared_ptr<std::unordered_set<GridCoord2D>>
|
||||
get_neighborhood(
|
||||
const GridCoord2D& coord,
|
||||
bool include_center,
|
||||
GridNeighborhoodType neighborhood_type
|
||||
) const;
|
||||
|
||||
/**
|
||||
* @brief Get the size of the grid in the `x` dimension.
|
||||
@@ -251,6 +395,28 @@ namespace kami {
|
||||
[[nodiscard]] unsigned int get_maximum_y() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @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.
|
||||
@@ -269,7 +435,7 @@ namespace kami {
|
||||
*
|
||||
* @return the adjusted coordinate wrapped if appropriate.
|
||||
*/
|
||||
[[nodiscard]] GridCoord2D coord_wrap(const GridCoord2D &coord) const;
|
||||
[[nodiscard]] GridCoord2D coord_wrap(const GridCoord2D& coord) const;
|
||||
|
||||
private:
|
||||
unsigned int _maximum_x, _maximum_y;
|
||||
@@ -278,13 +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.get_x_location()) ^ (hash<int>()(key.get_y_location()) << 1)) >> 1);
|
||||
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
|
||||
|
||||
@@ -38,10 +38,19 @@ 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;
|
||||
|
||||
/**
|
||||
@@ -49,7 +58,31 @@ namespace kami {
|
||||
*
|
||||
* @return a `semver::version` object containing version information
|
||||
*/
|
||||
inline semver::version get_version() { return version; }
|
||||
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
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
//! @endcond
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include <kami/domain.h>
|
||||
#include <kami/kami.h>
|
||||
@@ -41,8 +40,11 @@ namespace kami {
|
||||
|
||||
/**
|
||||
* @brief An abstract for generic models
|
||||
*
|
||||
* @see `ReporterModel`
|
||||
*/
|
||||
class LIBKAMI_EXPORT Model : public std::enable_shared_from_this<Model> {
|
||||
class LIBKAMI_EXPORT Model
|
||||
: public std::enable_shared_from_this<Model> {
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -50,7 +52,7 @@ namespace kami {
|
||||
*
|
||||
* @returns a shared pointer to the `Domain`
|
||||
*/
|
||||
std::optional<std::shared_ptr<Domain>> get_domain();
|
||||
std::shared_ptr<Domain> get_domain();
|
||||
|
||||
/**
|
||||
* @brief Add a `Domain` to this scheduler
|
||||
@@ -67,7 +69,7 @@ namespace kami {
|
||||
*
|
||||
* @returns a shared pointer to the `Population`
|
||||
*/
|
||||
std::optional<std::shared_ptr<Population>> get_population();
|
||||
std::shared_ptr<Population> get_population();
|
||||
|
||||
/**
|
||||
* @brief Add a `Model` to this scheduler
|
||||
@@ -84,7 +86,7 @@ namespace kami {
|
||||
*
|
||||
* @returns a shared pointer to the `Scheduler`
|
||||
*/
|
||||
std::optional<std::shared_ptr<Scheduler>> get_scheduler();
|
||||
std::shared_ptr<Scheduler> get_scheduler();
|
||||
|
||||
/**
|
||||
* @brief Add a `Model` to this scheduler
|
||||
@@ -96,6 +98,16 @@ namespace kami {
|
||||
*/
|
||||
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`
|
||||
|
||||
@@ -45,7 +45,8 @@ namespace kami {
|
||||
* @see `Grid1D`
|
||||
* @see `SoloGrid1D`
|
||||
*/
|
||||
class LIBKAMI_EXPORT MultiGrid1D : public Grid1D {
|
||||
class LIBKAMI_EXPORT MultiGrid1D
|
||||
: public Grid1D {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
@@ -53,8 +54,10 @@ namespace kami {
|
||||
* @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
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Place agent on the grid at the specified location.
|
||||
@@ -65,7 +68,10 @@ namespace kami {
|
||||
* @returns false if the agent is not placed at the specified
|
||||
* location, otherwise, true
|
||||
*/
|
||||
std::optional<AgentID> add_agent(const AgentID agent_id, const GridCoord1D &coord) override;
|
||||
AgentID add_agent(
|
||||
AgentID agent_id,
|
||||
const GridCoord1D& coord
|
||||
) override;
|
||||
};
|
||||
|
||||
} // namespace kami
|
||||
|
||||
@@ -45,7 +45,8 @@ namespace kami {
|
||||
* @see `Grid2D`
|
||||
* @see `SoloGrid2D`
|
||||
*/
|
||||
class LIBKAMI_EXPORT MultiGrid2D : public Grid2D {
|
||||
class LIBKAMI_EXPORT MultiGrid2D
|
||||
: public Grid2D {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
@@ -57,8 +58,12 @@ 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
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Place agent on the grid at the specified location.
|
||||
@@ -66,10 +71,12 @@ namespace kami {
|
||||
* @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
|
||||
*/
|
||||
std::optional<AgentID> add_agent(const AgentID agent_id, const GridCoord2D &coord) override;
|
||||
AgentID add_agent(
|
||||
AgentID agent_id,
|
||||
const GridCoord2D& coord
|
||||
) override;
|
||||
};
|
||||
|
||||
} // namespace kami
|
||||
|
||||
@@ -41,6 +41,41 @@ 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
|
||||
@@ -51,41 +86,6 @@ namespace kami {
|
||||
* wish to manipulate this mapping directly.
|
||||
*/
|
||||
std::map<kami::AgentID, std::shared_ptr<Agent>> _agent_map;
|
||||
|
||||
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::optional<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);
|
||||
|
||||
/**
|
||||
* @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::optional<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::shared_ptr<std::vector<AgentID>> get_agent_list() const;
|
||||
};
|
||||
} // namespace kami
|
||||
|
||||
|
||||
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
|
||||
@@ -30,7 +30,6 @@
|
||||
//! @endcond
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
@@ -39,7 +38,6 @@
|
||||
#include <kami/sequential.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
/**
|
||||
* @brief Will execute all agent steps in a random order.
|
||||
*
|
||||
@@ -48,10 +46,9 @@ namespace kami {
|
||||
* 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> {
|
||||
private:
|
||||
std::shared_ptr<std::ranlux24> _rng = nullptr;
|
||||
|
||||
class LIBKAMI_EXPORT RandomScheduler
|
||||
: public SequentialScheduler,
|
||||
std::enable_shared_from_this<RandomScheduler> {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor.
|
||||
@@ -64,7 +61,7 @@ namespace kami {
|
||||
* @param rng [in] A uniform random number generator of type
|
||||
* `std::mt19937`, used as the source of randomness.
|
||||
*/
|
||||
explicit RandomScheduler(std::shared_ptr<std::ranlux24> rng);
|
||||
explicit RandomScheduler(std::shared_ptr<std::mt19937> rng);
|
||||
|
||||
/**
|
||||
* @brief Execute a single time step.
|
||||
@@ -77,7 +74,28 @@ namespace kami {
|
||||
*
|
||||
* @returns returns vector of agents successfully stepped
|
||||
*/
|
||||
std::optional<std::shared_ptr<std::vector<AgentID>>> step(std::shared_ptr<Model> model, std::shared_ptr<std::vector<AgentID>> agent_list) override;
|
||||
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 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
|
||||
@@ -90,7 +108,7 @@ namespace kami {
|
||||
*
|
||||
* @returns a shared pointer to the random number generator
|
||||
*/
|
||||
std::shared_ptr<std::ranlux24> set_rng(std::shared_ptr<std::ranlux24> rng);
|
||||
std::shared_ptr<std::mt19937> set_rng(std::shared_ptr<std::mt19937> rng);
|
||||
|
||||
/**
|
||||
* @brief Get the RNG
|
||||
@@ -98,7 +116,11 @@ namespace kami {
|
||||
* @details Get a reference to the random number generator used to randomize
|
||||
* the order of agent stepping.
|
||||
*/
|
||||
std::shared_ptr<std::ranlux24> get_rng();
|
||||
std::shared_ptr<std::mt19937> get_rng();
|
||||
|
||||
private:
|
||||
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
|
||||
@@ -30,9 +30,10 @@
|
||||
//! @endcond
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/kami.h>
|
||||
#include <kami/model.h>
|
||||
|
||||
namespace kami {
|
||||
@@ -44,12 +45,6 @@ namespace kami {
|
||||
* the step function for each agent based on the type of scheduling implemented.
|
||||
*/
|
||||
class LIBKAMI_EXPORT Scheduler {
|
||||
protected:
|
||||
/**
|
||||
* Counter to increment on each step
|
||||
*/
|
||||
int _step_counter = 0;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Execute a single time step.
|
||||
@@ -63,7 +58,21 @@ namespace kami {
|
||||
*
|
||||
* @returns returns vector of agents successfully stepped
|
||||
*/
|
||||
virtual std::optional<std::shared_ptr<std::vector<AgentID>>> step(std::shared_ptr<Model> model) = 0;
|
||||
virtual std::unique_ptr<std::vector<AgentID>> step(std::shared_ptr<Model> model) = 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`
|
||||
*
|
||||
* @returns returns vector of agents successfully stepped
|
||||
*/
|
||||
virtual std::unique_ptr<std::vector<AgentID>> step(std::shared_ptr<ReporterModel> model) = 0;
|
||||
|
||||
/**
|
||||
* @brief Execute a single time step.
|
||||
@@ -78,7 +87,36 @@ namespace kami {
|
||||
*
|
||||
* @returns returns vector of agents successfully stepped
|
||||
*/
|
||||
virtual std::optional<std::shared_ptr<std::vector<AgentID>>> step(std::shared_ptr<Model> model, std::shared_ptr<std::vector<AgentID>> agent_list) = 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
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
//! @endcond
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <kami/agent.h>
|
||||
@@ -48,7 +47,8 @@ namespace kami {
|
||||
* That order is preserved between calls to `step()` but may be modified by
|
||||
* `addAgent()` or `deleteAgent()`.
|
||||
*/
|
||||
class LIBKAMI_EXPORT SequentialScheduler : public Scheduler {
|
||||
class LIBKAMI_EXPORT SequentialScheduler
|
||||
: public Scheduler {
|
||||
public:
|
||||
/**
|
||||
* @brief Execute a single time step.
|
||||
@@ -62,7 +62,21 @@ namespace kami {
|
||||
*
|
||||
* @returns returns vector of agents successfully stepped
|
||||
*/
|
||||
std::optional<std::shared_ptr<std::vector<AgentID>>> step(std::shared_ptr<Model> model) override;
|
||||
std::unique_ptr<std::vector<AgentID>> step(std::shared_ptr<Model> model) 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`
|
||||
*
|
||||
* @returns returns vector of agents successfully stepped
|
||||
*/
|
||||
std::unique_ptr<std::vector<AgentID>> step(std::shared_ptr<ReporterModel> model) override;
|
||||
|
||||
/**
|
||||
* @brief Execute a single time step.
|
||||
@@ -77,7 +91,30 @@ namespace kami {
|
||||
*
|
||||
* @returns returns vector of agents successfully stepped
|
||||
*/
|
||||
std::optional<std::shared_ptr<std::vector<AgentID>>> step(std::shared_ptr<Model> model, std::shared_ptr<std::vector<AgentID>> agent_list) override;
|
||||
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
|
||||
|
||||
@@ -37,14 +37,15 @@
|
||||
namespace kami {
|
||||
|
||||
/**
|
||||
* @brief A one-dimensional grid where each cell may contain one agenta
|
||||
* @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 {
|
||||
class LIBKAMI_EXPORT SoloGrid1D
|
||||
: public Grid1D {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
@@ -52,8 +53,10 @@ namespace kami {
|
||||
* @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
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Place agent on the grid at the specified location.
|
||||
@@ -64,7 +67,10 @@ namespace kami {
|
||||
* @returns false if the agent is not placed at the specified
|
||||
* location, otherwise, true
|
||||
*/
|
||||
std::optional<AgentID> add_agent(const AgentID agent_id, const GridCoord1D &coord) override;
|
||||
AgentID add_agent(
|
||||
AgentID agent_id,
|
||||
const GridCoord1D& coord
|
||||
) override;
|
||||
};
|
||||
|
||||
} // namespace kami
|
||||
|
||||
@@ -44,7 +44,8 @@ namespace kami {
|
||||
* @see `Grid2D`
|
||||
* @see `MultiGrid2D`
|
||||
*/
|
||||
class LIBKAMI_EXPORT SoloGrid2D : public Grid2D {
|
||||
class LIBKAMI_EXPORT SoloGrid2D
|
||||
: public Grid2D {
|
||||
public:
|
||||
/**
|
||||
* @details Constructor
|
||||
@@ -54,8 +55,12 @@ namespace kami {
|
||||
* @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
|
||||
);;
|
||||
|
||||
/**
|
||||
* @details Place agent on the grid at the specified location.
|
||||
@@ -66,7 +71,10 @@ namespace kami {
|
||||
* @returns false if the agent is not placed at the specified
|
||||
* location, otherwise, true
|
||||
*/
|
||||
std::optional<AgentID> add_agent(const AgentID agent_id, const GridCoord2D &coord) override;
|
||||
AgentID add_agent(
|
||||
AgentID agent_id,
|
||||
const GridCoord2D& coord
|
||||
) override;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
//! @endcond
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <kami/agent.h>
|
||||
@@ -47,37 +46,8 @@ namespace kami {
|
||||
* preserved between calls to `step()` but may be modified by `add_agent()` or
|
||||
* `delete_agent()`.
|
||||
*/
|
||||
class LIBKAMI_EXPORT StagedScheduler : public SequentialScheduler {
|
||||
private:
|
||||
/**
|
||||
* @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::optional<std::shared_ptr<std::vector<AgentID>>> advance(std::shared_ptr<Model> 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::optional<std::shared_ptr<std::vector<AgentID>>> advance(std::shared_ptr<Model> model, std::shared_ptr<std::vector<AgentID>> agent_list);
|
||||
|
||||
class LIBKAMI_EXPORT StagedScheduler
|
||||
: public SequentialScheduler {
|
||||
public:
|
||||
/**
|
||||
* @brief Execute a single time step
|
||||
@@ -92,7 +62,53 @@ namespace kami {
|
||||
*
|
||||
* @returns returns vector of agents successfully stepped
|
||||
*/
|
||||
std::optional<std::shared_ptr<std::vector<AgentID>>> step(std::shared_ptr<Model> model, std::shared_ptr<std::vector<AgentID>> agent_list) 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:
|
||||
/**
|
||||
* @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
|
||||
|
||||
@@ -10,25 +10,15 @@ 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
|
||||
agent.cc
|
||||
domain.cc
|
||||
grid1d.cc
|
||||
grid2d.cc
|
||||
model.cc
|
||||
multigrid1d.cc
|
||||
multigrid2d.cc
|
||||
population.cc
|
||||
random.cc
|
||||
sequential.cc
|
||||
sologrid1d.cc
|
||||
sologrid2d.cc
|
||||
staged.cc
|
||||
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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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,21 +23,27 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/domain.h>
|
||||
#include <kami/grid1d.h>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#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>
|
||||
|
||||
namespace kami {
|
||||
|
||||
int GridCoord1D::get_x_location() const {
|
||||
GridCoord1D::GridCoord1D(int x_coord)
|
||||
:_x_coord(x_coord) {
|
||||
}
|
||||
|
||||
int GridCoord1D::x() const {
|
||||
return _x_coord;
|
||||
}
|
||||
|
||||
@@ -45,19 +51,65 @@ namespace kami {
|
||||
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) {
|
||||
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;
|
||||
|
||||
@@ -65,89 +117,77 @@ namespace kami {
|
||||
_agent_index = std::make_unique<std::map<AgentID, GridCoord1D>>();
|
||||
}
|
||||
|
||||
std::optional<AgentID> Grid1D::delete_agent(AgentID agent_id) {
|
||||
auto coord = get_location_by_agent(agent_id);
|
||||
if (!coord)
|
||||
return std::nullopt;
|
||||
|
||||
return delete_agent(agent_id, coord.value());
|
||||
AgentID Grid1D::delete_agent(AgentID agent_id) {
|
||||
return delete_agent(agent_id, get_location_by_agent(agent_id));
|
||||
}
|
||||
|
||||
std::optional<AgentID> Grid1D::delete_agent(AgentID agent_id, const GridCoord1D &coord) {
|
||||
auto agent_location = _agent_grid->find(coord);
|
||||
if (agent_location == _agent_grid->end())
|
||||
return std::nullopt;
|
||||
|
||||
for (auto test_agent_id = agent_location; test_agent_id != _agent_grid->end(); test_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 std::nullopt;
|
||||
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();
|
||||
bool Grid1D::is_location_valid(const GridCoord1D& coord) const {
|
||||
auto x = coord.x();
|
||||
|
||||
return (x >= 0 && x < static_cast<int>(_maximum_x));
|
||||
}
|
||||
|
||||
bool Grid1D::is_location_empty(const GridCoord1D &coord) const {
|
||||
bool Grid1D::is_location_empty(const GridCoord1D& coord) const {
|
||||
auto grid_location = _agent_grid->equal_range(coord);
|
||||
return grid_location.first == grid_location.second;
|
||||
}
|
||||
|
||||
std::optional<AgentID> Grid1D::move_agent(const AgentID agent_id, const GridCoord1D &coord) {
|
||||
auto coord_current = get_location_by_agent(agent_id);
|
||||
|
||||
if (!coord_current)
|
||||
return std::nullopt;
|
||||
if (!delete_agent(agent_id, coord_current.value()))
|
||||
return std::nullopt;
|
||||
|
||||
return add_agent(agent_id, coord);
|
||||
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);
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<std::unordered_set<GridCoord1D>>>
|
||||
Grid1D::get_neighborhood(const AgentID agent_id, const bool include_center) const {
|
||||
auto coord = get_location_by_agent(agent_id);
|
||||
if (!coord)
|
||||
return std::nullopt;
|
||||
|
||||
return std::move(get_neighborhood(coord.value(), include_center));
|
||||
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::optional<std::shared_ptr<std::unordered_set<GridCoord1D>>>
|
||||
Grid1D::get_neighborhood(const GridCoord1D &coord, const bool include_center) const {
|
||||
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>>();
|
||||
auto x = coord.get_x_location();
|
||||
|
||||
// We assume our starting position is valid
|
||||
if (include_center)
|
||||
neighborhood->insert(coord);
|
||||
|
||||
// E, W
|
||||
{
|
||||
auto new_location = coord_wrap(GridCoord1D(x + 1));
|
||||
for (auto& direction : directions) {
|
||||
auto new_location = coord_wrap(coord + direction);
|
||||
|
||||
if (is_location_valid(new_location))
|
||||
neighborhood->insert(coord_wrap(new_location));
|
||||
}
|
||||
{
|
||||
auto new_location = coord_wrap(GridCoord1D(x - 1));
|
||||
if (is_location_valid(new_location))
|
||||
neighborhood->insert(coord_wrap(new_location));
|
||||
neighborhood->insert(new_location);
|
||||
}
|
||||
|
||||
return std::move(neighborhood);
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<std::set<AgentID>>> Grid1D::get_location_contents(const GridCoord1D &coord) const {
|
||||
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))
|
||||
return std::nullopt;
|
||||
throw error::LocationUnavailable(fmt::format("Coordinates {} are invalid", coord.to_string()));
|
||||
if (is_location_empty(coord))
|
||||
return agent_ids;
|
||||
|
||||
@@ -160,19 +200,23 @@ namespace kami {
|
||||
return agent_ids;
|
||||
}
|
||||
|
||||
bool Grid1D::get_wrap_x() const { return _wrap_x; }
|
||||
bool Grid1D::get_wrap_x() const {
|
||||
return _wrap_x;
|
||||
}
|
||||
|
||||
unsigned int Grid1D::get_maximum_x() const { return _maximum_x; }
|
||||
unsigned int Grid1D::get_maximum_x() const {
|
||||
return _maximum_x;
|
||||
}
|
||||
|
||||
std::optional<GridCoord1D> Grid1D::get_location_by_agent(const AgentID &agent_id) const {
|
||||
GridCoord1D Grid1D::get_location_by_agent(const AgentID& agent_id) const {
|
||||
auto coord = _agent_index->find(agent_id);
|
||||
if (coord == _agent_index->end())
|
||||
return std::nullopt;
|
||||
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();
|
||||
GridCoord1D Grid1D::coord_wrap(const GridCoord1D& coord) const {
|
||||
auto x = coord.x();
|
||||
|
||||
if (_wrap_x)
|
||||
x = (x + static_cast<int>(_maximum_x)) % static_cast<int>(_maximum_x);
|
||||
|
||||
@@ -23,24 +23,26 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/domain.h>
|
||||
#include <kami/grid2d.h>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/domain.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;
|
||||
}
|
||||
|
||||
@@ -48,20 +50,101 @@ namespace kami {
|
||||
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;
|
||||
@@ -71,128 +154,92 @@ namespace kami {
|
||||
_agent_index = std::make_unique<std::map<AgentID, GridCoord2D>>();
|
||||
}
|
||||
|
||||
std::optional<AgentID> Grid2D::delete_agent(AgentID agent_id) {
|
||||
auto coord = get_location_by_agent(agent_id);
|
||||
if (!coord)
|
||||
return std::nullopt;
|
||||
|
||||
return delete_agent(agent_id, coord.value());
|
||||
AgentID Grid2D::delete_agent(const AgentID agent_id) {
|
||||
return delete_agent(agent_id, get_location_by_agent(agent_id));
|
||||
}
|
||||
|
||||
std::optional<AgentID> Grid2D::delete_agent(AgentID agent_id, const GridCoord2D &coord) {
|
||||
auto agent_location = _agent_grid->find(coord);
|
||||
if (agent_location == _agent_grid->end())
|
||||
return std::nullopt;
|
||||
|
||||
for (auto test_agent_id = agent_location; test_agent_id != _agent_grid->end(); 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 agent_id;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
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();
|
||||
bool Grid2D::is_location_valid(const GridCoord2D& coord) const {
|
||||
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));
|
||||
}
|
||||
|
||||
bool Grid2D::is_location_empty(const GridCoord2D &coord) const {
|
||||
bool Grid2D::is_location_empty(const GridCoord2D& coord) const {
|
||||
auto grid_location = _agent_grid->equal_range(coord);
|
||||
return grid_location.first == grid_location.second;
|
||||
}
|
||||
|
||||
std::optional<AgentID> Grid2D::move_agent(const AgentID agent_id, const GridCoord2D &coord) {
|
||||
auto coord_current = get_location_by_agent(agent_id);
|
||||
|
||||
if (!coord_current)
|
||||
return std::nullopt;
|
||||
if (!delete_agent(agent_id, coord_current.value()))
|
||||
return std::nullopt;
|
||||
|
||||
return add_agent(agent_id, coord);
|
||||
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);
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<std::unordered_set<GridCoord2D>>>
|
||||
Grid2D::get_neighborhood(const AgentID agent_id, const bool include_center,
|
||||
const GridNeighborhoodType neighborhood_type) const {
|
||||
auto coord = get_location_by_agent(agent_id);
|
||||
if (!coord)
|
||||
return std::nullopt;
|
||||
|
||||
return std::move(get_neighborhood(coord.value(), include_center, neighborhood_type));
|
||||
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::optional<std::shared_ptr<std::unordered_set<GridCoord2D>>>
|
||||
Grid2D::get_neighborhood(const GridCoord2D &coord, const bool include_center,
|
||||
const GridNeighborhoodType neighborhood_type) const {
|
||||
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>>();
|
||||
auto x = coord.get_x_location();
|
||||
auto y = coord.get_y_location();
|
||||
std::vector<GridCoord2D> directions;
|
||||
|
||||
// We assume our starting position is valid
|
||||
if (include_center)
|
||||
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);
|
||||
|
||||
// N, E, S, W
|
||||
{
|
||||
auto new_location = coord_wrap(GridCoord2D(x, y - 1));
|
||||
if (is_location_valid(new_location))
|
||||
neighborhood->insert(coord_wrap(new_location));
|
||||
}
|
||||
{
|
||||
auto new_location = coord_wrap(GridCoord2D(x, y + 1));
|
||||
if (is_location_valid(new_location))
|
||||
neighborhood->insert(coord_wrap(new_location));
|
||||
}
|
||||
{
|
||||
auto new_location = coord_wrap(GridCoord2D(x + 1, y));
|
||||
if (is_location_valid(new_location))
|
||||
neighborhood->insert(coord_wrap(new_location));
|
||||
}
|
||||
{
|
||||
auto new_location = coord_wrap(GridCoord2D(x - 1, y));
|
||||
if (is_location_valid(new_location))
|
||||
neighborhood->insert(coord_wrap(new_location));
|
||||
}
|
||||
for (auto& direction : directions) {
|
||||
auto new_location = coord_wrap(coord + direction);
|
||||
|
||||
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->insert(coord_wrap(new_location));
|
||||
}
|
||||
{
|
||||
auto new_location = coord_wrap(GridCoord2D(x + 1, y + 1));
|
||||
if (is_location_valid(new_location))
|
||||
neighborhood->insert(coord_wrap(new_location));
|
||||
}
|
||||
{
|
||||
auto new_location = coord_wrap(GridCoord2D(x - 1, y + 1));
|
||||
if (is_location_valid(new_location))
|
||||
neighborhood->insert(coord_wrap(new_location));
|
||||
}
|
||||
{
|
||||
auto new_location = coord_wrap(GridCoord2D(x - 1, y - 1));
|
||||
if (is_location_valid(new_location))
|
||||
neighborhood->insert(coord_wrap(new_location));
|
||||
}
|
||||
if (is_location_valid(new_location))
|
||||
neighborhood->insert(new_location);
|
||||
}
|
||||
|
||||
return std::move(neighborhood);
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<std::set<AgentID>>> Grid2D::get_location_contents(const GridCoord2D &coord) const {
|
||||
std::shared_ptr<std::set<AgentID>> Grid2D::get_location_contents(const GridCoord2D& coord) const {
|
||||
auto agent_ids = std::make_shared<std::set<AgentID>>();
|
||||
|
||||
if (!is_location_valid(coord))
|
||||
return std::nullopt;
|
||||
throw error::LocationUnavailable(fmt::format("Coordinates {} are invalid", coord.to_string()));
|
||||
if (is_location_empty(coord))
|
||||
return agent_ids;
|
||||
|
||||
@@ -205,24 +252,32 @@ namespace kami {
|
||||
return agent_ids;
|
||||
}
|
||||
|
||||
bool Grid2D::get_wrap_x() const { return _wrap_x; }
|
||||
bool Grid2D::get_wrap_x() const {
|
||||
return _wrap_x;
|
||||
}
|
||||
|
||||
bool Grid2D::get_wrap_y() const { return _wrap_y; }
|
||||
bool Grid2D::get_wrap_y() const {
|
||||
return _wrap_y;
|
||||
}
|
||||
|
||||
unsigned int Grid2D::get_maximum_x() const { return _maximum_x; }
|
||||
unsigned int Grid2D::get_maximum_x() const {
|
||||
return _maximum_x;
|
||||
}
|
||||
|
||||
unsigned int Grid2D::get_maximum_y() const { return _maximum_y; }
|
||||
unsigned int Grid2D::get_maximum_y() const {
|
||||
return _maximum_y;
|
||||
}
|
||||
|
||||
std::optional<GridCoord2D> Grid2D::get_location_by_agent(const AgentID &agent_id) const {
|
||||
GridCoord2D Grid2D::get_location_by_agent(const AgentID& agent_id) const {
|
||||
auto coord = _agent_index->find(agent_id);
|
||||
if (coord == _agent_index->end())
|
||||
return std::nullopt;
|
||||
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();
|
||||
GridCoord2D Grid2D::coord_wrap(const GridCoord2D& coord) const {
|
||||
auto x = coord.x();
|
||||
auto y = coord.y();
|
||||
|
||||
if (_wrap_x)
|
||||
x = (x + static_cast<int>(_maximum_x)) % static_cast<int>(_maximum_x);
|
||||
|
||||
@@ -26,15 +26,16 @@
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include <kami/error.h>
|
||||
#include <kami/model.h>
|
||||
#include <kami/scheduler.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
std::optional<std::shared_ptr<Domain>> Model::get_domain() {
|
||||
if(_domain == nullptr)
|
||||
return std::nullopt;
|
||||
return(_domain);
|
||||
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) {
|
||||
@@ -42,10 +43,10 @@ namespace kami {
|
||||
return _domain;
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<Population>> Model::get_population() {
|
||||
if(_pop == nullptr)
|
||||
return std::nullopt;
|
||||
return(_pop);
|
||||
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) {
|
||||
@@ -53,10 +54,10 @@ namespace kami {
|
||||
return _pop;
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<Scheduler>> Model::get_scheduler() {
|
||||
if(_sched == nullptr)
|
||||
return std::nullopt;
|
||||
return(_sched);
|
||||
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) {
|
||||
@@ -64,4 +65,9 @@ namespace kami {
|
||||
return _sched;
|
||||
}
|
||||
|
||||
std::shared_ptr<Model> Model::step() {
|
||||
_sched->step(shared_from_this());
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
} // namespace kami
|
||||
|
||||
@@ -23,16 +23,29 @@
|
||||
* 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 {
|
||||
|
||||
std::optional<AgentID> MultiGrid1D::add_agent(const AgentID agent_id, const GridCoord1D &coord) {
|
||||
MultiGrid1D::MultiGrid1D(
|
||||
unsigned int maximum_x,
|
||||
bool wrap_x
|
||||
)
|
||||
:Grid1D(maximum_x, wrap_x) {
|
||||
}
|
||||
|
||||
AgentID MultiGrid1D::add_agent(
|
||||
const AgentID agent_id,
|
||||
const GridCoord1D& coord
|
||||
) {
|
||||
if (!is_location_valid(coord))
|
||||
return std::nullopt;
|
||||
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));
|
||||
|
||||
@@ -23,16 +23,31 @@
|
||||
* 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 {
|
||||
|
||||
std::optional<AgentID> MultiGrid2D::add_agent(const AgentID agent_id, const GridCoord2D &coord) {
|
||||
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) {
|
||||
}
|
||||
|
||||
AgentID MultiGrid2D::add_agent(
|
||||
const AgentID agent_id,
|
||||
const GridCoord2D& coord
|
||||
) {
|
||||
if (!is_location_valid(coord))
|
||||
return std::nullopt;
|
||||
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));
|
||||
|
||||
@@ -24,42 +24,47 @@
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#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) {
|
||||
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());
|
||||
return agent->get_agent_id();
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<Agent>> Population::delete_agent(const AgentID 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())
|
||||
return std::nullopt;
|
||||
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::make_optional(agent);
|
||||
return std::move(agent);
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<Agent>> Population::get_agent_by_id(const AgentID agent_id) const {
|
||||
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()) return(agent_it->second);
|
||||
return std::nullopt;
|
||||
if (agent_it == _agent_map.end())
|
||||
throw error::AgentNotFound("Agent not found in population");
|
||||
|
||||
return agent_it->second;
|
||||
}
|
||||
|
||||
std::shared_ptr<std::vector<AgentID>> Population::get_agent_list() const {
|
||||
auto key_selector = [](auto pair){ return pair.first; };
|
||||
auto agent_ids = std::make_shared<std::vector<AgentID>>(_agent_map.size());
|
||||
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);
|
||||
|
||||
@@ -25,34 +25,52 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <kami/error.h>
|
||||
#include <kami/model.h>
|
||||
#include <kami/random.h>
|
||||
#include <kami/sequential.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
RandomScheduler::RandomScheduler(std::shared_ptr<std::ranlux24> rng) {
|
||||
RandomScheduler::RandomScheduler(std::shared_ptr<std::mt19937> rng) {
|
||||
this->_rng = std::move(rng);
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<std::vector<AgentID>>> RandomScheduler::step(std::shared_ptr<Model> model, std::shared_ptr<std::vector<AgentID>> agent_list) {
|
||||
std::unique_ptr<std::vector<AgentID>>
|
||||
RandomScheduler::step(
|
||||
std::shared_ptr<Model> model,
|
||||
std::unique_ptr<std::vector<AgentID>> agent_list
|
||||
) {
|
||||
if (_rng == nullptr)
|
||||
return std::nullopt;
|
||||
throw error::ResourceNotAvailable("No random number generator available");
|
||||
|
||||
shuffle(agent_list->begin(), agent_list->end(), *_rng);
|
||||
return std::move(this->SequentialScheduler::step(model, agent_list));
|
||||
return std::move(this->SequentialScheduler::step(model, std::move(agent_list)));
|
||||
}
|
||||
|
||||
std::shared_ptr<std::ranlux24> RandomScheduler::set_rng(std::shared_ptr<std::ranlux24> rng) {
|
||||
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::ranlux24> RandomScheduler::get_rng() { return (this->_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
|
||||
@@ -24,40 +24,57 @@
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/reporter.h>
|
||||
#include <kami/sequential.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
std::optional<std::shared_ptr<std::vector<AgentID>>> SequentialScheduler::step(std::shared_ptr<Model> model) {
|
||||
std::unique_ptr<std::vector<AgentID>> SequentialScheduler::step(std::shared_ptr<Model> model) {
|
||||
auto population = model->get_population();
|
||||
|
||||
if(!population)
|
||||
return std::nullopt;
|
||||
|
||||
return std::move(this->step(model, population.value()->get_agent_list()));
|
||||
return std::move(this->step(model, population->get_agent_list()));
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<std::vector<AgentID>>> SequentialScheduler::step(std::shared_ptr<Model> model, std::shared_ptr<std::vector<AgentID>> agent_list) {
|
||||
auto return_agent_list = std::make_shared<std::vector<AgentID>>();
|
||||
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()));
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
if(!population)
|
||||
return std::nullopt;
|
||||
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);
|
||||
}
|
||||
|
||||
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_opt = population.value()->get_agent_by_id(agent_id);
|
||||
for (auto& agent_id : *agent_list) {
|
||||
auto agent = population->get_agent_by_id(agent_id);
|
||||
|
||||
if(agent_opt) {
|
||||
auto agent = agent_opt.value();
|
||||
|
||||
agent->step(model);
|
||||
return_agent_list->push_back(agent_id);
|
||||
}
|
||||
agent->step(model);
|
||||
return_agent_list->push_back(agent_id);
|
||||
}
|
||||
|
||||
return std::move(return_agent_list);
|
||||
|
||||
@@ -23,16 +23,29 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/error.h>
|
||||
#include <kami/sologrid1d.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
std::optional<AgentID> SoloGrid1D::add_agent(const AgentID agent_id, const GridCoord1D &coord) {
|
||||
SoloGrid1D::SoloGrid1D(
|
||||
unsigned int maximum_x,
|
||||
bool wrap_x
|
||||
)
|
||||
:Grid1D(maximum_x, wrap_x) {
|
||||
}
|
||||
|
||||
AgentID SoloGrid1D::add_agent(
|
||||
const AgentID agent_id,
|
||||
const GridCoord1D& coord
|
||||
) {
|
||||
if (!is_location_valid(coord))
|
||||
return std::nullopt;
|
||||
throw error::LocationInvalid(fmt::format("Coordinates {} are invalid", coord.to_string()));
|
||||
if (!is_location_empty(coord))
|
||||
return std::nullopt;
|
||||
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));
|
||||
|
||||
@@ -23,18 +23,33 @@
|
||||
* 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 {
|
||||
|
||||
std::optional<AgentID> SoloGrid2D::add_agent(const AgentID agent_id, const GridCoord2D &coord) {
|
||||
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) {
|
||||
}
|
||||
|
||||
AgentID SoloGrid2D::add_agent(
|
||||
const AgentID agent_id,
|
||||
const GridCoord2D& coord
|
||||
) {
|
||||
if (!is_location_valid(coord))
|
||||
return std::nullopt;
|
||||
throw error::LocationInvalid(fmt::format("Coordinates {} are invalid", coord.to_string()));
|
||||
if (!is_location_empty(coord))
|
||||
return std::nullopt;
|
||||
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));
|
||||
|
||||
@@ -24,45 +24,57 @@
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/model.h>
|
||||
#include <kami/reporter.h>
|
||||
#include <kami/sequential.h>
|
||||
#include <kami/staged.h>
|
||||
|
||||
namespace kami {
|
||||
|
||||
std::optional<std::shared_ptr<std::vector<AgentID>>> StagedScheduler::step(std::shared_ptr<Model> model, std::shared_ptr<std::vector<AgentID>> agent_list) {
|
||||
this->SequentialScheduler::step(model, agent_list);
|
||||
return std::move(this->advance(model, agent_list));
|
||||
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)));
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<std::vector<AgentID>>> StagedScheduler::advance(std::shared_ptr<Model> model) {
|
||||
auto population = model->get_population();
|
||||
|
||||
if(!population)
|
||||
return std::nullopt;
|
||||
|
||||
return std::move(this->advance(model, population.value()->get_agent_list()));
|
||||
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)));
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<std::vector<AgentID>>> StagedScheduler::advance(std::shared_ptr<Model> model, std::shared_ptr<std::vector<AgentID>> agent_list) {
|
||||
auto return_agent_list = std::make_shared<std::vector<AgentID>>();
|
||||
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()));
|
||||
}
|
||||
|
||||
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()));
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
if(!population)
|
||||
return std::nullopt;
|
||||
for (auto& agent_id : *agent_list) {
|
||||
auto agent = std::static_pointer_cast<StagedAgent>(population->get_agent_by_id(agent_id));
|
||||
|
||||
for(auto & agent_id : *agent_list) {
|
||||
auto agent_opt = population.value()->get_agent_by_id(agent_id);
|
||||
|
||||
if(agent_opt) {
|
||||
auto agent = std::static_pointer_cast<StagedAgent>(agent_opt.value());
|
||||
|
||||
agent->advance(model);
|
||||
return_agent_list->push_back(agent_id);
|
||||
}
|
||||
agent->advance(model);
|
||||
return_agent_list->push_back(agent_id);
|
||||
}
|
||||
|
||||
return std::move(return_agent_list);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
find_package(nlohmann_json 3.11.1 REQUIRED)
|
||||
|
||||
file(GLOB test_modules "${CMAKE_CURRENT_SOURCE_DIR}/*.cc")
|
||||
FOREACH (test_module ${test_modules})
|
||||
@@ -11,7 +12,7 @@ FOREACH (test_module ${test_modules})
|
||||
create_test(
|
||||
NAME ${test_src}
|
||||
SOURCES ${test_src}.cc
|
||||
PUBLIC_LINKED_TARGETS gmock gtest kami::libkami Threads::Threads
|
||||
PUBLIC_LINKED_TARGETS gmock gtest kami::libkami Threads::Threads nlohmann_json::nlohmann_json
|
||||
COMMAND ${test_src}
|
||||
PUBLIC_COMPILE_FEATURES ${COVERAGE_FLAGS}
|
||||
)
|
||||
|
||||
@@ -28,63 +28,72 @@
|
||||
#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 Agent {
|
||||
class TestAgent
|
||||
: public Agent {
|
||||
public:
|
||||
AgentID step(shared_ptr<Model> model) override {
|
||||
return get_agent_id();
|
||||
}
|
||||
};
|
||||
|
||||
class TestModel : public Model {
|
||||
class TestModel
|
||||
: public Model {
|
||||
};
|
||||
|
||||
class AgentTest
|
||||
: 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(Agent, DefaultConstructor) {
|
||||
const TestAgent agent_foo;
|
||||
const TestAgent agent_bar;
|
||||
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(Agent, get_agent_id) {
|
||||
const TestAgent agent_foo;
|
||||
const TestAgent 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(Agent, step) {
|
||||
TestAgent agent_foo;
|
||||
TestAgent agent_bar;
|
||||
auto model_world = make_shared<TestModel>();
|
||||
|
||||
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(Agent, Equality) {
|
||||
const TestAgent agent_foo;
|
||||
const TestAgent agent_bar;
|
||||
|
||||
TEST_F(AgentTest, equality) {
|
||||
EXPECT_TRUE(agent_foo == agent_foo);
|
||||
EXPECT_TRUE(agent_bar == agent_bar);
|
||||
}
|
||||
|
||||
TEST(Agent, Inequality) {
|
||||
const TestAgent agent_foo;
|
||||
const TestAgent agent_bar;
|
||||
|
||||
TEST_F(AgentTest, inequality) {
|
||||
EXPECT_TRUE(agent_foo != agent_bar);
|
||||
EXPECT_FALSE(agent_bar != agent_bar);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -30,48 +30,43 @@
|
||||
|
||||
using namespace kami;
|
||||
|
||||
TEST(AgentID, DefaultConstructor) {
|
||||
const AgentID agent_id_foo;
|
||||
const AgentID agent_id_bar;
|
||||
class AgentIDTest
|
||||
: public ::testing::Test {
|
||||
protected:
|
||||
AgentID agent_id_foo;
|
||||
AgentID agent_id_bar;
|
||||
};
|
||||
|
||||
TEST_F(AgentIDTest, DefaultConstructor) {
|
||||
EXPECT_EQ(agent_id_foo, agent_id_foo);
|
||||
EXPECT_NE(agent_id_foo, agent_id_bar);
|
||||
}
|
||||
|
||||
TEST(AgentID, to_string) {
|
||||
const AgentID agent_id_foo;
|
||||
const AgentID 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(AgentID, Equality) {
|
||||
const AgentID agent_id_foo;
|
||||
const AgentID agent_id_bar;
|
||||
|
||||
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(AgentID, Inequality) {
|
||||
const AgentID agent_id_foo;
|
||||
const AgentID agent_id_bar;
|
||||
|
||||
TEST_F(AgentIDTest, inequality) {
|
||||
EXPECT_TRUE(agent_id_foo != agent_id_bar);
|
||||
EXPECT_FALSE(agent_id_bar != agent_id_bar);
|
||||
}
|
||||
|
||||
TEST(AgentID, Ordering) {
|
||||
const AgentID agent_id_foo;
|
||||
const AgentID 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) {
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -30,12 +30,16 @@
|
||||
|
||||
using namespace kami;
|
||||
|
||||
TEST(GridCoord1D, DefaultConstructor) {
|
||||
const GridCoord1D gridcoord1d_foo(0);
|
||||
const GridCoord1D gridcoord1d_bar(1);
|
||||
const GridCoord1D gridcoord1d_baz(-1);
|
||||
const GridCoord1D gridcoord1d_qux(0);
|
||||
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);
|
||||
|
||||
@@ -44,22 +48,13 @@ TEST(GridCoord1D, DefaultConstructor) {
|
||||
EXPECT_NE(gridcoord1d_bar, gridcoord1d_baz);
|
||||
}
|
||||
|
||||
TEST(GridCoord1D, to_string) {
|
||||
const GridCoord1D gridcoord1d_foo(0);
|
||||
const GridCoord1D gridcoord1d_bar(1);
|
||||
const GridCoord1D gridcoord1d_baz(-1);
|
||||
|
||||
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(GridCoord1D, Equality) {
|
||||
const GridCoord1D gridcoord1d_foo(0);
|
||||
const GridCoord1D gridcoord1d_bar(1);
|
||||
const GridCoord1D gridcoord1d_baz(-1);
|
||||
const GridCoord1D gridcoord1d_qux(0);
|
||||
|
||||
TEST_F(GridCoord1DTest, equality) {
|
||||
EXPECT_TRUE(gridcoord1d_foo == gridcoord1d_foo);
|
||||
EXPECT_TRUE(gridcoord1d_foo == gridcoord1d_qux);
|
||||
|
||||
@@ -68,12 +63,7 @@ TEST(GridCoord1D, Equality) {
|
||||
EXPECT_FALSE(gridcoord1d_bar == gridcoord1d_baz);
|
||||
}
|
||||
|
||||
TEST(GridCoord1D, Inequality) {
|
||||
const GridCoord1D gridcoord1d_foo(0);
|
||||
const GridCoord1D gridcoord1d_bar(1);
|
||||
const GridCoord1D gridcoord1d_baz(-1);
|
||||
const GridCoord1D gridcoord1d_qux(0);
|
||||
|
||||
TEST_F(GridCoord1DTest, inequality) {
|
||||
EXPECT_FALSE(gridcoord1d_foo != gridcoord1d_foo);
|
||||
EXPECT_FALSE(gridcoord1d_foo != gridcoord1d_qux);
|
||||
|
||||
@@ -82,25 +72,23 @@ TEST(GridCoord1D, Inequality) {
|
||||
EXPECT_TRUE(gridcoord1d_bar != gridcoord1d_baz);
|
||||
}
|
||||
|
||||
TEST(GridCoord1D, get_x_location) {
|
||||
const GridCoord1D gridcoord1d_foo(0);
|
||||
const GridCoord1D gridcoord1d_bar(1);
|
||||
const GridCoord1D gridcoord1d_baz(-1);
|
||||
const GridCoord1D gridcoord1d_qux(0);
|
||||
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.get_x_location() == 0);
|
||||
EXPECT_TRUE(gridcoord1d_bar.get_x_location() == 1);
|
||||
EXPECT_TRUE(gridcoord1d_baz.get_x_location() == -1);
|
||||
EXPECT_TRUE(gridcoord1d_foo.x() == gridcoord1d_foo.x());
|
||||
EXPECT_TRUE(gridcoord1d_foo.x() == gridcoord1d_qux.x());
|
||||
|
||||
EXPECT_TRUE(gridcoord1d_foo.get_x_location() == gridcoord1d_foo.get_x_location());
|
||||
EXPECT_TRUE(gridcoord1d_foo.get_x_location() == gridcoord1d_qux.get_x_location());
|
||||
|
||||
EXPECT_FALSE(gridcoord1d_foo.get_x_location() == gridcoord1d_bar.get_x_location());
|
||||
EXPECT_FALSE(gridcoord1d_foo.get_x_location() == gridcoord1d_baz.get_x_location());
|
||||
EXPECT_FALSE(gridcoord1d_bar.get_x_location() == gridcoord1d_baz.get_x_location());
|
||||
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) {
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -30,13 +30,17 @@
|
||||
|
||||
using namespace kami;
|
||||
|
||||
TEST(GridCoord2D, DefaultConstructor) {
|
||||
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);
|
||||
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);
|
||||
@@ -47,7 +51,7 @@ TEST(GridCoord2D, DefaultConstructor) {
|
||||
|
||||
}
|
||||
|
||||
TEST(GridCoord2D, to_string) {
|
||||
TEST_F(GridCoord2DTest, to_string) {
|
||||
const GridCoord2D gridcoord2d_foo(0, 0);
|
||||
const GridCoord2D gridcoord2d_bar(1, 1);
|
||||
const GridCoord2D gridcoord2d_baz(-1, -1);
|
||||
@@ -61,7 +65,7 @@ TEST(GridCoord2D, to_string) {
|
||||
EXPECT_THAT(gridcoord2d_qu2.to_string(), "(1, 0)");
|
||||
}
|
||||
|
||||
TEST(GridCoord2D, Equality) {
|
||||
TEST_F(GridCoord2DTest, Equality) {
|
||||
const GridCoord2D gridcoord2d_foo(0, 0);
|
||||
const GridCoord2D gridcoord2d_bar(1, 1);
|
||||
const GridCoord2D gridcoord2d_baz(-1, -1);
|
||||
@@ -78,7 +82,7 @@ TEST(GridCoord2D, Equality) {
|
||||
EXPECT_FALSE(gridcoord2d_qux == gridcoord2d_qu2);
|
||||
}
|
||||
|
||||
TEST(GridCoord2D, Inequality) {
|
||||
TEST_F(GridCoord2DTest, Inequality) {
|
||||
const GridCoord2D gridcoord2d_foo(0, 0);
|
||||
const GridCoord2D gridcoord2d_bar(1, 1);
|
||||
const GridCoord2D gridcoord2d_baz(-1, -1);
|
||||
@@ -94,56 +98,58 @@ TEST(GridCoord2D, Inequality) {
|
||||
EXPECT_TRUE(gridcoord2d_qux != gridcoord2d_qu2);
|
||||
}
|
||||
|
||||
TEST(GridCoord2D, get_x_location) {
|
||||
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.get_x_location() == 0);
|
||||
EXPECT_TRUE(gridcoord2d_bar.get_x_location() == 1);
|
||||
EXPECT_TRUE(gridcoord2d_baz.get_x_location() == -1);
|
||||
EXPECT_FALSE(gridcoord2d_qux.get_x_location() == -1);
|
||||
EXPECT_FALSE(gridcoord2d_qu2.get_x_location() == -1);
|
||||
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.get_x_location() == gridcoord2d_foo.get_x_location());
|
||||
EXPECT_TRUE(gridcoord2d_foo.get_x_location() == gridcoord2d_qux.get_x_location());
|
||||
EXPECT_TRUE(gridcoord2d_bar.get_x_location() == gridcoord2d_qu2.get_x_location());
|
||||
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.get_x_location() == gridcoord2d_bar.get_x_location());
|
||||
EXPECT_FALSE(gridcoord2d_foo.get_x_location() == gridcoord2d_baz.get_x_location());
|
||||
EXPECT_FALSE(gridcoord2d_bar.get_x_location() == gridcoord2d_baz.get_x_location());
|
||||
EXPECT_FALSE(gridcoord2d_foo.get_x_location() == gridcoord2d_baz.get_x_location());
|
||||
EXPECT_FALSE(gridcoord2d_qux.get_x_location() == gridcoord2d_qu2.get_x_location());
|
||||
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(GridCoord2D, get_y_location) {
|
||||
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.get_y_location() == 0);
|
||||
EXPECT_TRUE(gridcoord2d_bar.get_y_location() == 1);
|
||||
EXPECT_TRUE(gridcoord2d_baz.get_y_location() == -1);
|
||||
EXPECT_FALSE(gridcoord2d_qux.get_y_location() == -1);
|
||||
EXPECT_FALSE(gridcoord2d_qu2.get_y_location() == -1);
|
||||
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.get_y_location() == gridcoord2d_foo.get_y_location());
|
||||
EXPECT_TRUE(gridcoord2d_bar.get_y_location() == gridcoord2d_qux.get_y_location());
|
||||
EXPECT_TRUE(gridcoord2d_foo.y() == gridcoord2d_foo.y());
|
||||
EXPECT_TRUE(gridcoord2d_bar.y() == gridcoord2d_qux.y());
|
||||
|
||||
EXPECT_FALSE(gridcoord2d_foo.get_y_location() == gridcoord2d_bar.get_y_location());
|
||||
EXPECT_FALSE(gridcoord2d_foo.get_y_location() == gridcoord2d_baz.get_y_location());
|
||||
EXPECT_FALSE(gridcoord2d_bar.get_y_location() == gridcoord2d_baz.get_y_location());
|
||||
EXPECT_FALSE(gridcoord2d_foo.get_y_location() == gridcoord2d_baz.get_y_location());
|
||||
EXPECT_FALSE(gridcoord2d_qux.get_y_location() == gridcoord2d_qu2.get_y_location());
|
||||
EXPECT_FALSE(gridcoord2d_bar.get_y_location() == gridcoord2d_qu2.get_y_location());
|
||||
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) {
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include <kami/agent.h>
|
||||
#include <kami/error.h>
|
||||
#include <kami/model.h>
|
||||
#include <kami/multigrid2d.h>
|
||||
#include <kami/population.h>
|
||||
@@ -34,18 +35,21 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace kami;
|
||||
using namespace kami::error;
|
||||
using namespace std;
|
||||
|
||||
class TestAgent : public Agent {
|
||||
class TestAgent
|
||||
: public Agent {
|
||||
public:
|
||||
AgentID step(shared_ptr<Model> model) override {
|
||||
return get_agent_id();
|
||||
}
|
||||
};
|
||||
|
||||
class TestModel : public Model {
|
||||
class TestModel
|
||||
: public Model {
|
||||
public:
|
||||
shared_ptr<Model> step() {
|
||||
shared_ptr<Model> step() final {
|
||||
return shared_from_this();
|
||||
}
|
||||
};
|
||||
@@ -61,24 +65,24 @@ TEST(Model, DefaultConstructor) {
|
||||
|
||||
TEST(Model, set_population) {
|
||||
auto model_foo = make_shared<TestModel>();
|
||||
auto popul_foo = make_shared<Population>();
|
||||
auto pop_foo = make_shared<Population>();
|
||||
|
||||
auto popul_bar = model_foo->set_population(popul_foo);
|
||||
EXPECT_EQ(popul_foo, popul_bar);
|
||||
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 popul_foo = make_shared<Population>();
|
||||
auto pop_foo = make_shared<Population>();
|
||||
|
||||
auto popul_nul = model_foo->get_population();
|
||||
EXPECT_THROW(auto pop_nul = model_foo->get_population(), ResourceNotAvailable);
|
||||
|
||||
auto popul_bar = model_foo->set_population(popul_foo);
|
||||
auto popul_baz = model_foo->get_population();
|
||||
auto pop_bar = model_foo->set_population(pop_foo);
|
||||
auto pop_baz = model_foo->get_population();
|
||||
|
||||
EXPECT_TRUE(popul_baz);
|
||||
EXPECT_EQ(popul_foo, popul_baz);
|
||||
EXPECT_EQ(popul_bar, popul_baz);
|
||||
EXPECT_TRUE(pop_baz);
|
||||
EXPECT_EQ(pop_foo, pop_baz);
|
||||
EXPECT_EQ(pop_bar, pop_baz);
|
||||
}
|
||||
|
||||
TEST(Model, set_scheduler) {
|
||||
@@ -93,8 +97,7 @@ TEST(Model, get_scheduler) {
|
||||
auto model_foo = make_shared<TestModel>();
|
||||
auto sched_foo = make_shared<SequentialScheduler>();
|
||||
|
||||
auto sched_nul = model_foo->get_scheduler();
|
||||
EXPECT_FALSE(sched_nul);
|
||||
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();
|
||||
@@ -116,8 +119,7 @@ TEST(Model, get_domain) {
|
||||
auto model_foo = make_shared<TestModel>();
|
||||
auto grid2_foo = make_shared<MultiGrid2D>(10, 10, true, true);
|
||||
|
||||
auto grid2_nul = model_foo->get_domain();
|
||||
EXPECT_FALSE(grid2_nul);
|
||||
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();
|
||||
@@ -127,7 +129,10 @@ TEST(Model, get_domain) {
|
||||
EXPECT_EQ(grid2_bar, grid2_baz);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace kami;
|
||||
using namespace kami::error;
|
||||
using namespace std;
|
||||
|
||||
TEST(MultiGrid1D, DefaultConstructor) {
|
||||
@@ -53,18 +54,15 @@ TEST(MultiGrid1D, add_agent) {
|
||||
|
||||
{
|
||||
auto agent_id_baz = multigrid1d_foo.add_agent(agent_id_foo, coord2);
|
||||
EXPECT_TRUE(agent_id_baz);
|
||||
EXPECT_EQ(agent_id_baz.value(), agent_id_foo);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_foo);
|
||||
}
|
||||
{
|
||||
auto agent_id_baz = multigrid1d_foo.add_agent(agent_id_bar, coord2);
|
||||
EXPECT_TRUE(agent_id_baz);
|
||||
EXPECT_EQ(agent_id_baz.value(), agent_id_bar);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_bar);
|
||||
}
|
||||
{
|
||||
auto agent_id_baz = multigrid1d_foo.add_agent(agent_id_bar, coord3);
|
||||
EXPECT_TRUE(agent_id_baz);
|
||||
EXPECT_EQ(agent_id_baz.value(), agent_id_bar);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_bar);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,8 +75,7 @@ TEST(MultiGrid1D, delete_agent) {
|
||||
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
auto agent_id_baz = multigrid1d_foo.delete_agent(agent_id_foo);
|
||||
EXPECT_TRUE(agent_id_baz);
|
||||
EXPECT_EQ(agent_id_baz.value(), agent_id_foo);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_foo);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
@@ -86,8 +83,7 @@ TEST(MultiGrid1D, delete_agent) {
|
||||
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_TRUE(agent_id_baz);
|
||||
EXPECT_EQ(agent_id_baz.value(), agent_id_foo);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_foo);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
@@ -95,8 +91,7 @@ TEST(MultiGrid1D, delete_agent) {
|
||||
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_TRUE(agent_id_baz);
|
||||
EXPECT_EQ(agent_id_baz.value(), agent_id_bar);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_bar);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -104,8 +99,7 @@ TEST(MultiGrid1D, delete_agent) {
|
||||
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
auto agent_id_baz = multigrid1d_foo.delete_agent(agent_id_foo, coord2);
|
||||
EXPECT_TRUE(agent_id_baz);
|
||||
EXPECT_EQ(agent_id_baz.value(), agent_id_foo);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_foo);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
@@ -113,8 +107,7 @@ TEST(MultiGrid1D, delete_agent) {
|
||||
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_TRUE(agent_id_baz);
|
||||
EXPECT_EQ(agent_id_baz.value(), agent_id_foo);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_foo);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
@@ -122,31 +115,27 @@ TEST(MultiGrid1D, delete_agent) {
|
||||
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_TRUE(agent_id_baz);
|
||||
EXPECT_EQ(agent_id_baz.value(), 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, coord3);
|
||||
EXPECT_FALSE(agent_id_baz);
|
||||
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));
|
||||
auto agent_id_baz = multigrid1d_foo.delete_agent(agent_id_foo, coord3);
|
||||
EXPECT_FALSE(agent_id_baz);
|
||||
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));
|
||||
auto agent_id_baz = multigrid1d_foo.delete_agent(agent_id_bar, coord3);
|
||||
EXPECT_FALSE(agent_id_baz);
|
||||
EXPECT_THROW(auto agent_id_baz = multigrid1d_foo.delete_agent(agent_id_bar, coord3), AgentNotFound);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,15 +191,13 @@ TEST(MultiGrid1D, move_agent) {
|
||||
|
||||
static_cast<void>(multigrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
auto agent_id_baz = multigrid1d_foo.move_agent(agent_id_foo, coord7);
|
||||
EXPECT_TRUE(agent_id_baz);
|
||||
EXPECT_EQ(agent_id_baz.value(), 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));
|
||||
auto agent_id_baz = multigrid1d_foo.move_agent(agent_id_foo, coord10);
|
||||
EXPECT_FALSE(agent_id_baz);
|
||||
EXPECT_THROW(auto agent_id_baz = multigrid1d_foo.move_agent(agent_id_foo, coord10), LocationInvalid);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
@@ -218,8 +205,7 @@ TEST(MultiGrid1D, move_agent) {
|
||||
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_TRUE(agent_id_baz);
|
||||
EXPECT_EQ(agent_id_baz.value(), agent_id_foo);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_foo);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
@@ -227,172 +213,189 @@ TEST(MultiGrid1D, move_agent) {
|
||||
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_TRUE(agent_id_baz);
|
||||
EXPECT_EQ(agent_id_baz.value(), agent_id_foo);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_foo);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MultiGrid1D, get_neighborhood) {
|
||||
const AgentID agent_id_foo, agent_id_bar;
|
||||
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 tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1, coord9
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(coord0, true);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
auto tval = unordered_set<GridCoord1D>({coord0, coord1, coord2});
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1, coord2
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(coord1, true);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, false);
|
||||
|
||||
auto tval = unordered_set<GridCoord1D>({coord0, coord1});
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(coord0, true);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, false);
|
||||
|
||||
auto tval = unordered_set<GridCoord1D>({coord0, coord1, coord2});
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1, coord2
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(coord1, true);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
auto tval = unordered_set<GridCoord1D>({coord1, coord9});
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord1, coord9
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(coord0, false);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
auto tval = unordered_set<GridCoord1D>({coord0, coord2});
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord2
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(coord1, false);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, false);
|
||||
|
||||
auto tval = unordered_set<GridCoord1D>({coord1});
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord1
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(coord0, false);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, false);
|
||||
|
||||
auto tval = unordered_set<GridCoord1D>({coord0, coord2});
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord2
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(coord1, false);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
auto tval = unordered_set<GridCoord1D>({coord0, coord1, coord9});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, true);
|
||||
|
||||
EXPECT_FALSE(rval);
|
||||
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 tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1, coord9
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, true);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
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 tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1, coord2
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, true);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
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 tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, true);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
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 tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1, coord2
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, true);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
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 tval = unordered_set < GridCoord1D > ({
|
||||
coord1, coord9
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, false);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
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 tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord2
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, false);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, false);
|
||||
multigrid1d_foo.add_agent(agent_id_foo, coord0);
|
||||
|
||||
auto tval = unordered_set<GridCoord1D>({coord1});
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord1
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, false);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
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 tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord2
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_neighborhood(agent_id_foo, false);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,17 +406,16 @@ TEST(MultiGrid1D, get_location_by_agent) {
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
|
||||
EXPECT_FALSE(multigrid1d_foo.get_location_by_agent(agent_id_foo));
|
||||
EXPECT_FALSE(multigrid1d_foo.get_location_by_agent(agent_id_bar));
|
||||
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_TRUE(local);
|
||||
EXPECT_EQ(local, coord2);
|
||||
EXPECT_FALSE(multigrid1d_foo.get_location_by_agent(agent_id_bar));
|
||||
EXPECT_THROW(auto loc = multigrid1d_foo.get_location_by_agent(agent_id_bar), AgentNotFound);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,9 +425,7 @@ TEST(MultiGrid1D, get_location_contents) {
|
||||
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
auto agent_list_foo = multigrid1d_foo.get_location_contents(coord10);
|
||||
|
||||
EXPECT_FALSE(agent_list_foo);
|
||||
EXPECT_THROW(auto agent_list_foo = multigrid1d_foo.get_location_contents(coord10), LocationUnavailable);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
@@ -433,7 +433,7 @@ TEST(MultiGrid1D, get_location_contents) {
|
||||
auto agent_list_foo = multigrid1d_foo.get_location_contents(coord1);
|
||||
|
||||
EXPECT_TRUE(agent_list_foo);
|
||||
EXPECT_TRUE(agent_list_foo.value()->empty());
|
||||
EXPECT_TRUE(agent_list_foo->empty());
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
@@ -441,11 +441,13 @@ TEST(MultiGrid1D, get_location_contents) {
|
||||
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 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.value());
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
MultiGrid1D multigrid1d_foo(10, true);
|
||||
@@ -453,15 +455,20 @@ TEST(MultiGrid1D, get_location_contents) {
|
||||
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 tval = set < AgentID > ({
|
||||
agent_id_foo, agent_id_bar
|
||||
});
|
||||
auto rval = multigrid1d_foo.get_location_contents(coord1);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,20 +24,25 @@
|
||||
*/
|
||||
|
||||
#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 {
|
||||
class TestAgent
|
||||
: public Agent {
|
||||
private:
|
||||
int _x;
|
||||
|
||||
public:
|
||||
explicit TestAgent(int x) : _x(x) {};
|
||||
explicit TestAgent(int x)
|
||||
:_x(x) {
|
||||
};
|
||||
|
||||
AgentID step(shared_ptr<Model> model) override {
|
||||
return get_agent_id();
|
||||
@@ -88,14 +93,13 @@ TEST(Population, get_agent_by_id) {
|
||||
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.value());
|
||||
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));
|
||||
auto agent_baz_opt = population_foo.get_agent_by_id(agent_bar->get_agent_id());
|
||||
EXPECT_FALSE(agent_baz_opt);
|
||||
EXPECT_THROW(auto agent_baz_opt = population_foo.get_agent_by_id(agent_bar->get_agent_id()), AgentNotFound);
|
||||
}
|
||||
{
|
||||
Population population_foo;
|
||||
@@ -105,13 +109,13 @@ TEST(Population, get_agent_by_id) {
|
||||
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.value());
|
||||
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.value());
|
||||
auto agent_qux = dynamic_pointer_cast<TestAgent>(agent_qux_opt);
|
||||
EXPECT_EQ(agent_qux->getval(), 1729);
|
||||
}
|
||||
{
|
||||
@@ -122,13 +126,13 @@ TEST(Population, get_agent_by_id) {
|
||||
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.value());
|
||||
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.value());
|
||||
auto agent_baz = dynamic_pointer_cast<TestAgent>(agent_baz_opt);
|
||||
EXPECT_EQ(agent_baz->getval(), 8675309);
|
||||
}
|
||||
}
|
||||
@@ -190,7 +194,10 @@ TEST(Population, get_agent_list) {
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -39,32 +39,39 @@
|
||||
using namespace kami;
|
||||
using namespace std;
|
||||
|
||||
class TestAgent : public Agent {
|
||||
class TestAgent
|
||||
: public Agent {
|
||||
public:
|
||||
AgentID step(shared_ptr<Model> model) override {
|
||||
return get_agent_id();
|
||||
}
|
||||
};
|
||||
|
||||
class TestModel : public Model {
|
||||
class TestModel
|
||||
: public Model {
|
||||
public:
|
||||
optional<shared_ptr<vector<AgentID>>> step() {
|
||||
return _sched->step(shared_from_this());
|
||||
shared_ptr<vector<AgentID>> retval;
|
||||
|
||||
shared_ptr<Model> step() override {
|
||||
retval = _sched->step(shared_from_this());
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
optional<shared_ptr<vector<AgentID>>> step(shared_ptr<vector<AgentID>> agent_list) {
|
||||
return _sched->step(shared_from_this(), move(agent_list));
|
||||
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 {
|
||||
class RandomSchedulerTest
|
||||
: public ::testing::Test {
|
||||
protected:
|
||||
shared_ptr<TestModel> mod = nullptr;
|
||||
shared_ptr<ranlux24> rng = nullptr;
|
||||
shared_ptr<mt19937> rng = nullptr;
|
||||
|
||||
void SetUp() override {
|
||||
mod = make_shared<TestModel>();
|
||||
rng = make_shared<ranlux24>();
|
||||
rng = make_shared<mt19937>();
|
||||
auto popul_foo = make_shared<Population>();
|
||||
auto sched_foo = make_shared<RandomScheduler>(rng);
|
||||
|
||||
@@ -89,64 +96,75 @@ TEST(RandomScheduler, DefaultConstructor) {
|
||||
}
|
||||
|
||||
TEST_F(RandomSchedulerTest, step_interface1) {
|
||||
auto tval = mod->get_population().value()->get_agent_list();
|
||||
auto rval = mod->step();
|
||||
auto tval = mod->get_population()->get_agent_list();
|
||||
auto aval = mod->get_population()->get_agent_list();
|
||||
mod->step(std::move(aval));
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(rval.value()->size(), 10);
|
||||
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.value()->begin(), rval.value()->end());
|
||||
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().value()->get_agent_list();
|
||||
auto rval = mod->step(tval);
|
||||
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.value()->size(), 10);
|
||||
EXPECT_EQ(rval->size(), 10);
|
||||
|
||||
set<AgentID> tval_set = set(tval->begin(), tval->end());
|
||||
set<AgentID> rval_set = set(rval.value()->begin(), rval.value()->end());
|
||||
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) {
|
||||
auto tval = mod->get_population().value()->get_agent_list();
|
||||
set<AgentID> tval_set = set(tval->begin(), tval->end());
|
||||
|
||||
// Do it a lot...
|
||||
for (auto i = 0; i < 10000; i++) {
|
||||
auto rval = mod->step();
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(rval.value()->size(), 10);
|
||||
auto tval = mod->get_population()->get_agent_list();
|
||||
auto aval = mod->get_population()->get_agent_list();
|
||||
mod->step(std::move(aval));
|
||||
|
||||
set<AgentID> rval_set = set(rval.value()->begin(), rval.value()->end());
|
||||
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().value())->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<ranlux24>();
|
||||
auto rval1 = static_pointer_cast<RandomScheduler>(mod->get_scheduler().value())->get_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().value())->set_rng(new_rng));
|
||||
auto rval2 = static_pointer_cast<RandomScheduler>(mod->get_scheduler().value())->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) {
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -37,40 +37,47 @@
|
||||
using namespace kami;
|
||||
using namespace std;
|
||||
|
||||
class TestAgent : public Agent {
|
||||
class TestAgent
|
||||
: public Agent {
|
||||
public:
|
||||
AgentID step(shared_ptr<Model> model) override {
|
||||
return get_agent_id();
|
||||
}
|
||||
};
|
||||
|
||||
class TestModel : public Model {
|
||||
class TestModel
|
||||
: public Model {
|
||||
public:
|
||||
optional<shared_ptr<vector<AgentID>>> step() {
|
||||
return _sched->step(shared_from_this());
|
||||
shared_ptr<vector<AgentID>> retval;
|
||||
|
||||
shared_ptr<Model> step() override {
|
||||
retval = _sched->step(shared_from_this());
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
optional<shared_ptr<vector<AgentID>>> step(shared_ptr<vector<AgentID>> agent_list) {
|
||||
return _sched->step(shared_from_this(), move(agent_list));
|
||||
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 {
|
||||
class SequentialSchedulerTest
|
||||
: public ::testing::Test {
|
||||
protected:
|
||||
shared_ptr<TestModel> mod = nullptr;
|
||||
|
||||
void SetUp() override {
|
||||
mod = make_shared<TestModel>();
|
||||
auto popul_foo = make_shared<Population>();
|
||||
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(popul_foo));
|
||||
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>(popul_foo->add_agent(agent_foo));
|
||||
static_cast<void>(pop_foo->add_agent(agent_foo));
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -85,36 +92,48 @@ TEST(SequentialScheduler, DefaultConstructor) {
|
||||
}
|
||||
|
||||
TEST_F(SequentialSchedulerTest, step_interface1) {
|
||||
auto tval = mod->get_population().value()->get_agent_list();
|
||||
auto rval = mod->step();
|
||||
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.value()->size(), 10);
|
||||
EXPECT_EQ(*rval.value(), *tval);
|
||||
EXPECT_EQ(rval->size(), 10);
|
||||
EXPECT_EQ(*rval, *tval);
|
||||
}
|
||||
|
||||
TEST_F(SequentialSchedulerTest, step_interface2) {
|
||||
auto tval = mod->get_population().value()->get_agent_list();
|
||||
auto rval = mod->step(tval);
|
||||
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.value()->size(), 10);
|
||||
EXPECT_EQ(*rval.value(), *tval);
|
||||
EXPECT_EQ(rval->size(), 10);
|
||||
EXPECT_EQ(*rval, *tval);
|
||||
}
|
||||
|
||||
TEST_F(SequentialSchedulerTest, step_10000) {
|
||||
auto tval = mod->get_population().value()->get_agent_list();
|
||||
|
||||
// Do it a lot...
|
||||
for (auto i = 0; i < 10000; i++) {
|
||||
auto rval = mod->step();
|
||||
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.value()->size(), 10);
|
||||
EXPECT_EQ(*rval.value(), *tval);
|
||||
EXPECT_EQ(rval->size(), 10);
|
||||
EXPECT_EQ(*rval, *tval);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -29,12 +29,14 @@
|
||||
#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) {
|
||||
@@ -53,17 +55,14 @@ TEST(SoloGrid1D, add_agent) {
|
||||
|
||||
{
|
||||
auto agent_id_baz = sologrid1d_foo.add_agent(agent_id_foo, coord2);
|
||||
EXPECT_TRUE(agent_id_baz);
|
||||
EXPECT_EQ(agent_id_baz.value(), agent_id_foo);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_foo);
|
||||
}
|
||||
{
|
||||
auto agent_id_baz = sologrid1d_foo.add_agent(agent_id_bar, coord2);
|
||||
EXPECT_FALSE(agent_id_baz);
|
||||
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_TRUE(agent_id_baz);
|
||||
EXPECT_EQ(agent_id_baz.value(), agent_id_bar);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_bar);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,25 +75,22 @@ TEST(SoloGrid1D, delete_agent) {
|
||||
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_foo);
|
||||
EXPECT_TRUE(agent_id_baz);
|
||||
EXPECT_EQ(agent_id_baz.value(), 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));
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_bar, 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_TRUE(agent_id_baz);
|
||||
EXPECT_EQ(agent_id_baz.value(), 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));
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_bar, coord2));
|
||||
auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_bar);
|
||||
EXPECT_FALSE(agent_id_baz);
|
||||
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);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -102,48 +98,42 @@ TEST(SoloGrid1D, delete_agent) {
|
||||
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_foo, coord2);
|
||||
EXPECT_TRUE(agent_id_baz);
|
||||
EXPECT_EQ(agent_id_baz.value(), 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));
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_bar, 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_TRUE(agent_id_baz);
|
||||
EXPECT_EQ(agent_id_baz.value(), 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));
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_bar, coord2));
|
||||
auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_bar, coord2);
|
||||
EXPECT_FALSE(agent_id_baz);
|
||||
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));
|
||||
auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_foo, coord3);
|
||||
EXPECT_FALSE(agent_id_baz);
|
||||
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));
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_bar, coord2));
|
||||
auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_foo, coord3);
|
||||
EXPECT_FALSE(agent_id_baz);
|
||||
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));
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_bar, coord2));
|
||||
auto agent_id_baz = sologrid1d_foo.delete_agent(agent_id_bar, coord3);
|
||||
EXPECT_FALSE(agent_id_baz);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +174,7 @@ TEST(SoloGrid1D, is_location_empty) {
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_bar, 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));
|
||||
}
|
||||
@@ -199,33 +189,29 @@ TEST(SoloGrid1D, move_agent) {
|
||||
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_foo, coord2));
|
||||
auto agent_id_baz = sologrid1d_foo.move_agent(agent_id_foo, coord7);
|
||||
EXPECT_TRUE(agent_id_baz);
|
||||
EXPECT_EQ(agent_id_baz.value(), 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));
|
||||
auto agent_id_baz = sologrid1d_foo.move_agent(agent_id_foo, coord10);
|
||||
EXPECT_FALSE(agent_id_baz);
|
||||
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));
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_bar, 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_TRUE(agent_id_baz);
|
||||
EXPECT_EQ(agent_id_baz.value(), 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));
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_bar, 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_TRUE(agent_id_baz);
|
||||
EXPECT_EQ(agent_id_baz.value(), agent_id_foo);
|
||||
EXPECT_EQ(agent_id_baz, agent_id_foo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,160 +222,176 @@ TEST(SoloGrid1D, get_neighborhood) {
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
auto tval = unordered_set<GridCoord1D>({coord0, coord1, coord9});
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1, coord9
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(coord0, true);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
auto tval = unordered_set<GridCoord1D>({coord0, coord1, coord2});
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1, coord2
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(coord1, true);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, false);
|
||||
|
||||
auto tval = unordered_set<GridCoord1D>({coord0, coord1});
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(coord0, true);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, false);
|
||||
|
||||
auto tval = unordered_set<GridCoord1D>({coord0, coord1, coord2});
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1, coord2
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(coord1, true);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
auto tval = unordered_set<GridCoord1D>({coord1, coord9});
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord1, coord9
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(coord0, false);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
auto tval = unordered_set<GridCoord1D>({coord0, coord2});
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord2
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(coord1, false);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, false);
|
||||
|
||||
auto tval = unordered_set<GridCoord1D>({coord1});
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord1
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(coord0, false);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, false);
|
||||
|
||||
auto tval = unordered_set<GridCoord1D>({coord0, coord2});
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord2
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(coord1, false);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
auto tval = unordered_set<GridCoord1D>({coord0, coord1, coord9});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, true);
|
||||
|
||||
EXPECT_FALSE(rval);
|
||||
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 tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1, coord9
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, true);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
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 tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1, coord2
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, true);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
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 tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, true);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
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 tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord1, coord2
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, true);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
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 tval = unordered_set < GridCoord1D > ({
|
||||
coord1, coord9
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, false);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
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 tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord2
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, false);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, false);
|
||||
sologrid1d_foo.add_agent(agent_id_foo, coord0);
|
||||
|
||||
auto tval = unordered_set<GridCoord1D>({coord1});
|
||||
auto tval = unordered_set < GridCoord1D > ({
|
||||
coord1
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, false);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
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 tval = unordered_set < GridCoord1D > ({
|
||||
coord0, coord2
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_neighborhood(agent_id_foo, false);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -400,17 +402,16 @@ TEST(SoloGrid1D, get_location_by_agent) {
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
|
||||
EXPECT_FALSE(sologrid1d_foo.get_location_by_agent(agent_id_foo));
|
||||
EXPECT_FALSE(sologrid1d_foo.get_location_by_agent(agent_id_bar));
|
||||
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_TRUE(local);
|
||||
EXPECT_EQ(local, coord2);
|
||||
EXPECT_FALSE(sologrid1d_foo.get_location_by_agent(agent_id_bar));
|
||||
EXPECT_THROW(auto loc = sologrid1d_foo.get_location_by_agent(agent_id_bar), AgentNotFound);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,9 +421,7 @@ TEST(SoloGrid1D, get_location_contents) {
|
||||
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
auto agent_list_foo = sologrid1d_foo.get_location_contents(coord10);
|
||||
|
||||
EXPECT_FALSE(agent_list_foo);
|
||||
EXPECT_THROW(auto agent_list_foo = sologrid1d_foo.get_location_contents(coord10), LocationUnavailable);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
@@ -430,35 +429,42 @@ TEST(SoloGrid1D, get_location_contents) {
|
||||
auto agent_list_foo = sologrid1d_foo.get_location_contents(coord1);
|
||||
|
||||
EXPECT_TRUE(agent_list_foo);
|
||||
EXPECT_TRUE(agent_list_foo.value()->empty());
|
||||
EXPECT_TRUE(agent_list_foo->empty());
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_foo, coord1));
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_bar, coord1));
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_baz, 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 tval = set < AgentID > ({
|
||||
agent_id_foo
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_location_contents(coord1);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
{
|
||||
SoloGrid1D sologrid1d_foo(10, true);
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_foo, coord1));
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_bar, coord1));
|
||||
static_cast<void>(sologrid1d_foo.add_agent(agent_id_baz, coord9));
|
||||
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 tval = set < AgentID > ({
|
||||
agent_id_foo
|
||||
});
|
||||
auto rval = sologrid1d_foo.get_location_contents(coord1);
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(tval, *rval.value());
|
||||
EXPECT_EQ(tval, *rval);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -23,6 +23,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -37,7 +38,8 @@
|
||||
using namespace kami;
|
||||
using namespace std;
|
||||
|
||||
class TestAgent : public StagedAgent {
|
||||
class TestAgent
|
||||
: public StagedAgent {
|
||||
public:
|
||||
AgentID step(shared_ptr<Model> model) override {
|
||||
return get_agent_id();
|
||||
@@ -48,18 +50,24 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class TestModel : public Model {
|
||||
class TestModel
|
||||
: public Model {
|
||||
public:
|
||||
optional<shared_ptr<vector<AgentID>>> step() {
|
||||
return _sched->step(shared_from_this());
|
||||
shared_ptr<vector<AgentID>> retval;
|
||||
|
||||
shared_ptr<Model> step() override {
|
||||
retval = _sched->step(shared_from_this());
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
optional<shared_ptr<vector<AgentID>>> step(shared_ptr<vector<AgentID>> agent_list) {
|
||||
return _sched->step(shared_from_this(), move(agent_list));
|
||||
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 {
|
||||
class StagedSchedulerTest
|
||||
: public ::testing::Test {
|
||||
protected:
|
||||
shared_ptr<TestModel> mod = nullptr;
|
||||
|
||||
@@ -89,36 +97,45 @@ TEST(StagedScheduler, DefaultConstructor) {
|
||||
}
|
||||
|
||||
TEST_F(StagedSchedulerTest, step_interface1) {
|
||||
auto tval = mod->get_population().value()->get_agent_list();
|
||||
auto rval = mod->step();
|
||||
auto tval = mod->get_population()->get_agent_list();
|
||||
auto aval = mod->get_population()->get_agent_list();
|
||||
mod->step(std::move(aval));
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(rval.value()->size(), 10);
|
||||
EXPECT_EQ(*rval.value(), *tval);
|
||||
auto rval = mod->retval;
|
||||
|
||||
EXPECT_EQ(rval->size(), 10);
|
||||
EXPECT_EQ(*rval, *tval);
|
||||
}
|
||||
|
||||
TEST_F(StagedSchedulerTest, step_interface2) {
|
||||
auto tval = mod->get_population().value()->get_agent_list();
|
||||
auto rval = mod->step(tval);
|
||||
auto tval = mod->get_population()->get_agent_list();
|
||||
auto aval = mod->get_population()->get_agent_list();
|
||||
mod->step(std::move(aval));
|
||||
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(rval.value()->size(), 10);
|
||||
EXPECT_EQ(*rval.value(), *tval);
|
||||
auto rval = mod->retval;
|
||||
|
||||
EXPECT_EQ(rval->size(), 10);
|
||||
EXPECT_EQ(*rval, *tval);
|
||||
}
|
||||
|
||||
TEST_F(StagedSchedulerTest, step_10000) {
|
||||
auto tval = mod->get_population().value()->get_agent_list();
|
||||
|
||||
// Do it a lot...
|
||||
for (auto i = 0; i < 10000; i++) {
|
||||
auto rval = mod->step();
|
||||
EXPECT_TRUE(rval);
|
||||
EXPECT_EQ(rval.value()->size(), 10);
|
||||
EXPECT_EQ(*rval.value(), *tval);
|
||||
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) {
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -28,12 +28,14 @@
|
||||
#include <kami/agent.h>
|
||||
#include <kami/model.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace kami;
|
||||
using namespace std;
|
||||
|
||||
class TestStagedAgent : public StagedAgent {
|
||||
class TestAgent
|
||||
: public StagedAgent {
|
||||
public:
|
||||
AgentID advance(shared_ptr<Model> model) override {
|
||||
return get_agent_id();
|
||||
@@ -44,60 +46,63 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class TestModel : public Model {
|
||||
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) {
|
||||
const TestStagedAgent agent_foo;
|
||||
const TestStagedAgent agent_bar;
|
||||
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(StagedAgent, get_agent_id) {
|
||||
const TestStagedAgent agent_foo;
|
||||
const TestStagedAgent 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(StagedAgent, advance) {
|
||||
TestStagedAgent agent_foo;
|
||||
TestStagedAgent agent_bar;
|
||||
auto model_world = make_shared<TestModel>();
|
||||
|
||||
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(StagedAgent, step) {
|
||||
TestStagedAgent agent_foo;
|
||||
TestStagedAgent agent_bar;
|
||||
auto model_world = make_shared<TestModel>();
|
||||
|
||||
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(StagedAgent, Equality) {
|
||||
const TestStagedAgent agent_foo;
|
||||
const TestStagedAgent agent_bar;
|
||||
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(StagedAgent, Inequality) {
|
||||
const TestStagedAgent agent_foo;
|
||||
const TestStagedAgent agent_bar;
|
||||
|
||||
TEST_F(StagedAgentTest, inequality) {
|
||||
EXPECT_TRUE(agent_foo != agent_bar);
|
||||
EXPECT_FALSE(agent_bar != agent_bar);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int main(
|
||||
int argc,
|
||||
char** argv
|
||||
) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user