2026-09-24 20:44:07 -06:00
|
|
|
<!-- # Evaluating DPFs {#evaluation} -->
|
|
|
|
|
|
|
|
|
|
`make_dpf(x, y)` returns one key per party. Evaluation of a key yields that
|
|
|
|
|
party's share. Leaf outputs are subtractive shares: open them with
|
|
|
|
|
`dpf::reconstruct`, which computes `share0 - share1`. Comparison outputs are
|
|
|
|
|
additive shares: `reconstruct` computes `share0 + share1`. A single-output
|
|
|
|
|
`eval_point` returns a small handle; `*handle` is the share. An unassigned
|
|
|
|
|
`dpf::wildcard` output throws `std::runtime_error`.
|
|
|
|
|
|
|
|
|
|
`[from, to]` is inclusive. The points passed to `eval_sequence` are a
|
|
|
|
|
nondecreasing range; an unsorted range throws `std::runtime_error`.
|
|
|
|
|
|
|
|
|
|
Memoizers hold interior nodes between calls. Output buffers hold the shares
|
|
|
|
|
a multi-point evaluation writes. Pass both as mutable named objects when a
|
|
|
|
|
later call should reuse them. The factories
|
|
|
|
|
`make_basic_path_memoizer`, `make_basic_interval_memoizer`, and
|
|
|
|
|
`make_*_sequence_memoizer` unwrap `party_key`, so a workspace built from
|
|
|
|
|
either party's type accepts both parties. Name that type with
|
|
|
|
|
`dpf::unwrap_party_key_t<std::decay_t<decltype(key)>>`.
|
|
|
|
|
|
|
|
|
|
# Memoizers {#memoizers}
|
|
|
|
|
|
|
|
|
|
## Path memoizers {#path_memoizers}
|
|
|
|
|
|
|
|
|
|
`eval_point` walks one root-to-leaf path. `make_basic_path_memoizer<Key>()`
|
|
|
|
|
keeps every node of the previous point, and the next point recomputes only
|
|
|
|
|
the suffix after the common prefix. `make_nonmemoizing_path_memoizer<Key>()`
|
|
|
|
|
keeps one node and starts from the root on every call. A one-off
|
|
|
|
|
`eval_point(key, x)` uses the nonmemoizing memoizer.
|
|
|
|
|
|
|
|
|
|
Pass the memoizer as a mutable lvalue. The default argument is a new
|
|
|
|
|
temporary, so it has no previous point to resume from. Keep a separate
|
|
|
|
|
memoizer for each key you are in the middle of evaluating. A different root
|
|
|
|
|
restarts the path.
|
2026-09-24 14:08:32 -06:00
|
|
|
|
|
|
|
|
**Code samples**\n
|
|
|
|
|
<div class="tabbed">
|
|
|
|
|
|
|
|
|
|
- <b class="tab-title">memoizers.cpp</b> \include{cpp} evaluation/memoizers.cpp
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-09-24 20:44:07 -06:00
|
|
|
## Interval memoizers {#interval_memoizers}
|
|
|
|
|
|
|
|
|
|
`eval_interval` and `eval_full` expand every leaf in a range.
|
|
|
|
|
`make_basic_interval_memoizer<Key>(from, to)` stores two levels of that
|
|
|
|
|
range. That is the workspace the convenience overloads allocate.
|
|
|
|
|
`make_full_tree_interval_memoizer<Key>(from, to)` keeps every level.
|
|
|
|
|
`make_basic_full_memoizer<Key>()` and `make_full_tree_full_memoizer<Key>()`
|
|
|
|
|
are the same workspaces sized for the whole domain.
|
|
|
|
|
|
|
|
|
|
Size the memoizer for the widest interval you will pass to it. A wider
|
|
|
|
|
interval throws `std::length_error`. The same key and the same endpoints
|
|
|
|
|
leave the final interior level in place. A different key or a different
|
|
|
|
|
interval rebuilds into the same allocation.
|
|
|
|
|
|
|
|
|
|
Passing only the memoizer still allocates a fresh output buffer and returns
|
|
|
|
|
`std::pair(buffer, iterable)`.
|
|
|
|
|
|
|
|
|
|
## Sequence memoizers {#sequence_memoizers}
|
|
|
|
|
|
|
|
|
|
`make_sequence_recipe<Key>(begin, end)` compiles a sorted point list into a
|
|
|
|
|
traversal. The recipe depends on the input type, and one recipe serves every
|
|
|
|
|
key of that type.
|
|
|
|
|
|
|
|
|
|
A sequence memoizer stores a reference to the recipe object it was built
|
|
|
|
|
from and checks later calls by address. Pass that same object, and keep the
|
|
|
|
|
recipe alive for as long as the memoizer is used. A copy of the recipe
|
|
|
|
|
throws `std::logic_error`.
|
|
|
|
|
|
|
|
|
|
`make_double_space_sequence_memoizer<Key>(recipe)` keeps two levels. It is
|
|
|
|
|
what `eval_sequence(key, recipe, buffer)` allocates when you omit the
|
|
|
|
|
memoizer. `make_inplace_reversing_sequence_memoizer<Key>(recipe)` keeps one
|
|
|
|
|
level and reverses direction as it descends.
|
|
|
|
|
`make_full_tree_sequence_memoizer<Key>(recipe)` retains every level. A key
|
|
|
|
|
whose depth differs from the recipe throws `std::logic_error`.
|
|
|
|
|
|
|
|
|
|
# Output buffers {#output_buffers}
|
|
|
|
|
|
|
|
|
|
`output_buffer<T>` is move-only storage with `size`, iterators, `data`, and
|
|
|
|
|
`operator[]`. Build it with the factory that matches the evaluation:
|
|
|
|
|
|
|
|
|
|
- `make_output_buffer_for_interval(key, from, to)`
|
|
|
|
|
- `make_output_buffer_for_full(key)`
|
|
|
|
|
- `make_output_buffer_for_subsequence(key, begin, end, tag)`
|
|
|
|
|
- `make_output_buffer_for_recipe_subsequence(key, recipe, tag)`
|
|
|
|
|
|
|
|
|
|
On a `party_key`, leaf slots are `subtractive_share`s and comparison slots
|
|
|
|
|
are `additive_share`s. `dpf::bit`, `dpf::twobit`, and `dpf::nyble` slots are
|
|
|
|
|
packed. Trivially default-constructible slot types are left uninitialized;
|
|
|
|
|
the evaluation overwrites every slot it is responsible for.
|
2026-09-24 14:08:32 -06:00
|
|
|
|
2026-09-24 20:44:07 -06:00
|
|
|
`eval_interval` and recipe `eval_sequence` take the buffer as a non-const
|
|
|
|
|
reference, so the argument is a named object. The returned iterable refers
|
|
|
|
|
into that buffer. Read it while the buffer is alive, and only over the
|
|
|
|
|
points the iterable covers. The next evaluation overwrites those slots.
|
2026-09-24 14:08:32 -06:00
|
|
|
|
2026-09-24 20:44:07 -06:00
|
|
|
For one output, the convenience overload returns the buffer itself as the
|
|
|
|
|
first element of the pair. For several output indices it returns a tuple of
|
|
|
|
|
buffers.
|
2026-09-24 14:08:32 -06:00
|
|
|
|
2026-09-24 20:44:07 -06:00
|
|
|
`make_output_buffer(dpf::out<I>, key, from, to)` and
|
|
|
|
|
`make_output_buffer(dpf::cmp, key, n)` size a buffer for one channel of a
|
|
|
|
|
multi-output or comparison key. The slot types follow the same party-share
|
|
|
|
|
rule.
|
|
|
|
|
|
|
|
|
|
**Code samples**\n
|
|
|
|
|
<div class="tabbed">
|
|
|
|
|
|
|
|
|
|
- <b class="tab-title">output_buffers.cpp</b> \include{cpp} evaluation/output_buffers.cpp
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
# dpf::eval_point {#eval_point}
|
|
|
|
|
|
|
|
|
|
`eval_point(key, x)` evaluates output 0 at one input.
|
|
|
|
|
`eval_point<I>(key, x)` selects another output. Two or more indices,
|
|
|
|
|
`eval_point<0, 1>(key, x)`, return a tuple of shares rather than handles.
|
|
|
|
|
`eval_point(key, x, path)` continues a path memoizer.
|
|
|
|
|
|
|
|
|
|
`eval_point(dpf::out<I>, key, x, path)` and `eval_point(dpf::cmp, key, x, path)`
|
|
|
|
|
are the same walk with an explicit channel. Comparison results are additive
|
|
|
|
|
shares.
|
2026-09-24 14:08:32 -06:00
|
|
|
|
|
|
|
|
**Code samples**\n
|
|
|
|
|
<div class="tabbed">
|
|
|
|
|
|
|
|
|
|
- <b class="tab-title">eval_point.cpp</b> \include{cpp} evaluation/eval_point.cpp
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-09-24 20:44:07 -06:00
|
|
|
# dpf::eval_interval {#eval_interval}
|
2026-09-24 14:08:32 -06:00
|
|
|
|
2026-09-24 20:44:07 -06:00
|
|
|
`eval_interval(key, from, to)` evaluates every input from `from` through
|
|
|
|
|
`to`. The iterable yields one share per input, in that order. Optional
|
|
|
|
|
arguments are an output buffer and then an interval memoizer. An output
|
|
|
|
|
index pack, `eval_interval<0, 1>(key, from, to, buffers, memo)`, writes each
|
|
|
|
|
selected output.
|
2026-09-24 14:08:32 -06:00
|
|
|
|
|
|
|
|
**Code samples**\n
|
|
|
|
|
<div class="tabbed">
|
|
|
|
|
|
|
|
|
|
- <b class="tab-title">eval_interval.cpp</b> \include{cpp} evaluation/eval_interval.cpp
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-09-24 20:44:07 -06:00
|
|
|
# dpf::eval_full {#eval_full}
|
|
|
|
|
|
|
|
|
|
`eval_full(key)` is the closed interval from
|
|
|
|
|
`std::numeric_limits<Input>::min()` through `max()`. The buffer and
|
|
|
|
|
full-domain memoizer overloads match `eval_interval`.
|
|
|
|
|
`make_output_buffer_for_full(key)` and `make_basic_full_memoizer<Key>()`
|
|
|
|
|
size both for that domain.
|
2026-09-24 14:08:32 -06:00
|
|
|
|
|
|
|
|
**Code samples**\n
|
|
|
|
|
<div class="tabbed">
|
|
|
|
|
|
|
|
|
|
- <b class="tab-title">eval_full.cpp</b> \include{cpp} evaluation/eval_full.cpp
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-09-24 20:44:07 -06:00
|
|
|
# dpf::eval_sequence {#eval_sequence}
|
2026-09-24 14:08:32 -06:00
|
|
|
|
2026-09-24 20:44:07 -06:00
|
|
|
`eval_sequence(key, begin, end, tag)` evaluates a sorted list.
|
|
|
|
|
`dpf::return_output_only_tag_` stores one share per listed point.
|
|
|
|
|
`dpf::return_entire_node_tag_` stores whole leaves; it is the default.
|
|
|
|
|
The iterable still yields one share per listed point, in list order.
|
2026-09-24 14:08:32 -06:00
|
|
|
|
2026-09-24 20:44:07 -06:00
|
|
|
`eval_sequence(key, recipe, buffer, memo, tag)` repeats that list.
|
|
|
|
|
`memo` is a sequence memoizer bound to `recipe`. Omit `memo` to allocate a
|
|
|
|
|
`double_space` workspace for that call.
|
2026-09-24 14:08:32 -06:00
|
|
|
|
|
|
|
|
**Code samples**\n
|
|
|
|
|
<div class="tabbed">
|
|
|
|
|
|
|
|
|
|
- <b class="tab-title">eval_sequence.cpp</b> \include{cpp} evaluation/eval_sequence.cpp
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-09-24 20:44:07 -06:00
|
|
|
# Buffered PRG {#buffered_prg}
|
|
|
|
|
|
|
|
|
|
`dpf::randomness::buffered_prg<PRG, Ts...>` (alias
|
|
|
|
|
`dpf::randomness::aes_buffered_prg<Ts...>`) is a forward cursor with one
|
|
|
|
|
PRG stream per value type. `get<I>()` and `fill<I>(out, n)` consume the
|
|
|
|
|
cursor. `at<I>(index)` reads an absolute index and leaves the cursor where
|
|
|
|
|
it is. `sampled<I>()` is how far `get` and `fill` have advanced.
|
|
|
|
|
`per_stream_buffer_elems` is at least 1.
|
|
|
|
|
|
|
|
|
|
`dpf::randomness::lane_table<T>` is the seekable form for a runtime set of
|
|
|
|
|
roles. `value_at(role, index)` and `mask_at(role, index)` are independent
|
|
|
|
|
streams, and a repeated index returns the same element.
|
|
|
|
|
|
|
|
|
|
**Code samples**\n
|
|
|
|
|
<div class="tabbed">
|
|
|
|
|
|
|
|
|
|
- <b class="tab-title">buffered_prg.cpp</b> \include{cpp} evaluation/buffered_prg.cpp
|
|
|
|
|
|
|
|
|
|
</div>
|