RESPOND 2.4.0
Researching Effective Strategies to Prevent Opioid Death
Loading...
Searching...
No Matches
history.hpp
1
2// File: history.hpp //
3// Project: respond //
4// Created Date: 2026-02-05 //
5// Author: Matthew Carroll //
6// ----- //
7// Last Modified: 2026-05-06 //
8// Modified By: Matthew Carroll //
9// ----- //
10// Copyright (c) 2026 Syndemics Lab at Boston Medical Center //
12#ifndef RESPOND_HISTORY_HPP_
13#define RESPOND_HISTORY_HPP_
14
15#include <algorithm>
16#include <map>
17#include <utility>
18#include <vector>
19
20#include <Eigen/Dense>
21
22namespace respond {
23enum class HistoryMode { Snapshot, Accumulated };
24
25inline HistoryMode GetDefaultHistoryMode(const std::string &name) {
26 if (name == "intervention_admission" || name == "total_overdose" ||
27 name == "fatal_overdose" || name == "background_death") {
28 return HistoryMode::Accumulated;
29 }
30 return HistoryMode::Snapshot;
31}
32
37class History {
38public:
43 History(const std::string &name = "state",
44 const std::string &log_name = "console")
45 : History(name, log_name, GetDefaultHistoryMode(name)) {}
46
51 History(const std::string &name, const std::string &log_name,
52 HistoryMode mode)
53 : _log_name(log_name), _name(name), _mode(mode) {}
54
56 ~History() = default;
59 History(const History &other) {
60 _timesteps = other.GetRecordedTimesteps();
61 _states = other.GetRecordedStates();
62 _name = other.GetHistoryName();
63 _log_name = other.GetLogName();
64 _mode = other.GetHistoryMode();
65 _pending_state = other.GetPendingState();
66 }
67
71 History &operator=(const History &other) {
72 if (this != &other) {
73 _timesteps = other.GetRecordedTimesteps();
74 _states = other.GetRecordedStates();
75 _name = other.GetHistoryName();
76 _log_name = other.GetLogName();
77 _mode = other.GetHistoryMode();
78 _pending_state = other.GetPendingState();
79 }
80 return *this;
81 }
82
86 History(History &&other) noexcept {
87 _timesteps = std::move(other._timesteps);
88 _states = std::move(other._states);
89 _name = other.GetHistoryName();
90 _log_name = other.GetLogName();
91 _mode = other.GetHistoryMode();
92 _pending_state = std::move(other._pending_state);
93 }
94
98 History &operator=(History &&other) noexcept {
99 if (this != &other) {
100 _timesteps = std::move(other._timesteps);
101 _states = std::move(other._states);
102 _name = other.GetHistoryName();
103 _log_name = other.GetLogName();
104 _mode = other.GetHistoryMode();
105 _pending_state = std::move(other._pending_state);
106 }
107 return *this;
108 }
109
113 bool operator==(const History &other) const {
114 return GetHistoryName() == other.GetHistoryName() &&
115 GetLogName() == other.GetLogName() &&
116 GetHistoryMode() == other.GetHistoryMode() &&
117 GetStateMap() == other.GetStateMap() &&
118 GetPendingState().isApprox(other.GetPendingState());
119 }
120
124 bool operator!=(const History &other) const { return !(*this == other); }
125
128 std::map<int, Eigen::VectorXd> GetStateMap() const {
129 std::map<int, Eigen::VectorXd> state_map;
130 for (size_t index = 0; index < _timesteps.size(); ++index) {
131 state_map[_timesteps[index]] = _states[index];
132 }
133 return state_map;
134 }
135
138 const std::vector<int> &GetRecordedTimesteps() const { return _timesteps; }
139
142 const std::vector<Eigen::VectorXd> &GetRecordedStates() const {
143 return _states;
144 }
145
148 HistoryMode GetHistoryMode() const { return _mode; }
149
152 bool HasPendingState() const { return _pending_state.size() > 0; }
153
156 Eigen::VectorXd GetPendingState() const { return _pending_state; }
157
161 if (_timesteps.empty()) {
162 return -1;
163 }
164 return _timesteps.back();
165 }
166
169 std::string GetHistoryName() const { return _name; }
170
173 std::string GetLogName() const { return _log_name; }
174
179 std::vector<Eigen::VectorXd> GetStateAsVector() const {
180 std::vector<Eigen::VectorXd> ret;
181 if (_states.empty()) {
182 // warn empty state vector - no states recorded
183 return {};
184 }
185 int default_size = _states.front().size();
186 int tstep = 0;
187 for (size_t index = 0; index < _timesteps.size(); ++index) {
188 const int recorded_timestep = _timesteps[index];
189 const auto &recorded_state = _states[index];
190 if (recorded_timestep > tstep) {
191 // Fill gap: raise error if timestep mapping is invalid
192 }
193 while (recorded_timestep > tstep) {
194 ret.push_back(GetZeroVector(default_size));
195 tstep++;
196 }
197 ret.push_back(recorded_state);
198 tstep++;
199 }
200 return ret;
201 }
202
209 void AddState(const Eigen::VectorXd &state, int timestep = -1) {
210 if (timestep < 0) {
211 timestep = GetNextTimestep();
212 }
213
214 const auto existing =
215 std::find(_timesteps.begin(), _timesteps.end(), timestep);
216 if (existing != _timesteps.end()) {
217 const auto index =
218 static_cast<size_t>(existing - _timesteps.begin());
219 _states[index] = state;
220 return;
221 }
222
223 _timesteps.push_back(timestep);
224 _states.push_back(state);
225 }
226
230 void RecordSnapshot(const Eigen::VectorXd &state, int timestep) {
231 AddState(state, timestep);
232 }
233
236 void AccumulateState(const Eigen::VectorXd &state) {
237 if (_mode != HistoryMode::Accumulated) {
238 AddState(state);
239 return;
240 }
241
242 if (_pending_state.size() == 0) {
243 _pending_state = state;
244 return;
245 }
246 _pending_state += state;
247 }
248
252 void FlushPendingState(int timestep, Eigen::Index state_size) {
253 if (_mode != HistoryMode::Accumulated) {
254 return;
255 }
256
257 Eigen::VectorXd value;
258 if (_pending_state.size() > 0) {
259 value = _pending_state;
260 } else {
261 value = Eigen::VectorXd::Zero(state_size);
262 }
263
264 AddState(value, timestep);
265 _pending_state.resize(0);
266 }
267
269 void Clear() {
270 _timesteps.clear();
271 _states.clear();
272 _pending_state.resize(0);
273 }
274
275private:
277 std::string _log_name;
279 std::string _name;
281 HistoryMode _mode;
283 std::vector<int> _timesteps;
285 std::vector<Eigen::VectorXd> _states;
287 Eigen::VectorXd _pending_state;
288
292 int GetNextTimestep() {
293 if (_timesteps.empty()) {
294 return 0;
295 }
296 return _timesteps.back() + 1;
297 }
298
302 Eigen::VectorXd GetZeroVector(const int &size) const {
303 return Eigen::VectorXd::Zero(size);
304 }
305};
306} // namespace respond
307
308#endif // RESPOND_HISTORY_HPP_
Tracks and manages state vector history over time. History records state snapshots at discrete timest...
Definition: history.hpp:37
std::string GetHistoryName() const
Retrieves the identifier name of this history.
Definition: history.hpp:169
std::string GetLogName() const
Retrieves the logger name for this history.
Definition: history.hpp:173
History & operator=(const History &other)
Copy assignment operator implementing the Rule of Five.
Definition: history.hpp:71
Eigen::VectorXd GetPendingState() const
Retrieves the pending accumulated state.
Definition: history.hpp:156
const std::vector< Eigen::VectorXd > & GetRecordedStates() const
Retrieves the recorded state vectors without densifying gaps.
Definition: history.hpp:142
History & operator=(History &&other) noexcept
Move assignment operator implementing the Rule of Five.
Definition: history.hpp:98
History(const std::string &name="state", const std::string &log_name="console")
Constructs a History tracker.
Definition: history.hpp:43
~History()=default
Destructor (default).
void Clear()
Clears all recorded state history.
Definition: history.hpp:269
History(const History &other)
Copy constructor implementing the Rule of Five. Creates an independent copy of the history state and ...
Definition: history.hpp:59
int GetLatestRecordedTimestep() const
Retrieves the latest recorded timestep.
Definition: history.hpp:160
std::map< int, Eigen::VectorXd > GetStateMap() const
Retrieves the complete state map (timestep -> state vector).
Definition: history.hpp:128
void AddState(const Eigen::VectorXd &state, int timestep=-1)
Records a state vector at a specific or automatic timestep.
Definition: history.hpp:209
std::vector< Eigen::VectorXd > GetStateAsVector() const
Converts the sparse history map to a contiguous vector of states. Gaps in timesteps are filled with z...
Definition: history.hpp:179
bool operator!=(const History &other) const
Inequality comparison operator.
Definition: history.hpp:124
History(History &&other) noexcept
Move constructor implementing the Rule of Five.
Definition: history.hpp:86
void FlushPendingState(int timestep, Eigen::Index state_size)
Flushes pending accumulated state into a recorded timestep.
Definition: history.hpp:252
HistoryMode GetHistoryMode() const
Retrieves the configured history recording mode.
Definition: history.hpp:148
bool HasPendingState() const
Indicates whether an accumulated history has pending state.
Definition: history.hpp:152
void AccumulateState(const Eigen::VectorXd &state)
Adds a contribution to an accumulated history.
Definition: history.hpp:236
void RecordSnapshot(const Eigen::VectorXd &state, int timestep)
Records a snapshot value at a concrete timestep.
Definition: history.hpp:230
History(const std::string &name, const std::string &log_name, HistoryMode mode)
Constructs a History tracker with an explicit recording mode.
Definition: history.hpp:51
bool operator==(const History &other) const
Equality comparison operator.
Definition: history.hpp:113
const std::vector< int > & GetRecordedTimesteps() const
Retrieves the recorded timesteps without densifying gaps.
Definition: history.hpp:138