89 std::vector<T> times_;
90 std::vector<std::vector<typename T::value_type>> states_;
91 std::vector<std::vector<typename T::value_type>> derivatives_;
92 bool computed_{
false};
95 void set_data(
const std::vector<T>& times,
const std::vector<std::vector<typename T::value_type>>& states) {
99 compute_derivatives();
102 std::vector<typename T::value_type> interpolate(T t) {
104 throw std::runtime_error(
"Spline not computed");
107 if (times_.empty()) {
108 throw std::runtime_error(
"No data for interpolation");
112 auto it = std::lower_bound(times_.begin(), times_.end(), t);
114 if (it == times_.begin()) {
118 if (it == times_.end()) {
119 return states_.back();
122 size_t idx = std::distance(times_.begin(), it) - 1;
123 T h = times_[idx + 1] - times_[idx];
124 T a = (times_[idx + 1] - t) / h;
125 T b = (t - times_[idx]) / h;
127 std::vector<typename T::value_type> result(states_[idx].size());
129 for (
size_t i = 0; i < result.size(); ++i) {
130 result[i] = a * states_[idx][i] + b * states_[idx + 1][i] +
131 ((a * a * a - a) * derivatives_[idx][i] + (b * b * b - b) * derivatives_[idx + 1][i]) * (h * h) / 6.0;
138 void compute_derivatives() {
139 size_t n = times_.size();
142 derivatives_.resize(n);
143 for (
size_t i = 0; i < n; ++i) {
144 derivatives_[i].resize(states_[i].size());
149 for (
size_t j = 0; j < states_[0].size(); ++j) {
150 derivatives_[0][j] = derivatives_[1][j] = 0.0;
157 std::vector<typename T::value_type> a(n), b(n), c(n);
159 for (
size_t j = 0; j < states_[0].size(); ++j) {
160 std::vector<typename T::value_type> d(n);
163 for (
size_t i = 1; i < n - 1; ++i) {
164 T h1 = times_[i] - times_[i - 1];
165 T h2 = times_[i + 1] - times_[i];
168 b[i] = 2.0 * (h1 + h2);
170 d[i] = 6.0 * ((states_[i + 1][j] - states_[i][j]) / h2 - (states_[i][j] - states_[i - 1][j]) / h1);
174 b[0] = b[n - 1] = 1.0;
175 c[0] = a[n - 1] = 0.0;
176 d[0] = d[n - 1] = 0.0;
179 solve_tridiagonal(a, b, c, d);
181 for (
size_t i = 0; i < n; ++i) {
182 derivatives_[i][j] = d[i];
189 void solve_tridiagonal(std::vector<typename T::value_type>& a,
190 std::vector<typename T::value_type>& b,
191 std::vector<typename T::value_type>& c,
192 std::vector<typename T::value_type>& d) {
196 for (
size_t i = 1; i < n; ++i) {
197 typename T::value_type m = a[i] / b[i - 1];
198 b[i] = b[i] - m * c[i - 1];
199 d[i] = d[i] - m * d[i - 1];
203 d[n - 1] = d[n - 1] / b[n - 1];
204 for (
int i = n - 2; i >= 0; --i) {
205 d[i] = (d[i] - c[i] * d[i + 1]) / b[i];
229 std::map<typename IntegratorDecorator<S>::time_type, S> state_history_;
230 std::unique_ptr<CubicSplineInterpolator<typename IntegratorDecorator<S>::time_type>> spline_interpolator_;
232 mutable std::mutex history_mutex_;
233 typename IntegratorDecorator<S>::time_type last_query_time_{};
234 bool history_compressed_{
false};
246 , config_(std::move(
config))
255 void step(
typename IntegratorDecorator<S>::state_type& state,
typename IntegratorDecorator<S>::time_type dt)
override {
256 this->wrapped_integrator_->step(state, dt);
257 record_state(state, this->current_time());
263 void integrate(
typename IntegratorDecorator<S>::state_type& state,
typename IntegratorDecorator<S>::time_type dt,
264 typename IntegratorDecorator<S>::time_type end_time)
override {
266 record_state(state, this->current_time());
269 this->wrapped_integrator_->integrate(state, dt, end_time);
272 record_state(state, this->current_time());
275 if (config_.enable_compression && state_history_.size() > config_.compression_threshold) {
287 std::lock_guard<std::mutex> lock(history_mutex_);
289 auto start_time = std::chrono::high_resolution_clock::now();
291 if (state_history_.empty()) {
292 throw std::runtime_error(
"No history available for interpolation");
297 if (t < bounds.first || t > bounds.second) {
298 if (!config_.allow_extrapolation) {
299 stats_.out_of_bounds_queries++;
300 throw std::runtime_error(
"Time " + std::to_string(t) +
" is outside interpolation bounds [" +
301 std::to_string(bounds.first) +
", " + std::to_string(bounds.second) +
"]");
305 typename IntegratorDecorator<S>::time_type range = bounds.second - bounds.first;
306 if (std::abs(t - bounds.first) > config_.extrapolation_warning_threshold * range ||
307 std::abs(t - bounds.second) > config_.extrapolation_warning_threshold * range) {
308 stats_.extrapolation_warnings++;
312 S result = perform_interpolation(t);
314 auto end_time = std::chrono::high_resolution_clock::now();
315 double duration_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count();
316 stats_.update_interpolation_time(duration_ns);
318 last_query_time_ = t;
328 std::vector<S> results;
329 results.reserve(time_points.size());
331 for (
typename IntegratorDecorator<S>::time_type t : time_points) {
345 std::pair<std::vector<typename IntegratorDecorator<S>::time_type>, std::vector<S>>
get_dense_output(
346 typename IntegratorDecorator<S>::time_type start_time,
347 typename IntegratorDecorator<S>::time_type end_time,
349 if (num_points < 2) {
350 throw std::invalid_argument(
"num_points must be at least 2");
353 std::vector<typename IntegratorDecorator<S>::time_type> times;
354 std::vector<S> states;
356 typename IntegratorDecorator<S>::time_type dt = (end_time - start_time) / (num_points - 1);
358 for (
size_t i = 0; i < num_points; ++i) {
359 typename IntegratorDecorator<S>::time_type t = start_time + i * dt;
364 return {std::move(times), std::move(states)};
385 std::pair<typename IntegratorDecorator<S>::time_type,
typename IntegratorDecorator<S>::time_type>
get_time_bounds()
const {
386 std::lock_guard<std::mutex> lock(history_mutex_);
387 if (state_history_.empty()) {
388 return {
typename IntegratorDecorator<S>::time_type{},
typename IntegratorDecorator<S>::time_type{}};
390 return {state_history_.begin()->first, state_history_.rbegin()->first};
397 std::lock_guard<std::mutex> lock(history_mutex_);
398 state_history_.clear();
399 history_compressed_ =
false;
406 std::lock_guard<std::mutex> lock(history_mutex_);
407 return state_history_.size();
420 void record_state(
const S& state,
typename IntegratorDecorator<S>::time_type time) {
421 std::lock_guard<std::mutex> lock(history_mutex_);
424 if (state_history_.size() >= config_.max_history_size) {
426 state_history_.erase(state_history_.begin());
429 state_history_[time] = state;
435 S perform_interpolation(
typename IntegratorDecorator<S>::time_type t) {
436 switch (config_.method) {
437 case InterpolationMethod::LINEAR:
438 return linear_interpolation(t);
439 case InterpolationMethod::CUBIC_SPLINE:
440 return cubic_spline_interpolation(t);
441 case InterpolationMethod::HERMITE:
442 return hermite_interpolation(t);
443 case InterpolationMethod::AKIMA:
444 return akima_interpolation(t);
446 throw std::runtime_error(
"Unknown interpolation method");
453 S linear_interpolation(
typename IntegratorDecorator<S>::time_type t) {
454 auto it = state_history_.lower_bound(t);
456 if (it == state_history_.begin()) {
460 if (it == state_history_.end()) {
461 return state_history_.rbegin()->second;
464 auto prev_it = std::prev(it);
466 typename IntegratorDecorator<S>::time_type t1 = prev_it->first;
467 typename IntegratorDecorator<S>::time_type t2 = it->first;
468 const S& s1 = prev_it->second;
469 const S& s2 = it->second;
471 typename IntegratorDecorator<S>::time_type alpha = (t - t1) / (t2 - t1);
474 for (
size_t i = 0; i < result.size(); ++i) {
475 result[i] = (1 - alpha) * s1[i] + alpha * s2[i];
484 S cubic_spline_interpolation(
typename IntegratorDecorator<S>::time_type t) {
486 std::vector<typename IntegratorDecorator<S>::time_type> times;
487 std::vector<std::vector<typename S::value_type>> states;
489 times.reserve(state_history_.size());
490 states.reserve(state_history_.size());
492 for (
const auto& [time, state] : state_history_) {
493 times.push_back(time);
494 std::vector<typename S::value_type> state_vec(state.begin(), state.end());
495 states.push_back(std::move(state_vec));
498 spline_interpolator_->set_data(times, states);
499 auto result_vec = spline_interpolator_->interpolate(t);
503 if constexpr (std::is_same_v<S, std::vector<typename S::value_type>>) {
506 std::copy(result_vec.begin(), result_vec.end(), result.begin());
515 S hermite_interpolation(
typename IntegratorDecorator<S>::time_type t) {
517 return cubic_spline_interpolation(t);
523 S akima_interpolation(
typename IntegratorDecorator<S>::time_type t) {
525 return cubic_spline_interpolation(t);
531 void compress_history() {
532 if (state_history_.size() <= config_.compression_threshold) {
537 auto it = state_history_.begin();
538 while (it != state_history_.end() && state_history_.size() > config_.compression_threshold / 2) {
539 auto next_it = std::next(it);
540 if (next_it != state_history_.end()) {
541 auto next_next_it = std::next(next_it);
542 if (next_next_it != state_history_.end()) {
544 if (is_point_redundant(*it, *next_it, *next_next_it)) {
545 it = state_history_.erase(next_it);
553 stats_.history_compressions++;
554 history_compressed_ =
true;
560 bool is_point_redundant(
const std::pair<
typename IntegratorDecorator<S>::time_type, S>& p1,
561 const std::pair<
typename IntegratorDecorator<S>::time_type, S>& p2,
562 const std::pair<
typename IntegratorDecorator<S>::time_type, S>& p3) {
564 typename IntegratorDecorator<S>::time_type alpha = (p2.first - p1.first) / (p3.first - p1.first);
566 for (
size_t i = 0; i < p2.second.size(); ++i) {
567 double interpolated = (1 - alpha) * p1.second[i] + alpha * p3.second[i];
568 double error = std::abs(interpolated - p2.second[i]);
569 if (error > config_.compression_tolerance) {