RESPOND 2.5.1
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-07-13 //
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 <respond/constants.hpp>
16#include <respond/logging.hpp>
17
18#include <algorithm>
19#include <map>
20#include <utility>
21#include <vector>
22
23#include <Eigen/Dense>
24
25namespace respond {
26
29enum class HistoryMode : int {
30 kSnapshot = 0, // Snapshot of state at each timestep
31 kAccumulated = 1, // Accumulated contributions over timesteps
32 kCount = 2 // Enum Counter
33};
34
39inline HistoryMode GetDefaultHistoryMode(const std::string &name) {
40 if (name == "intervention_admission" || name == "total_overdose" ||
41 name == "fatal_overdose" || name == "background_death") {
42 return HistoryMode::kAccumulated;
43 }
44 return HistoryMode::kSnapshot;
45}
46
51class History {
52public:
54 //
55 // Rule of Five: Copy and Move Semantics
56 //
58
61 History() : History("state") {}
62
66 History(const std::string &name)
67 : History(name, GetDefaultHistoryMode(name)) {}
68
72 History(const std::string &name, const HistoryMode &mode)
73 : History(name, mode, RESPOND_DEFAULT_LOG, RESPOND_DEFAULT_LOG_FILE) {}
74
79 History(const std::string &name, const HistoryMode &mode,
80 const std::string &log_name)
81 : History(name, mode, log_name, RESPOND_DEFAULT_LOG_FILE) {}
82
86 History(const std::string &name, const std::string &log_name)
87 : History(name, GetDefaultHistoryMode(name), log_name,
88 RESPOND_DEFAULT_LOG_FILE) {}
89
95 History(const std::string &name, const std::string &log_name,
96 const std::string &log_filepath)
97 : History(name, GetDefaultHistoryMode(name), log_name, log_filepath) {}
98
105 History(const std::string &name, const HistoryMode &mode,
106 const std::string &log_name, const std::string &log_filepath)
107 : _name(name), _mode(mode), _log_name(log_name) {
108 CreateFileLogger(log_name, log_filepath);
109 }
110
112 ~History() = default;
113
116 History(const History &other) {
117 _timesteps = other.GetRecordedTimesteps();
118 _states = other.GetRecordedStates();
119 _name = other._name;
120 _log_name = other._log_name;
121 _mode = other._mode;
122 _pending_state = other.GetPendingState();
123 }
124
128 History &operator=(const History &other) {
129 if (this != &other) {
130 _timesteps = other.GetRecordedTimesteps();
131 _states = other.GetRecordedStates();
132 _name = other._name;
133 _log_name = other._log_name;
134 _mode = other._mode;
135 _pending_state = other.GetPendingState();
136 }
137 return *this;
138 }
139
143 History(History &&other) noexcept {
144 _timesteps = std::move(other._timesteps);
145 _states = std::move(other._states);
146 _name = other._name;
147 _log_name = other._log_name;
148 _mode = other._mode;
149 _pending_state = std::move(other._pending_state);
150 }
151
155 History &operator=(History &&other) noexcept {
156 if (this != &other) {
157 _timesteps = std::move(other._timesteps);
158 _states = std::move(other._states);
159 _name = other._name;
160 _log_name = other._log_name;
161 _mode = other._mode;
162 _pending_state = std::move(other._pending_state);
163 }
164 return *this;
165 }
166
168 //
169 // History Methods: State Vector Management
170 //
172
179 void AddState(const Eigen::Ref<const Eigen::VectorXd> &state,
180 int timestep = -1) {
181 if (timestep < 0) {
182 timestep = GetNextTimestep();
183 }
184
185 const auto existing =
186 std::find(_timesteps.begin(), _timesteps.end(), timestep);
187 if (existing != _timesteps.end()) {
188 const auto index =
189 static_cast<size_t>(existing - _timesteps.begin());
190 _states[index] = state;
191 return;
192 }
193
194 _timesteps.push_back(timestep);
195 _states.push_back(state);
196 }
197
200 void AccumulateState(const Eigen::Ref<const Eigen::VectorXd> &state) {
201 if (_mode != HistoryMode::kAccumulated) {
202 LogWarning(_log_name, "AccumulateState called on non-accumulated "
203 "history, adding state instead: " +
204 _name);
205 AddState(state);
206 return;
207 }
208
209 if (_pending_state.size() == 0) {
210 _pending_state = state;
211 return;
212 }
213 _pending_state += state;
214 }
215
219 void FlushPendingState(int timestep, Eigen::Index state_size) {
220 if (_mode != HistoryMode::kAccumulated) {
221 LogInfo(_log_name,
222 "FlushPendingState called on non-accumulated history, "
223 "no pending state to flush: " +
224 _name);
225 return;
226 }
227
228 Eigen::VectorXd value;
229 if (_pending_state.size() > 0) {
230 value = _pending_state;
231 } else {
232 LogInfo(_log_name,
233 "FlushPendingState called with no pending state, "
234 "recording zero vector: " +
235 _name);
236 value = Eigen::VectorXd::Zero(state_size);
237 }
238
239 AddState(value, timestep);
240 _pending_state.resize(0);
241 }
242
244 void Clear() {
245 _timesteps.clear();
246 _states.clear();
247 _pending_state.resize(0);
248 }
249
252 bool HasPendingState() const { return _pending_state.size() > 0; }
253
255 //
256 // Getters and Setters for History Vectors
257 //
259
262 std::map<int, Eigen::VectorXd> GetStateMap() const {
263 std::map<int, Eigen::VectorXd> state_map;
264 for (size_t index = 0; index < _timesteps.size(); ++index) {
265 state_map[_timesteps[index]] = _states[index];
266 }
267 return state_map;
268 }
269
272 const std::vector<int> &GetRecordedTimesteps() const { return _timesteps; }
273
276 const std::vector<Eigen::VectorXd> &GetRecordedStates() const {
277 return _states;
278 }
279
282 HistoryMode GetHistoryMode() const { return _mode; }
283
286 Eigen::VectorXd GetPendingState() const { return _pending_state; }
287
291 if (_timesteps.empty()) {
292 LogWarning(_log_name,
293 "GetLatestRecordedTimestep called on empty history: " +
294 _name);
295 return -1;
296 }
297 return _timesteps.back();
298 }
299
302 std::string GetName() const { return _name; }
303
308 std::vector<Eigen::VectorXd> GetStateAsVector() const {
309 std::vector<Eigen::VectorXd> ret;
310 if (_states.empty()) {
311 LogWarning(_log_name,
312 "GetStateAsVector called on empty history: " + _name);
313 return {};
314 }
315 int default_size = _states.front().size();
316 int tstep = 0;
317 for (size_t index = 0; index < _timesteps.size(); ++index) {
318 const int recorded_timestep = _timesteps[index];
319 const auto &recorded_state = _states[index];
320 while (recorded_timestep > tstep) {
321 ret.push_back(GetZeroVector(default_size));
322 tstep++;
323 }
324 ret.push_back(recorded_state);
325 tstep++;
326 }
327 return ret;
328 }
329
331 //
332 // Operator Comparisons and Stream Output
333 //
335
339 bool operator==(const History &other) const {
340 return _name == other._name && _log_name == other._log_name &&
341 _mode == other._mode && GetStateMap() == other.GetStateMap() &&
342 GetPendingState().isApprox(other.GetPendingState());
343 }
344
348 bool operator!=(const History &other) const { return !(*this == other); }
349
350 friend std::ostream &operator<<(std::ostream &os, const History &history) {
351 os << "History(name=" << history._name << ", timesteps=[";
352 for (const auto &t : history._timesteps) {
353 os << t << ",";
354 }
355 os << "], pending_state=" << history._pending_state.transpose() << ")";
356 return os;
357 }
358
359private:
361 std::string _log_name;
363 std::string _name;
365 HistoryMode _mode;
367 std::vector<int> _timesteps;
369 std::vector<Eigen::VectorXd> _states;
371 Eigen::VectorXd _pending_state;
372
376 int GetNextTimestep() {
377 if (_timesteps.empty()) {
378 return 0;
379 }
380 return _timesteps.back() + 1;
381 }
382
386 Eigen::VectorXd GetZeroVector(const int &size) const {
387 return Eigen::VectorXd::Zero(size);
388 }
389};
390} // namespace respond
391
392#endif // RESPOND_HISTORY_HPP_
Tracks and manages state vector history over time. History records state snapshots at discrete timest...
Definition: history.hpp:51
History(const std::string &name, const std::string &log_name)
Constructs a history with a specified name and logger.
Definition: history.hpp:86
std::string GetName() const
Retrieves the identifier name of this history.
Definition: history.hpp:302
History & operator=(const History &other)
Copy assignment operator implementing the Rule of Five.
Definition: history.hpp:128
Eigen::VectorXd GetPendingState() const
Retrieves the pending accumulated state.
Definition: history.hpp:286
const std::vector< Eigen::VectorXd > & GetRecordedStates() const
Retrieves the recorded state vectors without densifying gaps.
Definition: history.hpp:276
History(const std::string &name, const HistoryMode &mode)
Constructs a history with a specified name and mode.
Definition: history.hpp:72
History(const std::string &name, const std::string &log_name, const std::string &log_filepath)
Constructs a history with a specified name, logger, and log file path.
Definition: history.hpp:95
History & operator=(History &&other) noexcept
Move assignment operator implementing the Rule of Five.
Definition: history.hpp:155
~History()=default
Destructor (default).
History(const std::string &name, const HistoryMode &mode, const std::string &log_name)
Constructs a history with a specified name, mode, and logger.
Definition: history.hpp:79
History()
Default constructor initializing a history with the default name "state" and default mode based on th...
Definition: history.hpp:61
void Clear()
Clears all recorded state history.
Definition: history.hpp:244
History(const std::string &name)
Constructs a history with a specified name, using the default mode based on that name.
Definition: history.hpp:66
History(const History &other)
Copy constructor implementing the Rule of Five. Creates an independent copy of the history state and ...
Definition: history.hpp:116
int GetLatestRecordedTimestep() const
Retrieves the latest recorded timestep.
Definition: history.hpp:290
std::map< int, Eigen::VectorXd > GetStateMap() const
Retrieves the complete state map (timestep -> state vector).
Definition: history.hpp:262
History(const std::string &name, const HistoryMode &mode, const std::string &log_name, const std::string &log_filepath)
Constructs a history with a specified name, mode, logger, and log file path.
Definition: history.hpp:105
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:308
bool operator!=(const History &other) const
Inequality comparison operator.
Definition: history.hpp:348
History(History &&other) noexcept
Move constructor implementing the Rule of Five.
Definition: history.hpp:143
void FlushPendingState(int timestep, Eigen::Index state_size)
Flushes pending accumulated state into a recorded timestep.
Definition: history.hpp:219
HistoryMode GetHistoryMode() const
Retrieves the configured history recording mode.
Definition: history.hpp:282
bool HasPendingState() const
Indicates whether an accumulated history has pending state.
Definition: history.hpp:252
void AccumulateState(const Eigen::Ref< const Eigen::VectorXd > &state)
Adds a contribution to an accumulated history.
Definition: history.hpp:200
void AddState(const Eigen::Ref< const Eigen::VectorXd > &state, int timestep=-1)
Records a state vector at a specific or automatic timestep.
Definition: history.hpp:179
bool operator==(const History &other) const
Equality comparison operator.
Definition: history.hpp:339
const std::vector< int > & GetRecordedTimesteps() const
Retrieves the recorded timesteps without densifying gaps.
Definition: history.hpp:272