Chapter 10 of 10
Budget allocation
Reallocation against the stored posterior, reporting the probability a plan wins rather than a single optimistic number.
Given a budget and per-channel bounds, find the allocation that maximizes expected incremental outcome under the posterior:
maximize (1/S) * sum_s sum_m f_m(x_m ; theta_s)
subject to sum_m x_m <= B
lower_m <= x_m <= upper_mThe objective is evaluated per draw and averaged last. This is not a performance detail: the response is nonlinear in the parameters, so optimizing at the posterior mean is a different problem with a different answer. Averaging the curve is not the same as the curve through the average.
Reporting a plan, not a number
A single expected lift is the wrong output. The optimizer also reports the share of posterior draws under which the proposed plan actually beats current spend — because a 12% expected gain that holds in 55% of draws is a completely different recommendation from one that holds in 99%, and reporting only the mean makes them look identical.
Bounds default to 0.7× to 1.3× of observed spend. That is not timidity: outside the historical support the saturation curve is extrapolation, and an optimizer allowed to propose a channel’s spend at 5× its observed maximum is reading a region of the curve the data never visited.
source · engine/mmm/optimize.py
Two-stage solver
A greedy marginal-return hill-climb runs first on a discrete spend grid. For a separable objective with concave per-channel terms, always handing the next budget increment to the channel with the highest marginal return is globally optimal — this is not a heuristic under those conditions.
SLSQP then polishes with analytic gradients from JAX autodiff. The polish matters because the grid is discrete; the greedy stage matters because SLSQP is a local method and needs a starting point in the right basin. When the risk term is active the objective stops being separable, so the greedy pass runs at kappa = 0 and SLSQP carries the risk term forward from there.
greedy marginal-return allocation over 81 grid points polish SLSQP · maxiter 200 · ftol 1e-9 · JAX gradients guard revert to greedy if the polish is worse or infeasible
At the optimum, channels not pinned to a bound have equal marginal ROI. That common value is the shadow price of the budget constraint — the return on the next dollar of total budget — and it is the single most useful number the optimizer produces.
The optimizer inherits every assumption above it