Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion include/respond/model.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// Created Date: 2026-02-05 //
// Author: Matthew Carroll //
// ----- //
// Last Modified: 2026-02-09 //
// Last Modified: 2026-02-12 //
// Modified By: Matthew Carroll //
// ----- //
// Copyright (c) 2026 Syndemics Lab at Boston Medical Center //
Expand Down Expand Up @@ -42,6 +42,8 @@ class Model {
// History, but let that be the History's responsibility
virtual std::map<std::string, History> GetHistories() const = 0;

virtual void CreateDefaultHistories() = 0;

virtual void SetHistories(const std::map<std::string, History> &h) = 0;
// getter for model name
virtual std::string GetModelName() const = 0;
Expand Down
31 changes: 4 additions & 27 deletions include/respond/simulation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// Created Date: 2026-02-05 //
// Author: Matthew Carroll //
// ----- //
// Last Modified: 2026-02-10 //
// Last Modified: 2026-02-12 //
// Modified By: Matthew Carroll //
// ----- //
// Copyright (c) 2026 Syndemics Lab at Boston Medical Center //
Expand Down Expand Up @@ -33,11 +33,9 @@ class Simulation {
/// @brief The core function to run the simulation. Runs all models
/// associated with the simulation.
/// @param duration The number of steps to take for each model.
void Run(const int &duration) {
for (int i = 0; i < duration; ++i) {
for (const auto &model : _models) {
model->RunTransitions();
}
void Run() {
for (const auto &model : _models) {
model->RunTransitions();
}
}

Expand Down Expand Up @@ -89,27 +87,6 @@ class Simulation {
return ret;
}

/// @brief The default histories are:
/// 1. State
/// 2. Total Overdoses
/// 3. Fatal Overdoses
/// 4. Intervention Admissions
/// 5. Background Mortality
/// @return A vector of the default history objects.
static std::map<std::string, History>
CreateDefaultHistories(const std::string &log_name) {
std::vector<std::string> names = {
"state", "total_overdose", "fatal_overdose",
"intervention_admission", "background_death"};

std::map<std::string, History> ret;
for (const auto &n : names) {
History h(n, log_name);
ret[n] = h;
}
return ret;
}

std::string GetLogName() const { return _log_name; }

// Copying object
Expand Down
10 changes: 9 additions & 1 deletion src/background.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// Created Date: 2026-02-05 //
// Author: Matthew Carroll //
// ----- //
// Last Modified: 2026-02-05 //
// Last Modified: 2026-02-12 //
// Modified By: Matthew Carroll //
// ----- //
// Copyright (c) 2026 Syndemics Lab at Boston Medical Center //
Expand All @@ -28,7 +28,15 @@ BackgroundDeath::Execute(const Eigen::VectorXd &state,
if (h.find("background_death") != h.end()) {
h["background_death"].AddState(deaths);
}
if (!(state.array() >= deaths.array()).all()) {
std::runtime_error(
"The state is not larger than the estimated background deaths!");
}
auto new_state = state - deaths; // remove deaths from state

if (h.find("background_death") != h.end()) {
h["state"].AddState(new_state);
}
return new_state;
}

Expand Down
43 changes: 39 additions & 4 deletions src/internals/markov.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// Created Date: 2026-02-05 //
// Author: Matthew Carroll //
// ----- //
// Last Modified: 2026-02-10 //
// Last Modified: 2026-02-12 //
// Modified By: Matthew Carroll //
// ----- //
// Copyright (c) 2026 Syndemics Lab at Boston Medical Center //
Expand Down Expand Up @@ -78,15 +78,33 @@ class Markov : public virtual Model {
return _transition_vector;
}

/// @brief The default histories are:
/// 1. State
/// 2. Total Overdoses
/// 3. Fatal Overdoses
/// 4. Intervention Admissions
/// 5. Background Mortality
/// @return A vector of the default history objects.
void CreateDefaultHistories() override {
std::vector<std::string> names = {
"state", "total_overdose", "fatal_overdose",
"intervention_admission", "background_death"};

std::map<std::string, History> ret;
for (const auto &n : names) {
History h(n, GetLogName());
ret[n] = h;
}
SetHistories(ret);
}

// manipulate the state vector
void RunTransitions() override {
SetupHistory();
auto histories = GetHistories();
for (const auto &t : _transition_vector) {
SetState(t->Execute(GetState(), histories));
}
if (histories.find("state") != histories.end()) {
histories["state"].AddState(GetState());
}
SetHistories(histories);
}
// assume ownership of the Transition
Expand Down Expand Up @@ -123,6 +141,23 @@ class Markov : public virtual Model {
std::string _name;
std::string _log_name;
std::map<std::string, History> _histories;

void SetupHistory() {
auto histories = GetHistories();
if (histories.empty()) {
CreateDefaultHistories();
histories = GetHistories();
}
histories["state"].AddState(GetState());
auto size = GetState().size();

histories["intervention_admission"].AddState(
Eigen::VectorXd::Zero(size));
histories["total_overdose"].AddState(Eigen::VectorXd::Zero(size));
histories["fatal_overdose"].AddState(Eigen::VectorXd::Zero(size));
histories["background_death"].AddState(Eigen::VectorXd::Zero(size));
SetHistories(histories);
}
};
} // namespace respond

Expand Down
8 changes: 5 additions & 3 deletions src/migration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// Created Date: 2026-02-05 //
// Author: Matthew Carroll //
// ----- //
// Last Modified: 2026-02-05 //
// Last Modified: 2026-02-12 //
// Modified By: Matthew Carroll //
// ----- //
// Copyright (c) 2026 Syndemics Lab at Boston Medical Center //
Expand All @@ -26,8 +26,10 @@ Eigen::VectorXd Migration::Execute(const Eigen::VectorXd &state,
throw std::runtime_error("Unable to add Migration Transition Vector to "
"State Vector, mismatched sizes.");
}
auto new_state = state + GetTransitionMatrices()[0];
return new_state;
auto subtracted = state + GetTransitionMatrices()[0];
auto zero_stop = subtracted.array().max(
Eigen::VectorXd::Zero(subtracted.size()).array());
return zero_stop;
}

std::unique_ptr<Transition> Migration::Create(const std::string &name,
Expand Down
6 changes: 5 additions & 1 deletion src/overdose.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// Created Date: 2026-02-05 //
// Author: Matthew Carroll //
// ----- //
// Last Modified: 2026-02-05 //
// Last Modified: 2026-02-12 //
// Modified By: Matthew Carroll //
// ----- //
// Copyright (c) 2026 Syndemics Lab at Boston Medical Center //
Expand Down Expand Up @@ -42,6 +42,10 @@ Eigen::VectorXd Overdose::Execute(const Eigen::VectorXd &state,
if (h.find("fatal_overdose") != h.end()) {
h["fatal_overdose"].AddState(fods);
}
if (!(state.array() >= fods.array()).all()) {
std::runtime_error(
"The state is not larger than the estimated fatal overdoses!");
}
auto new_state = state - fods; // remove fods from state
return new_state;
}
Expand Down
27 changes: 22 additions & 5 deletions tests/integration/respond_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// Created Date: 2026-02-06 //
// Author: Matthew Carroll //
// ----- //
// Last Modified: 2026-02-10 //
// Last Modified: 2026-02-13 //
// Modified By: Matthew Carroll //
// ----- //
// Copyright (c) 2026 Syndemics Lab at Boston Medical Center //
Expand Down Expand Up @@ -86,7 +86,7 @@ TEST_F(RespondTest, RunTransitionsInModel) {
}

TEST_F(RespondTest, RunSimulationOneStep) {
markov->SetHistories(Simulation::CreateDefaultHistories("test_logger"));
markov->CreateDefaultHistories();

markov->SetState(init_state);

Expand All @@ -108,7 +108,7 @@ TEST_F(RespondTest, RunSimulationOneStep) {

Simulation sim("test_logger");
sim.AddModel(markov);
sim.Run(1);
sim.Run();

auto histories = sim.GetModelHistories();
ASSERT_EQ(histories.size(), 1);
Expand All @@ -119,11 +119,28 @@ TEST_F(RespondTest, RunSimulationOneStep) {
}

auto state_history = mm_histories.at("state");
ASSERT_EQ(state_history.size(), 1);
// 2 because it carries the initial state and 1 step
ASSERT_EQ(state_history.size(), 2);

Eigen::Vector3d final_state;
ASSERT_TRUE(state_history[0].isApprox(init_state));
final_state << 0.76715528791564891, 0.72320370216816077, 1.037712429738102;
ASSERT_TRUE(state_history[0].isApprox(final_state));
ASSERT_TRUE(state_history[1].isApprox(final_state));
}

TEST_F(RespondTest, CreateDefaultHistories) {
std::vector<std::string> expected = {
"state", "total_overdose", "fatal_overdose", "intervention_admission",
"background_death"};

std::sort(expected.begin(), expected.end());

markov->CreateDefaultHistories();
std::vector<std::string> results;
for (const auto &kv : markov->GetHistories()) {
results.push_back(kv.first);
}
ASSERT_EQ(results, expected);
}

} // namespace testing
Expand Down
3 changes: 2 additions & 1 deletion tests/mocks/model_mock.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// Created Date: 2025-08-01 //
// Author: Matthew Carroll //
// ----- //
// Last Modified: 2026-02-09 //
// Last Modified: 2026-02-12 //
// Modified By: Matthew Carroll //
// ----- //
// Copyright (c) 2025-2026 Syndemics Lab at Boston Medical Center //
Expand Down Expand Up @@ -45,6 +45,7 @@ class MockModel : public virtual Model {
MOCK_METHOD(std::string, GetModelName, (), (const, override));
MOCK_METHOD(std::string, GetLogName, (), (const, override));
MOCK_METHOD((std::unique_ptr<Model>), clone, (), (const, override));
MOCK_METHOD(void, CreateDefaultHistories, (), (override));
};
} // namespace testing
} // namespace respond
Expand Down
17 changes: 1 addition & 16 deletions tests/unit/simulation_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// Created Date: 2026-02-09 //
// Author: Matthew Carroll //
// ----- //
// Last Modified: 2026-02-10 //
// Last Modified: 2026-02-12 //
// Modified By: Matthew Carroll //
// ----- //
// Copyright (c) 2026 Syndemics Lab at Boston Medical Center //
Expand Down Expand Up @@ -133,20 +133,5 @@ TEST_F(SimulationTest, GetHistoryNames) {
ASSERT_EQ(s.GetModelHistoryNames(), expected);
}

TEST_F(SimulationTest, CreateDefaultHistories) {
std::vector<std::string> expected = {
"state", "total_overdose", "fatal_overdose", "intervention_admission",
"background_death"};

std::sort(expected.begin(), expected.end());

Simulation s;
auto defaults = s.CreateDefaultHistories("test_logger");
std::vector<std::string> results;
for (const auto &kv : defaults) {
results.push_back(kv.first);
}
ASSERT_EQ(results, expected);
}
} // namespace testing
} // namespace respond