/// @file dpf/eval_full.hpp /// @brief Evaluate every input in the DPF domain. /// @details Equivalent to `eval_interval` from /// `std::numeric_limits::min()` through `max()`. /// When the input offset is not yet assigned, use `defer_eval_full` /// (full-domain identity eval plus a deferred rotation view). /// @snippet evaluation/eval_full.cpp eval-full /// @author Ryan Henry /// @author Christopher Jiang /// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors) /// @license Released under a GNU General Public v2.0 (GPLv2) license; /// see [LICENSE.md](@ref license) for details. #ifndef LIBDPF_INCLUDE_DPF_EVAL_FULL_HPP__ #define LIBDPF_INCLUDE_DPF_EVAL_FULL_HPP__ #include #include "hedley/hedley.h" #include #include #include #include #include "dpf/dpf_key.hpp" #include "dpf/eval_common.hpp" #include "dpf/eval_target.hpp" #include "dpf/output_buffer.hpp" #include "dpf/interval_memoizer.hpp" #include "dpf/rotation_iterable.hpp" #include "dpf/subinterval_iterable.hpp" #include "dpf/verifiable.hpp" namespace dpf { namespace internal { template , bool> = false> auto eval_full(const DpfKey & dpf, OutputBuffers && outbufs, IntervalMemoizer && memoizer, std::index_sequence, proof_token * pi = nullptr) { using dpf_type = DpfKey; using input_type = typename dpf_type::input_type; auto offset = dpf.offset_x(0); // N.B.: throws if dpf is not ready dpf::internal::eval_interval_impl(dpf, std::numeric_limits::min(), std::numeric_limits::max(), outbufs, memoizer, std::make_index_sequence(), pi); return utils::make_tuple(dpf::rotation_iterable(std::begin(utils::get(outbufs)), std::end(utils::get(outbufs)), offset)...); } template , bool> = false> auto eval_full(const DpfKey & dpf, OutputBuffers && outbufs, IntervalMemoizer && memoizer, std::index_sequence, proof_token * pi = nullptr) { using dpf_type = DpfKey; using input_type = typename dpf_type::input_type; dpf::internal::eval_interval_impl(dpf, std::numeric_limits::min(), std::numeric_limits::max(), outbufs, memoizer, std::make_index_sequence(), pi); return utils::make_tuple( subinterval_iterable(std::begin(utils::get(outbufs)), utils::size(utils::get(outbufs)), std::size_t{0}, utils::get(outbufs).size() - 1, std::size_t{0}, std::size_t{0})...); } } // namespace internal /// \complexity Same expansion as `eval_interval` on the whole domain. L = 2^{n - lg(outputs_per_leaf)} leaf nodes, n = `depth`. Time Θ(L) interior traversals. The output buffer stores one slot per domain point (2^n). template && !is_multilevel_key_v, bool> = true> HEDLEY_ALWAYS_INLINE auto eval_full(const DpfKey & dpf, OutputBuffers && outbufs, IntervalMemoizer && memoizer, proof_token * pi = nullptr) { assert_not_wildcard_output(dpf); return internal::eval_full(dpf, outbufs, memoizer, std::make_index_sequence<1+sizeof...(Is)>(), pi); } /// @brief Evaluate the whole domain and fold a once-per-BFS-node VDPF proof. /// \complexity Same expansion as `eval_interval` on the whole domain. L = 2^{n - lg(outputs_per_leaf)} leaf nodes, n = `depth`. Time Θ(L) interior traversals. The output buffer stores one slot per domain point (2^n). template && !is_multilevel_key_v, bool> = true> HEDLEY_ALWAYS_INLINE auto eval_full(const DpfKey & dpf, OutputBuffers && outbufs, IntervalMemoizer && memoizer, prove_ref pr) { static_assert(DpfKey::is_verifiable, "eval_full(..., prove(π)): key must carry dpf::verifiable"); detail::vdpf::init_proof(pr.token, dpf); auto out = eval_full(dpf, std::forward(outbufs), std::forward(memoizer), &pr.token); detail::vdpf::fold_output_binding(pr.token, dpf); return out; } /// @brief Evaluate the whole domain and fold each written output into a sketch. /// \complexity Same expansion as `eval_interval` on the whole domain. L = 2^{n - lg(outputs_per_leaf)} leaf nodes, n = `depth`. Time Θ(L) interior traversals. The output buffer stores one slot per domain point (2^n). template && !is_multilevel_key_v, bool> = true> HEDLEY_ALWAYS_INLINE auto eval_full(const DpfKey & dpf, OutputBuffers && outbufs, IntervalMemoizer && memoizer, sketch_ref & sk) { static_assert(DpfKey::is_extractable, "eval_full(..., sketch(σ)): key must carry dpf::extractable"); auto ret = eval_full(dpf, outbufs, std::forward(memoizer)); if constexpr (sizeof...(Is) == 0) { for (std::size_t k = 0; k < utils::size(outbufs); ++k) sk.absorb(outbufs[k]); } return ret; } /// \complexity Same expansion as `eval_interval` on the whole domain. L = 2^{n - lg(outputs_per_leaf)} leaf nodes, n = `depth`. Time Θ(L) interior traversals. The output buffer stores one slot per domain point (2^n). template && !is_multilevel_key_v, bool> = true, std::enable_if_t>, std::decay_t>, bool> = true> HEDLEY_ALWAYS_INLINE auto eval_full(const DpfKey & dpf, OutputBuffers & outbufs) // NOLINT(runtime/references) { // The full-domain workspace is keyed only by the key type. Reusing it // drops a heap allocation per call and lets a repeated key skip the // interior rebuild (assign_interval keeps the last level). thread_local auto memo = dpf::make_basic_full_memoizer(); return eval_full(dpf, outbufs, memo); } /// @brief Evaluate the whole domain into `outbufs` and fold a VDPF proof. /// \complexity Same expansion as `eval_interval` on the whole domain. L = 2^{n - lg(outputs_per_leaf)} leaf nodes, n = `depth`. Time Θ(L) interior traversals. The output buffer stores one slot per domain point (2^n). template && !is_multilevel_key_v, bool> = true, std::enable_if_t>, std::decay_t>, bool> = true> HEDLEY_ALWAYS_INLINE auto eval_full(const DpfKey & dpf, OutputBuffers & outbufs, prove_ref pr) // NOLINT(runtime/references) { static_assert(DpfKey::is_verifiable, "eval_full(..., prove(π)): key must carry dpf::verifiable"); return eval_full(dpf, outbufs, dpf::make_basic_full_memoizer(dpf), pr); } /// \complexity Same expansion as `eval_interval` on the whole domain. L = 2^{n - lg(outputs_per_leaf)} leaf nodes, n = `depth`. Time Θ(L) interior traversals. The output buffer stores one slot per domain point (2^n). template && !is_multilevel_key_v, bool> = true, std::enable_if_t>, std::decay_t>, bool> = true> HEDLEY_ALWAYS_INLINE auto eval_full(const DpfKey & dpf, IntervalMemoizer && memoizer) { auto outbufs = utils::make_tuple( make_output_buffer_for_full(dpf), make_output_buffer_for_full(dpf)...); // moving `outbufs` is allowed as the `outbufs` are `std::vectors` // the underlying data remains on the heap // and thus the data the iterable refers to is still valid auto iterable = eval_full(dpf, outbufs, memoizer); return std::make_pair(std::move(outbufs), std::move(iterable)); } /// @brief Evaluate the whole domain, allocating a basic full memoizer and a buffer. /// @tparam I output index /// @tparam Is is /// @tparam DpfKey DPF key type /// @tparam DpfKey DPF key type /// @param dpf the DPF key /// @return the evaluation result /// \complexity Same expansion as `eval_interval` on the whole domain. L = 2^{n - lg(outputs_per_leaf)} leaf nodes, n = `depth`. Time Θ(L) interior traversals. The output buffer stores one slot per domain point (2^n). template && !is_multilevel_key_v, bool> = true> HEDLEY_ALWAYS_INLINE auto eval_full(const DpfKey & dpf) { return eval_full(dpf, dpf::make_basic_full_memoizer(dpf)); } /// @brief Full-domain deferred eval while the input offset is unset. /// @details Sugar for `defer_eval_interval` over `[min, max]`. `outbufs` must /// be full-domain sized. After assign, `.get()` yields the logical /// full-domain view (same values as eager `eval_full` after assign). template && !is_multilevel_key_v, bool> = true> auto defer_eval_full(const DpfKey & dpf, OutputBuffers & outbufs, IntervalMemoizer && memoizer) // NOLINT(runtime/references) { using input_type = typename DpfKey::input_type; return defer_eval_interval(dpf, std::numeric_limits::min(), std::numeric_limits::max(), outbufs, std::forward(memoizer)); } /// @brief `defer_eval_full` with a basic full-domain memoizer. template && !is_multilevel_key_v, bool> = true, std::enable_if_t>, std::decay_t>, bool> = true> auto defer_eval_full(const DpfKey & dpf, OutputBuffers & outbufs) // NOLINT(runtime/references) { return defer_eval_full(dpf, outbufs, dpf::make_basic_full_memoizer(dpf)); } } // namespace dpf #endif // LIBDPF_INCLUDE_DPF_EVAL_FULL_HPP__