78 Commits
0.6.0 ... main

Author SHA1 Message Date
James P. Howard, II
568a5f6f5d Merge branch 'release/0.7.2' 2023-01-22 21:45:26 -05:00
James P. Howard, II
ffe25478c1 chore(version): Bump to version 0.7.2 2023-01-22 21:45:20 -05:00
James P. Howard, II
f10bc39cce docs(sphinx): Fix version numbers in Python requirements 2023-01-22 21:41:02 -05:00
James P. Howard, II
5a0f76f7fa docs(doxygen): Automate version numbering 2023-01-22 21:34:27 -05:00
James P. Howard, II
da5347b102 docs(doxygen): Cleanup doxygen config 2023-01-22 21:26:12 -05:00
James P. Howard, II
dcbfddad13 chore(docs): Update changelog for continuous domains 2023-01-22 21:17:36 -05:00
James P. Howard, II
7e3052bac0 chore(docs): Intro note in changelog 2023-01-22 21:14:54 -05:00
James P. Howard, II
14b244586c Merge branch 'release/0.7.1' into develop 2023-01-22 21:04:50 -05:00
James P. Howard, II
eacac535ab Merge branch 'release/0.7.1' 2023-01-22 21:04:42 -05:00
James P. Howard, II
cdce9feb51 chore(version): Bump to version 0.7.1 2023-01-22 21:04:37 -05:00
James P. Howard, II
4602d87024 docs(pdf): Remove latex build 2023-01-22 21:03:21 -05:00
James P. Howard, II
8f658dc5ca Merge branch 'release/0.7.0' into develop 2023-01-22 20:49:44 -05:00
James P. Howard, II
597bb298eb Merge branch 'release/0.7.0' 2023-01-22 20:49:34 -05:00
James P. Howard, II
60018c2bce chore(version): Bump to version 0.7.0 2023-01-22 20:49:28 -05:00
James P. Howard, II
74905d04a5 style(tests): automatic reformatting 2023-01-22 20:47:29 -05:00
James P. Howard, II
adec433bce style(source): automatic reformatting 2023-01-22 20:46:34 -05:00
James P. Howard, II
431a4ac8cf style(includes): automatic reformatting 2023-01-22 20:45:46 -05:00
James P. Howard, II
e56c257707 style(examples): automatic reformatting 2023-01-22 20:44:55 -05:00
James P. Howard, II
8cf2193f7c docs(changelog): Updates to changelog for tutorial 2023-01-22 20:42:44 -05:00
James P. Howard, II
dacb7d38de Merge branch 'feature/tutorial' into develop 2023-01-22 20:37:37 -05:00
James P. Howard, II
62e80ed547 docs(tutorial): Add the long-awaited tutorial 2023-01-22 20:33:54 -05:00
James P. Howard, II
e02b339055 docs(minimal): Create a minimal example
This is even more elementary than the starter model and is for use in the tutorial
2023-01-22 19:55:37 -05:00
James P. Howard, II
3af8d8b8e0 chore(docs): We use main, not master 2023-01-21 21:56:18 -05:00
James P. Howard, II
2e212ad5c6 chore(admin): Updated copyrights for 2023 2023-01-21 21:54:07 -05:00
James P. Howard, II
426f1b740d docs(examples): Additional updates for documentation 2023-01-21 21:52:54 -05:00
James P. Howard, II
0d75e3b691 docs(examples): Added documentation for each example 2023-01-21 21:50:01 -05:00
James P. Howard, II
9d27ec927d docs: About ABMs section
Created new about ABMs section using ChatGPT
2023-01-21 20:56:50 -05:00
James P. Howard, II
91fdb5fc68 docs(constants): Fix documentation for constants 2023-01-20 10:56:36 -05:00
James P. Howard, II
c08f9509e5 docs(gridx): Remove documentation for nonexistent option 2023-01-20 10:50:51 -05:00
James P. Howard, II
2ead0b30e0 docs(reporters): Add documentation for reporters
Add documentation for reporters, related classes ReporterModel and ReporterAgent, and the associated calls in the schedulers
2023-01-20 10:49:40 -05:00
James P. Howard, II
3e88c87644 docs(exceptions): Document exceptions 2023-01-20 10:48:42 -05:00
James P. Howard, II
003c3e1be0 chore(doxygen): Remove unused options to suppress warnings 2023-01-20 10:37:52 -05:00
James P. Howard, II
f333f96bca fix(hexgrid): Removing hexgrid
Hexgrids are not currently functional, so it is being removed
2023-01-19 08:46:44 -05:00
James P. Howard, II
5d58df48b2 build(cmake): automate selection of sources files
Use globbing to get the sources files for each example and the library
2023-01-18 23:35:21 -05:00
James P. Howard, II
022f9e31aa Added update to changelog 2022-09-21 11:46:19 -04:00
James P. Howard, II
ad9dbf3eb2 Updates to citations 2022-09-21 11:45:30 -04:00
James P. Howard, II
0f1f1ad41a Generalize coordinates to location 2022-09-16 16:51:41 -04:00
James P. Howard, II
f0295e9891 Rename exceptions to follow NounAdjective convention 2022-09-16 16:49:35 -04:00
James P. Howard, II
8f7b6b65c2 Unit test revisions for exception-handling 2022-09-16 16:47:17 -04:00
James P. Howard, II
80ac9571a7 Renamed kami::exception to kami::error to avoid conflicts with std::expection 2022-09-16 16:32:13 -04:00
James P. Howard, II
7a7db768ed Remove code redundancies 2022-09-10 23:07:38 -04:00
James P. Howard, II
9eb47c8656 Move to exception-based error handling 2022-09-09 22:12:42 -04:00
James P. Howard, II
2aeef72f19 Grid distance API changes 2022-09-07 21:46:41 -04:00
James P. Howard, II
552d8fc725 Updated Reporter documentation 2022-09-07 21:46:01 -04:00
James P. Howard, II
8050ce9b8f Remove unnecessary includes from examples 2022-09-03 22:05:33 -04:00
James P. Howard, II
317509ce0b Rename x/y accessors 2022-09-03 22:01:15 -04:00
James P. Howard, II
e1028a377d Grid and domain cleanup and documentation updates 2022-09-03 21:56:32 -04:00
James P. Howard, II
860e367785 Correction to Jenny's Constant 2022-09-03 21:54:31 -04:00
James P. Howard, II
445e10252e Separate all method declarations from definitions 2022-09-03 21:54:31 -04:00
James P. Howard, II
a08b316b96 New step documentation 2022-09-01 11:39:29 -04:00
James P. Howard, II
893c8da8e2 Refactor get_neighborhood 2022-09-01 11:38:51 -04:00
James P. Howard, II
10cb3cf3f3 Added env=Debug to CodeQL 2022-08-29 22:15:28 -04:00
James P. Howard, II
3ed40f4cdb Rework CodeQL build steps based on build-develop 2022-08-29 22:10:43 -04:00
James P. Howard, II
003e7a4d12 Add develop to CodeQL 2022-08-29 22:07:21 -04:00
James P. Howard, II
6751951e84 Add CodeQL analysis 2022-08-29 21:55:56 -04:00
James P. Howard, II
7def091083 Remove the hash method from the coordinate types from the documentation 2022-08-29 15:20:06 -04:00
James P. Howard, II
a872c89b78 Added CFF file 2022-08-29 11:41:40 -04:00
James P. Howard, II
b573a3f676 Add package DOI 2022-08-29 11:06:44 -04:00
James P. Howard, II
d2ccdbea73 Make CTidy happy 2022-08-28 20:14:34 -04:00
James P. Howard, II
b9b1cb12e6 Let's start a Position for generic coords 2022-08-28 18:07:29 -04:00
James P. Howard, II
948f744414 Starting hexgrids 2022-08-28 18:06:25 -04:00
James P. Howard, II
06355bdf67 Updates to the testing code 2022-08-28 17:43:36 -04:00
James P. Howard, II
a215ca7320 Reporter agent test code 2022-08-28 16:56:48 -04:00
James P. Howard, II
097024a6b8 Reworked the agent and staged agent tests 2022-08-28 16:41:47 -04:00
James P. Howard, II
a0c2a7869c Reporter testing 2022-08-28 15:41:22 -04:00
James P. Howard, II
a45b9bf22d This file is gone 2022-08-26 23:48:25 -04:00
James P. Howard, II
7849a49355 Documentation updates: changelog.rst and todo.rst 2022-08-26 23:42:04 -04:00
James P. Howard, II
f86dbd4c24 Implementation of the Bank Reserves Model, including some reworking of everything else necessary, also, a partial unit test 2022-08-26 23:32:23 -04:00
James P. Howard, II
cdc9c56765 Let's add a default step method to the Model class 2022-08-24 13:46:12 -04:00
James P. Howard, II
972e83aeeb Create distance measures for the orthogonal grids 2022-08-24 13:45:39 -04:00
James P. Howard, II
922572973d Add some useful constants 2022-08-24 13:44:53 -04:00
James P. Howard, II
8906825235 We're going back to mt19937 as ranlux24 is slow 2022-08-24 13:44:15 -04:00
James P. Howard, II
64dc35c9c4 First blush on the reporter interface. No tests and no working examples yet... 2022-08-21 19:09:15 -04:00
James P. Howard, II
6c34098b53 Add Chebyshev distance constant 2022-08-21 11:41:47 -04:00
James P. Howard, II
bc0740219f Make shared_ptrs that can be unique, unique 2022-08-20 11:45:42 -04:00
James P. Howard, II
11660de09c Merge branch 'feature/reporters' into develop 2022-08-19 21:51:08 -04:00
James P. Howard, II
3b3a7f0bca Merge branch 'release/0.6.0' into develop 2022-08-19 20:58:14 -04:00
James P. Howard, II
a801720aa5 Agent reporter sketch 2022-08-11 22:17:01 -04:00
88 changed files with 5280 additions and 1980 deletions

89
.github/workflows/codeql-analysis.yml vendored Normal file
View 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
View 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

View File

@@ -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}

View File

@@ -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

View File

@@ -3,6 +3,7 @@
[![Documentation status](https://readthedocs.org/projects/kami/badge/?version=main)](https://kami.readthedocs.io/en/main/)
[![Release status](https://img.shields.io/github/release/JHUAPL/kami.svg)](https://github.com/JHUAPL/kami/releases)
![License](https://img.shields.io/github/license/JHUAPL/kami)
[![DOI](https://img.shields.io/badge/DOI-10.5281%2Fzenodo.6975259-success.svg)](https://doi.org/10.5281/zenodo.6975259)
# Kami is Agent-Based Modeling in Modern C++

View File

@@ -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")

View File

@@ -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
View 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::

View File

@@ -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

View File

@@ -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")

View 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::

View 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::

View 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
View 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
View 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::

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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::

View File

@@ -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::

View 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})

View 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

View 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

View 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);
}
}

View 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();
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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})

View 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;
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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.

View File

@@ -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

View File

@@ -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
View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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`

View File

@@ -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

View File

@@ -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

View File

@@ -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
View 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

View File

@@ -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
View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
};

View File

@@ -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

View File

@@ -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"
)

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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));

View File

@@ -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));

View File

@@ -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);

View File

@@ -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
View 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

View File

@@ -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);

View File

@@ -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));

View File

@@ -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));

View File

@@ -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);

View File

@@ -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}
)

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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();
}

View 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();
}

View File

@@ -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
View 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();
}

View 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();
}

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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();
}