1// Backend interface: batched numerical primitives over CSR panels. Every primitive has a CPU FP64 reference;2// the Metal backend implements the same contract in FP32 (+ compensated partial sums combined in FP64 on the CPU).3#pragma once4#include <memory>5#include <span>6#include <string>7#include <vector>8#include "raid/core/types.hpp"9#include "raid/core/plan.hpp"1011namespace raid::backend {1213/// Batched input: `values` concatenated, `offsets` (n_series+1). All kernels are segment-wise over series.14struct Batch {15 std::span<const f64> values;16 std::span<const i64> offsets;17 usize n_series() const { return offsets.empty() ? 0 : offsets.size() - 1; }18 usize size(usize i) const { return static_cast<usize>(offsets[i + 1] - offsets[i]); }19 std::span<const f64> series(usize i) const { return values.subspan(static_cast<usize>(offsets[i]), size(i)); }20};2122/// Timing / provenance of one primitive call (filled by every backend).23struct KernelStats {24 std::string kernel;25 Backend backend{Backend::cpu};26 Precision precision{Precision::fp64};27 f64 wall_seconds{0};28 f64 device_seconds{0}; // GPU time (Metal GPUStartTime/EndTime) or 029 usize bytes_in{0}, bytes_out{0};30 usize buffer_allocations{0};31 bool compiled_now{false}; // first shader compilation happened in this call32 bool fell_back{false}; // Metal → CPU fallback occurred33 std::string note;34};3536class Backend {37 public:38 virtual ~Backend() = default;39 virtual std::string name() const = 0;40 virtual raid::Backend kind() const = 0;41 virtual Device device() const = 0;42 virtual bool available() const = 0;4344 /// Σ x² per series (realized variance when x are returns).45 virtual std::vector<f64> sum_squares(const Batch& b, KernelStats* st = nullptr) = 0;46 /// Σ |x_i| |x_{i-1}| per series (bipower core, without constants).47 virtual std::vector<f64> sum_abs_adjacent(const Batch& b, KernelStats* st = nullptr) = 0;48 /// Σ x² 1{x<0}, Σ x² 1{x>0} per series (semivariances).49 virtual void semivariances(const Batch& b, std::vector<f64>& neg, std::vector<f64>& pos, KernelStats* st = nullptr) = 0;50 /// Σ x⁴ per series (realized quarticity core).51 virtual std::vector<f64> sum_fourth(const Batch& b, KernelStats* st = nullptr) = 0;52 /// Rolling sum of squares with window w per series; output same layout as input (NaN for incomplete windows).53 virtual std::vector<f64> rolling_sum_squares(const Batch& b, usize window, KernelStats* st = nullptr) = 0;54 /// Batched sufficient statistics for regressions with k regressors: for each series i, X_i is (n_i × k) row-major55 /// stored in `x` (concatenated), y_i in `y`; outputs xtx (k×k per series, row-major), xty (k per series), yty.56 virtual void regression_stats(std::span<const f64> x, std::span<const f64> y, std::span<const i64> offsets, usize k,57 std::vector<f64>& xtx, std::vector<f64>& xty, std::vector<f64>& yty, KernelStats* st = nullptr) = 0;58 /// Batched GARCH(1,1)-family negative log-likelihood for a grid of parameter vectors on each series.59 /// params: n_series × n_params × p (row-major); returns nll (n_series × n_params). Model codes in models/garch.hpp.60 virtual std::vector<f64> garch_nll_batch(const Batch& returns, std::span<const f64> params, usize n_params, usize p, int model_code, f64 nu_fixed, KernelStats* st = nullptr) = 0;61};6263std::shared_ptr<Backend> make_cpu_backend(unsigned threads = 0);64#ifdef RAID_ENABLE_METAL65std::shared_ptr<Backend> make_metal_backend(const PrecisionPolicy& policy = {});66#endif67/// Resolve a plan to a concrete backend (auto → Metal if available and allowed, else CPU). Never throws for auto.68std::shared_ptr<Backend> resolve(const ExecutionPlan& plan, std::string* reason = nullptr);69std::vector<Device> list_devices();7071} // namespace raid::backend7273namespace raid::backend {74#ifdef RAID_ENABLE_METAL75/// Metal-only fused realized kernel (one pass for Σx², Σ|x||x₋₁|, Σx²1{x<0}, Σx²1{x>0}, Σx⁴). Returns false if `be` is not Metal.76bool realized_fused(Backend& be, const Batch& b, std::vector<f64>& s2, std::vector<f64>& adj, std::vector<f64>& neg, std::vector<f64>& pos, std::vector<f64>& s4, KernelStats* st = nullptr);77/// Seconds spent compiling/loading the shader library (0 if not Metal or not yet loaded).78f64 metal_compile_seconds(Backend& be);79#endif80}81