| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "ml_config.h" |
| 4 | #include "ml_features.h" |
| 5 | |
| 6 | static inline size_t ml_effective_smooth_n(const ml_features_t *features) |
| 7 | { |
| 8 | // smooth_n == 0 is normalized to an effective window of 1, preserving the |
| 9 | // existing feature-extraction shape without introducing a separate no-op path. |
| 10 | return features->smooth_n == 0 ? 1 : features->smooth_n; |
| 11 | } |
| 12 | |
| 13 | static inline void ml_validate_features_input(const ml_features_t *features, bool prediction_path) |
| 14 | { |
| 15 | size_t smooth_n = ml_effective_smooth_n(features); |
| 16 | size_t min_required_samples = features->diff_n + smooth_n + features->lag_n; |
| 17 | |
| 18 | fatal_assert(features->dst_n >= features->src_n && |
| 19 | "ml_features: dst buffer must be at least as large as src buffer"); |
| 20 | if (prediction_path) { |
| 21 | fatal_assert(features->src_n >= min_required_samples && |
| 22 | "ml_features_preprocess_predict: src buffer is smaller than diff_n + effective_smooth_n + lag_n"); |
| 23 | } else { |
| 24 | fatal_assert(features->src_n >= min_required_samples && |
| 25 | "ml_features_preprocess: src buffer is smaller than diff_n + effective_smooth_n + lag_n"); |
| 26 | } |
| 27 | } |
| 28 | |
| 29 | static void ml_features_diff(ml_features_t *features) |
| 30 | { |
| 31 | if (features->diff_n == 0) |
| 32 | return; |
| 33 | |
| 34 | for (size_t idx = 0; idx != (features->src_n - features->diff_n); idx++) { |
| 35 | size_t high = (features->src_n - 1) - idx; |
| 36 | size_t low = high - features->diff_n; |
| 37 | |
| 38 | features->dst[low] = features->src[high] - features->src[low]; |
| 39 | } |
| 40 | |
| 41 | size_t n = features->src_n - features->diff_n; |
| 42 | memcpy(features->src, features->dst, n * sizeof(calculated_number_t)); |
| 43 | |
| 44 | for (size_t idx = features->src_n - features->diff_n; idx != features->src_n; idx++) |
| 45 | features->src[idx] = 0.0; |
| 46 | } |
| 47 | |
| 48 | static void ml_features_smooth(ml_features_t *features) |
| 49 | { |
| 50 | size_t smooth_n = ml_effective_smooth_n(features); |
| 51 | calculated_number_t sum = 0.0; |
| 52 | |
| 53 | size_t idx = 0; |
| 54 | for (; idx != smooth_n - 1; idx++) |
| 55 | sum += features->src[idx]; |
| 56 | |
| 57 | for (; idx != (features->src_n - features->diff_n); idx++) { |
| 58 | sum += features->src[idx]; |
| 59 | calculated_number_t prev_cn = features->src[idx - (smooth_n - 1)]; |
| 60 | features->src[idx - (smooth_n - 1)] = sum / smooth_n; |
| 61 | sum -= prev_cn; |
| 62 | } |
| 63 | |
| 64 | for (idx = 0; idx != smooth_n; idx++) |
| 65 | features->src[(features->src_n - 1) - idx] = 0.0; |
| 66 | } |
| 67 | |
| 68 | static void ml_features_lag(ml_features_t *features, std::vector<DSample> &preprocessed_features, double sampling_ratio) |
| 69 | { |
| 70 | size_t n = features->src_n - features->diff_n - ml_effective_smooth_n(features) + 1 - features->lag_n; |
| 71 | preprocessed_features.clear(); |
| 72 | preprocessed_features.reserve(n); |
| 73 | |
| 74 | uint32_t max_mt = std::numeric_limits<uint32_t>::max(); |
| 75 | uint32_t cutoff = static_cast<double>(max_mt) * sampling_ratio; |
| 76 | |
| 77 | size_t sample_idx = 0; |
| 78 | |
| 79 | for (size_t idx = 0; idx != n; idx++) { |
| 80 | if (Cfg.random_nums[idx % Cfg.random_nums.size()] > cutoff) { |
| 81 | continue; |
| 82 | } |
| 83 | |
| 84 | preprocessed_features.emplace_back(); |
| 85 | DSample &DS = preprocessed_features[sample_idx++]; |
| 86 | DS.set_size(features->lag_n + 1); |
| 87 | |
| 88 | for (size_t feature_idx = 0; feature_idx != features->lag_n + 1; feature_idx++) |
| 89 | DS(feature_idx) = features->src[idx + feature_idx]; |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | void ml_features_preprocess(ml_features_t *features, std::vector<DSample> &preprocessed_features, double sampling_ratio) |
| 94 | { |
| 95 | ml_validate_features_input(features, false); |
| 96 | ml_features_diff(features); |
| 97 | ml_features_smooth(features); |
| 98 | ml_features_lag(features, preprocessed_features, sampling_ratio); |
| 99 | } |
| 100 | |
| 101 | void ml_features_preprocess_predict(ml_features_t *features, DSample &sample) |
| 102 | { |
| 103 | ml_validate_features_input(features, true); |
| 104 | ml_features_diff(features); |
| 105 | ml_features_smooth(features); |
| 106 | |
| 107 | sample.set_size(features->lag_n + 1); |
| 108 | for (size_t feature_idx = 0; feature_idx != features->lag_n + 1; feature_idx++) |
| 109 | sample(feature_idx) = features->src[feature_idx]; |
| 110 | } |