Compare¶
Four helpers for questions that sit next to a decision rather than
inside it: did the distribution of outcomes move between two periods
(compare_outcome_laws), could an A/B split have happened by chance
(sample_two_row), which single noisy feature best predicts a target
(best_coordinate), and when to accept an item from a stream you cannot
go back to (single_item_max_of_samples, single_item_one_sample).
None of them is called by Module.decide.
Public here: compare_outcome_laws, sample_two_row,
best_coordinate, single_item_max_of_samples,
single_item_one_sample (top level); w2_1d, sample_switch and
apply_coordinate from the submodules. Recipes:
Compare outcomes and features. Read
the honest limits on each helper's theory page before quoting a factor
(for example, factor_claimed=0.5 is a guarantee in expectation only
for single_item_max_of_samples, under its stated assumptions; the
scalar-bar rule reports None: prophet inequalities).
sample_two_row and sample_switch take limits= and raise
ValueError above max_table_cells_exact and max_switch_steps.
courtade_kumar ¶
Courtade–Kumar 1-bit summaries (family 119, Lean).
After independent bit-flip noise on uniform bits, a coordinate (or its complement) retains the most mutual information. This module returns that coordinate instead of training a 1-bit bottleneck.
CoordinateSelection ¶
Bases: TypedDict
Result of :func:best_coordinate.
best_coordinate ¶
best_coordinate(
bits: ArrayLike, target: ArrayLike
) -> CoordinateSelection
bits shape (n_samples, d) in {0,1}. target length n_samples.
Source code in src/grounded_decision/compare/courtade_kumar.py
ot_stability ¶
Wasserstein-2 comparison with the 1/3 Brenier stability (family 374).
Lean: ‖T_μ − T_ν‖_{L²} ≤ C W₂(μ,ν)^{1/3}, and every larger exponent fails. To keep map error ≤ τ, require W₂ ≲ τ³ (C is absorbed into a user scale).
OutcomeLawComparison ¶
Bases: TypedDict
Result of :func:compare_outcome_laws.
w2_1d ¶
Exact W₂ between two empirical 1-D distributions of any sizes.
In one dimension the optimal transport plan matches quantiles. With
sample sizes n and m, both empirical quantile functions are
step functions; this integrates the squared difference exactly over
the merged breakpoints k/n and j/m. Every sample counts: no
tail is dropped when the sizes differ.
Source code in src/grounded_decision/compare/ot_stability.py
map_tolerance ¶
Largest W₂ compatible with Brenier-map error τ under exponent 1/3.
tables ¶
Contingency-table sampling (family 115).
Exact sampling for 2-row tables via sequential hypergeometric draws. Those draws follow the hypergeometric (Fisher exact test) distribution on tables with the given margins, which is what a permutation test needs. It is not the uniform distribution on those tables. General tables: 2×2 switch chain, which is symmetric on the fiber and therefore uniform in stationarity. This is not the paper FPRAS; the mixer_status field says so.
TableSampleDict ¶
Bases: TypedDict
Serialized form of :class:TableSample.
sample_two_row ¶
sample_two_row(
col_sums: Sequence[int],
row0: int,
*,
rng: Generator | None = None,
limits: Limits = DEFAULT_LIMITS,
) -> TableSample
Draw one 2-row table with the given margins, exactly (hypergeometric).
Raises:
| Type | Description |
|---|---|
ValueError
|
|
Source code in src/grounded_decision/compare/tables.py
sample_switch ¶
sample_switch(
row_sums: Sequence[int],
col_sums: Sequence[int],
*,
steps: int = 2000,
rng: Generator | None = None,
limits: Limits = DEFAULT_LIMITS,
) -> TableSample
Run the 2×2 switch chain from a feasible table with the given margins.
Raises:
| Type | Description |
|---|---|
ValueError
|
The margins disagree, or |
Source code in src/grounded_decision/compare/tables.py
prophet ¶
Single-item prophet rules: pick one arrival online, without hindsight.
Values arrive one at a time; each must be accepted or rejected on the spot, and at most one can be accepted. The "prophet" sees every value in advance and takes the largest. Two threshold rules are provided:
- :func:
single_item_max_of_samples- the bar is the largest of one past sample per arrival. This rule has a proven guarantee (Rubinstein, Wang and Weinberg, "Optimal single-choice prophet inequalities from samples", ITCS 2020): its expected value is at least half the prophet's expected value.factor_claimedis0.5. - :func:
single_item_one_sample- the bar is one scalar you choose (for example yesterday's median). For an arbitrary scalar bar there is no worst-case guarantee, sofactor_claimedisNoneand theguaranteefield says so. Measure the ratio on your own traffic.
The catalog's Lean-verified factor for the more general matroid setting
(family 111) is 2^{-310}: true, and not a usable product factor. It is
carried on every result as lean_factor so you can see it and leave it
out of any SLO.
Why the max-of-samples bar earns 1/2¶
Assumptions: values X_1..X_n are independent and non-negative;
S_i is one independent draw from the same distribution as X_i
(for example yesterday's value at the same slot); the arrival order is
fixed in advance; values have no ties (continuous distributions, or
break ties with a tiny random perturbation). The rule accepts the first
X_i >= T where T = max_i S_i.
Proof. Pool the 2n numbers and sort them, Z_1 > Z_2 > ... > Z_2n.
Condition on the unordered pairs {X_i, S_i}. Because X_i and
S_i are independent and identically distributed, which member of each
pair is the value is a fair coin, independent across pairs. Let f be
the position of the first sample in the sorted order. The values above
T = Z_f are exactly Z_1..Z_{f-1}, so when f >= 2 the rule
accepts one of them and earns at least Z_{f-1}; when f = 1 it
earns at least 0. The prophet earns Z_v, v the position of the
first value. Writing Z_k = sum_{j>=k} (Z_j - Z_{j+1}) (with
Z_{2n+1} = 0), it suffices to show for every j:
P(2 <= f <= j + 1) >= P(v <= j) / 2.
- If positions
1..jholdjdifferent pairs,P(v <= j) = 1 - 2^{-j}(some value among them), andP(2 <= f <= j + 1) = P(Z_1 is a value) - P(Z_1..Z_{j+1} are all values) >= 1/2 - 2^{-(j+1)}, which is exactly half. - Otherwise two of positions
1..jform a pair, so one is a value:P(v <= j) = 1; andZ_1..Z_{j+1}cannot all be values, soP(2 <= f <= j + 1) = P(Z_1 is a value) = 1/2.
Summing over j gives E[rule] >= E[Z_{f-1}; f >= 2] >= E[max X] / 2.
If no value reaches the bar the rule takes the last arrival; with
non-negative values that can only add. The factor cannot be improved:
no online rule beats 1/2 in the worst case, even one that knows every
distribution exactly.
LEAN_FACTOR
module-attribute
¶
Catalog family 111's Lean-verified factor; reported, never promised.
ProphetChoice ¶
Bases: TypedDict
Result of :func:single_item_max_of_samples or :func:single_item_one_sample.
index and value are None only when values is empty.
factor_claimed is the guaranteed fraction of the prophet's expected
value, or None when the rule has no guarantee; guarantee says
in words what is (or is not) promised and under which assumptions.
single_item_max_of_samples ¶
single_item_max_of_samples(
values: Sequence[float], samples: Sequence[float]
) -> ProphetChoice
Take the first value that reaches the largest past sample.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
values
|
Sequence[float]
|
Today's arrivals, in arrival order (non-negative). |
required |
samples
|
Sequence[float]
|
One past draw per arrival, from the same distribution as
that arrival (for example yesterday's value at the same slot).
Must have the same length as |
required |
Returns:
| Type | Description |
|---|---|
ProphetChoice
|
The choice, with |
ProphetChoice
|
earns at least half of what an all-knowing prophet would. The proof |
ProphetChoice
|
and its assumptions are in this module's docstring. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/grounded_decision/compare/prophet.py
single_item_one_sample ¶
single_item_one_sample(
sample: float, values: Sequence[float]
) -> ProphetChoice
Irrevocable online choice: take the first value that reaches sample.
sample is one scalar bar. For an arbitrary bar there is no
worst-case guarantee, so factor_claimed is None. When you have
one past sample per arrival, use :func:single_item_max_of_samples,
which guarantees half the prophet's expected value.