WeightedLowess
A C++ library for LOWESS with various weighting schemes
Loading...
Searching...
No Matches
interpolate.hpp
Go to the documentation of this file.
1#ifndef WEIGHTEDLOWESS_INTERPOLATE_HPP
2#define WEIGHTEDLOWESS_INTERPOLATE_HPP
3
4#include <vector>
5#include <cstddef>
6#include <utility>
7#include <cassert>
8
9#include "sanisizer/sanisizer.hpp"
10
11#include "window.hpp"
12#include "utils.hpp"
13
19namespace WeightedLowess {
20
30 std::vector<std::size_t> boundaries;
34};
35
52template<typename Data_>
54 const Data_* const x_fit,
55 const PrecomputedWindows<Data_>& windows_fit,
56 const std::size_t num_points_out,
57 const Data_* const x_out
58) {
59 const auto& anchors = windows_fit.anchors;
60 const auto num_anchors = anchors.size();
61
62 AssignedSegments output;
63 sanisizer::resize(output.boundaries, num_anchors);
64
65 std::size_t counter = 0;
66 {
67 const auto right = x_fit[anchors[0]];
68 while (counter < num_points_out && x_out[counter] < right) {
69 ++counter;
70 }
71 }
72
73 for (I<decltype(num_anchors)> i = 1; i < num_anchors; ++i) {
74 output.boundaries[i - 1] = counter;
75 if (counter == num_points_out) {
76 continue;
77 }
78 const auto right = x_fit[anchors[i]];
79 while (counter < num_points_out && x_out[counter] < right) {
80 ++counter;
81 }
82 }
83
84 // Anyone equal to the last anchor get assigned to the last segment.
85 {
86 const auto right = x_fit[anchors[num_anchors - 1]];
87 while (counter < num_points_out && x_out[counter] == right) {
88 ++counter;
89 }
90 output.boundaries[num_anchors - 1] = counter;
91 }
92
93 return output;
94}
95
101inline std::pair<std::size_t, std::size_t> get_interpolation_boundaries(const AssignedSegments& assigned_out) {
102 return std::make_pair(assigned_out.boundaries.front(), assigned_out.boundaries.back());
103}
104
127template<typename Data_>
129 const Data_* const x_fit,
130 const PrecomputedWindows<Data_>& windows_fit,
131 const Data_* const fitted_fit,
132 const Data_* const x_out,
133 const AssignedSegments& assigned_out,
134 Data_* const fitted_out,
135 int num_threads
136) {
137 const auto& anchors = windows_fit.anchors;
138 const auto num_anchors = anchors.size();
139 assert(num_anchors > 0);
140 const auto num_anchors_m1 = num_anchors - 1;
141
142 // One would think that we should parallelize across x_out instead of anchors,
143 // as this has better worksharing when x_out is not evenly distributed across anchor segments.
144 // However, if we did so, we'd have to store the slope and intercept for the anchor segments first,
145 // then look up the slope and intercept for each element of x_out.
146 // This involves an extra memory access and is not SIMD-able.
147 parallelize(num_threads, num_anchors_m1, [&](const int, const I<decltype(num_anchors_m1)> start, const I<decltype(num_anchors_m1)> length) {
148 for (I<decltype(start)> s = start, end = start + length; s < end; ++s) {
149 const auto run_start = assigned_out.boundaries[s];
150 const auto run_end = assigned_out.boundaries[s + 1];
151 if (run_start == run_end) {
152 continue;
153 }
154
155 const auto left_anchor = windows_fit.anchors[s];
156 const auto right_anchor = windows_fit.anchors[s + 1];
157 const Data_ xdiff = x_fit[right_anchor] - x_fit[left_anchor];
158 const Data_ ydiff = fitted_fit[right_anchor] - fitted_fit[left_anchor];
159 if (xdiff > 0) {
160 const Data_ slope = ydiff / xdiff;
161 const Data_ intercept = fitted_fit[right_anchor] - slope * x_fit[right_anchor];
162 for (auto outpt = run_start; outpt < run_end; ++outpt) {
163 fitted_out[outpt] = slope * x_out[outpt] + intercept;
164 }
165 } else {
166 // Protect against infinite slopes by just taking the average.
167 const Data_ ave = fitted_fit[left_anchor] + ydiff / 2;
168 std::fill(fitted_out + run_start, fitted_out + run_end, ave);
169 }
170 }
171 });
172}
173
194template<typename Data_>
195std::pair<std::size_t, std::size_t> interpolate(
196 const Data_* const x_fit,
197 const PrecomputedWindows<Data_>& windows_fit,
198 const Data_* const fitted_fit,
199 const std::size_t num_points_out,
200 const Data_* const x_out,
201 Data_* const fitted_out,
202 int num_threads
203) {
204 const auto assigned_out = assign_to_segments(x_fit, windows_fit, num_points_out, x_out);
205 interpolate(x_fit, windows_fit, fitted_fit, x_out, assigned_out, fitted_out, num_threads);
206 return get_interpolation_boundaries(assigned_out);
207}
208
209}
210
211#endif
Namespace for LOWESS functions.
Definition compute.hpp:18
AssignedSegments assign_to_segments(const Data_ *const x_fit, const PrecomputedWindows< Data_ > &windows_fit, const std::size_t num_points_out, const Data_ *const x_out)
Definition interpolate.hpp:53
void interpolate(const Data_ *const x_fit, const PrecomputedWindows< Data_ > &windows_fit, const Data_ *const fitted_fit, const Data_ *const x_out, const AssignedSegments &assigned_out, Data_ *const fitted_out, int num_threads)
Definition interpolate.hpp:128
int parallelize(const int num_workers, const Task_ num_tasks, Run_ run_task_range)
Definition parallelize.hpp:32
std::pair< std::size_t, std::size_t > get_interpolation_boundaries(const AssignedSegments &assigned_out)
Definition interpolate.hpp:101
Assigned segments.
Definition interpolate.hpp:26
Precomputed windows for LOWESS smoothing.
Definition window.hpp:241
Compute the smoothing window for each point.