This repository accompanies "Cascaded Bid Floors for Heavy-Tailed Mobile Ad
Auctions: A Deployed Production System". The experiments/ scripts
reproduce every simulator figure and table in the paper; the library
under src/floorgym/ is reusable for your own bid-floor research.
Requires Python 3.10+.
pip install -e .Run the test suite with pytest from the repo root.
import numpy as np
from floorgym import (
BidderPopulation, ContextualBuyer, FirstPriceWithFloor,
NoFloor, FixedFloor, SyntheticContextGenerator, evaluate,
)
pop = BidderPopulation(
[ContextualBuyer(base_mu=0.6, sigma=1.2) for _ in range(8)],
num_participants_per_round=8,
shading_factor=0.8,
)
ctx_gen = SyntheticContextGenerator()
mech = FirstPriceWithFloor()
stats = evaluate(
[NoFloor(), FixedFloor("Fixed $2", 2.0)],
pop, ctx_gen, mech,
regime="cascade_redraw",
rng=np.random.default_rng(0),
n_rounds=10_000,
)
for name, st in stats.items():
print(f"{name:>12s} RPI={st.revenue/st.n_rounds:.3f} fill={st.n_filled/st.n_rounds:.3f}")Top-level imports from floorgym:
| Name | Purpose |
|---|---|
Strategy |
@runtime_checkable Protocol defining set_floor(ctx, rng, *, values=None) -> float and an optional train(logs, rng). |
evaluate |
Run n_rounds paired auction rounds across a list of strategies. Takes an injectable AuctionMechanism and an optional context_hook. |
EvalStats, Regime |
Output dataclass and the literal type "single" | "cascade_redraw" | "cascade_redraw_same". |
BidderPopulation, ContextualBuyer, LogNormalBuyer |
The bidder model (paper Eq. 1–2). |
AuctionMechanism, FirstPriceWithFloor, SecondPriceWithFloor |
Auction resolution with floor enforcement. |
run_cascade_attempt |
The primary-then-safety cascade primitive (paper §3). |
ContextGenerator, SyntheticContextGenerator |
Synthetic impression-context distributions. |
ImpressionContext, AuctionResult |
Core dataclasses. |
OracleStrategy, bucket_key, hour_bucket |
Per-realization oracle (paper §3.2) and its bucketing helpers. |
NoFloor, FixedFloor, EmpiricalMyersonStrategy, AffineRevenueFormula, HierarchicalLookup, DecisionTreeCART, CausalGBDT, NoisyCPMPredictor |
Strategy roster. The paper's main table uses NoisyCPMPredictor, FixedFloor, and OracleStrategy; the rest are provided as additional reference baselines for extending the library. |
precompute_bucket_cpm_table |
Build the per-bucket E[CPM] table the noisy CPM predictor reads at inference. |
The paper's bidder calibration sits in floorgym.calibration.paper:
PAPER_PARAMS, build_population, build_context_generator,
patch_ru_with_prior_winning_bid, collect_warmup_logs.
A strategy is anything that quacks like the Strategy Protocol — a name
attribute, a set_floor(ctx, rng, *, values=None) -> float method, and a
no-op-friendly train(logs, rng). There is no base class to inherit:
isinstance(my_thing, Strategy) works on any duck that has those names.
from floorgym import Strategy
class MyStrategy:
name = "My Strategy"
def set_floor(self, ctx, rng, *, values=None):
return 0.5 * ctx.user_revenue_72h * 1000
def train(self, logs, rng):
pass
assert isinstance(MyStrategy(), Strategy)All experiment scripts run against the calibrated bidder model in
config/calibrated_buyers.json. Calibration parameters and sweep grids
are exposed as CLI flags with paper-matching defaults; pass --help to
each script for the full list. Seeds are fixed by default so re-runs are
deterministic.
| Paper artifact | Command | Output |
|---|---|---|
| Fig 1 — shading sweep | python experiments/shading_sweep.py |
results/shading_sweep/shading_sweep.{pdf,png,csv} |
| Fig 2 — sigma sweep | python experiments/sigma_sweep.py |
results/sigma_sweep/sigma_sweep.{pdf,png,csv} |
| Fig 3 — redraw noise | python experiments/redraw_noise.py |
results/redraw_noise/redraw_noise.{pdf,png,csv} |
Table 1 (tab:redraw) |
python experiments/noisy_cpm_benchmark.py |
results/noisy_cpm_benchmark/{table_cascade_redraw.csv, summary.md} |
End-to-end runtime is on the order of tens of minutes at default sample
sizes; pass smaller --*-rounds flags for a quick smoke run.
The three figure scripts write their PDFs with the filenames that
paper3.tex expects in its figures/ directory.
Not reproduced from FloorGym. The system-architecture figure
(figures/system_architecture.pdf in the paper repo) and the production
A/B-test table tab:format_lift come from the deployed system, not the
simulator.
src/floorgym/
__init__.py Public API re-exports
strategy.py Strategy Protocol — the public contract
evaluation.py EvalStats + evaluate() — the harness
bidder.py BidderPopulation + ContextualBuyer + LogNormalBuyer
auction.py First-price / second-price with floor enforcement
cascade.py run_cascade_attempt — paper's redraw cascade
context.py SyntheticContextGenerator (tier × hour × format)
impression.py ImpressionContext / AuctionResult dataclasses
oracle.py Per-realization OracleStrategy (paper §3.2)
buckets.py Shared (tier, hour, format) bucketing helpers
reporting.py build_table / write_summary
calibration/paper.py PAPER_PARAMS + build_population + helpers
strategies/
baselines.py NoFloor, FixedFloor
learned.py EmpiricalMyerson / AffineRevenueFormula / ...
cpm.py NoisyCPMPredictor + precompute_bucket_cpm_table
experiments/
noisy_cpm_benchmark.py Cascade-redraw with noisy CPM predictor (Table 1)
shading_sweep.py Shading sweep (Fig 1)
sigma_sweep.py Within-bucket noise sweep (Fig 2)
redraw_noise.py Redraw noise figure with log-normal overlay (Fig 3)
tests/ pytest suite (Protocol, cascade, oracle, evaluation, strategies)
config/
calibrated_buyers.json Paper Appendix B
Released under the MIT License.