#include #include #include #include #include #include "dpf.hpp" /// Fused inner product: do not materialize the DPF vector. /// A scalar weight vector dots with one output. A row of a tuple or /// `std::array` dots with several outputs, including an ancestor slot /// and the leaf, read off one path. int main() { using In = std::uint8_t; //! [eval-inner-product-scalar] // Trivial: sum_x DPF(x) * w[x] over a short interval. const In alpha = 42; const std::uint64_t beta = 7; auto [k0, k1] = dpf::make_dpf(alpha, beta); const In from = 40; const In to = 50; std::vector w(to - from + 1); for (std::size_t i = 0; i < w.size(); ++i) w[i] = i + 1; const auto s0 = dpf::eval_inner_product(dpf::paired, k0, from, to, w); const auto s1 = dpf::eval_inner_product(dpf::paired, k1, from, to, w); //! [eval-inner-product-scalar] if (dpf::reconstruct(s0, s1) != beta * w[alpha - from]) { std::cerr << "scalar interval\n"; return 1; } //! [eval-inner-product-full] // Trivial full domain. Only α contributes. std::vector wall(256, 1); const auto f0 = dpf::eval_full_inner_product(dpf::paired, k0, wall); const auto f1 = dpf::eval_full_inner_product(dpf::paired, k1, wall); //! [eval-inner-product-full] if (dpf::reconstruct(f0, f1) != beta) { std::cerr << "full\n"; return 1; } //! [eval-inner-product-paired] // Two outputs on the same leaf. rows[i] = {weight for output 0, output 1}. auto [p0, p1] = dpf::make_dpf(In{9}, std::uint32_t{3}, std::uint32_t{5}); std::vector> rows; for (In x = 8;; ++x) { rows.push_back({std::uint32_t{1}, std::uint32_t{x}}); if (x == 10) break; } const auto a0 = dpf::eval_inner_product<0, 1>(dpf::paired, p0, In{8}, In{10}, rows); const auto a1 = dpf::eval_inner_product<0, 1>(dpf::paired, p1, In{8}, In{10}, rows); //! [eval-inner-product-paired] // x=9 is hot: output0 * 1 + output1 * 9. if (dpf::reconstruct(a0, a1) != std::uint64_t{3} * 1u + std::uint64_t{5} * 9u) { std::cerr << "paired leaf\n"; return 1; } //! [eval-inner-product-ancestor] // Prefix slot at<4> and the full-domain leaf, one path per point. // 0x2a and 0x2b share the high nibble 0x2, so both see payload 5 there. // 0x10 is a different nibble. Only 0x2a is hot on the leaf. auto [h0, h1] = dpf::make_dpf(In{0x2a}, dpf::at<4>(std::uint8_t{5}), std::uint8_t{9}); const std::vector pts{0x10, 0x2a, 0x2b}; const std::vector> hw{ {1u, 0u}, {1u, 1u}, {2u, 4u}}; const auto q0 = dpf::eval_sequence_inner_product<0, 1>(h0, pts.begin(), pts.end(), hw); const auto q1 = dpf::eval_sequence_inner_product<0, 1>(h1, pts.begin(), pts.end(), hw); //! [eval-inner-product-ancestor] // 0x10 is off. 0x2a: 5*1 + 9*1. 0x2b: prefix still 5, leaf 0, times (2, 4). const std::uint64_t ancestor_expect = 5u * 1u + 9u * 1u + 5u * 2u; if (dpf::reconstruct(q0, q1) != ancestor_expect) { std::cerr << "ancestor\n"; return 1; } //! [eval-inner-product-recipe] const auto recipe = dpf::make_sequence_recipe(pts.begin(), pts.end()); const auto r0 = dpf::eval_sequence_inner_product<0, 1>( h0, recipe, pts.begin(), pts.end(), hw); const auto r1 = dpf::eval_sequence_inner_product<0, 1>( h1, recipe, pts.begin(), pts.end(), hw); //! [eval-inner-product-recipe] if (dpf::reconstruct(r0, r1) != ancestor_expect) { std::cerr << "recipe\n"; return 1; } std::cout << dpf::reconstruct(s0, s1) << "\n"; return 0; }