Advertising Is a Stock. Your Optimizer Solves a One-Period Problem.

Every marketing-mix model in production says the same thing about advertising: a dollar buys a decaying asset. That is what adstock is, a state variable that accumulates spend and leaks it back out at a rate the model estimates. A system with a state has a correct decision object, and that object is a policy over time. Then the optimizer runs, and it returns a vector: so much to search, so much to TV, over a window. The timing question is the one the state variable exists to answer, and it gets handed to a separate module offering a menu of hand-picked calendar shapes, none of which consults the model. This is not a philosophical complaint. It produces two defects you can quantify from a fit you already have. The first is a mechanical tax. Summing contribution over a window discards the carryover that lands past the window edge, and the size of the discard is exactly the kernel's mean lag divided by the window length: 22% for a six-week half-life over a quarter, against 5% for paid search. The tool used to set brand budgets deflates brand media, arithmetically, before anyone argues about it. The second defect is sharper, because whether flat spend or pulsing is optimal is decided by curvature the model already estimated, and the default prior on that curvature puts almost exactly half its mass on the side that makes your flat plan suboptimal.

The Model Has a State. The Decision Object Doesn't.

Write the model the way the code writes it. A channel's adstocked exposure at time \( t \) is a finite-impulse-response convolution of its own spend history, \( a_t = \sum_k w_k\, x_{t-k} \), and the KPI contribution is that exposure pushed through a saturation curve, \( \beta\, S(a_t) \). The weight vector \( w \) is the state-transition kernel: it is the entire content of the claim that advertising persists. For the geometric case the recursion fits on one line, \( a_t = x_t + \alpha a_{t-1} \), and the framework's own docstring spells out the consequence, that "the sum of the adstock weights is 1/(1-alpha), so the total effect of a unit spend is scaled by this factor." How those kernels and curves are parameterized, and how weakly they are identified, is its own subject. What follows takes the fitted objects as given and asks what decision they imply.

Now state the decision problem the model implies. The object of choice is a path \( x_1, \ldots, x_T \) that drives a stock whose current level determines this period's return, and a window total is only a summary of that path. Choosing a path is a control problem, and it has been a control problem in the marketing-science literature since 1962, when Nerlove and Arrow reframed advertising as investment in a depreciating goodwill stock rather than as a current-period expense. The fifteen years that followed produced enough work on it to require a survey. Sethi (1977) catalogues the goodwill-capital and sales-response families and their optimal policies across forty pages of SIAM Review, and essentially none of it is visible downstream of a modern MMM. That is the gap this post is about, and the gap is not in the literature.

Definition: goodwill stock

In the Nerlove–Arrow formulation, advertising at rate \( u(t) \) accumulates into a stock \( G(t) \) that depreciates continuously: \( \dot G = u - \delta G \). Demand depends on \( G \), not on current spend. Advertising is therefore capital expenditure, and the relevant question becomes "what stock level should I hold, and what flow holds it," in place of "what should I spend this quarter." The MMM's adstock kernel is a discrete-time version of exactly this object: \( \delta \) is the decay your model calls \( \alpha \), re-expressed as \( \delta = -\ln\alpha \) per period.

The Nerlove–Arrow steady state is a clean, closed-form benchmark. Hold the stock where the marginal profit it generates equals its user cost, the interest rate plus the depreciation rate:

$$ (p - c)\,\frac{\partial Q}{\partial G} \;=\; r + \delta. $$

The advertising rate that sustains it is \( u^\star = \delta G^\star \). Both inputs are things an MMM estimates: \( \partial Q/\partial G \) is the marginal response the optimizer already differentiates, and \( \delta \) is the adstock decay. The first cash-out goes against the argument I am building, which is why it goes first.

An MMM's marginal contribution per dollar sums the entire carryover tail with no discounting at all. Nerlove–Arrow says it should be discounted, by the factor \( \delta/(r+\delta) \). Put numbers on that. At a three-week half-life (retention 0.79, an ordinary paid-search or promo channel) the weekly depreciation rate is \( \delta = 0.23 \), and a 4% annual cost of capital is \( r = 0.00077 \) per week. The discounting term is 0.33% of the user cost. Push to a six-week half-life and a 15% annual hurdle rate and it reaches 2.4%. The dynamic correction the classical model supplies comes in smaller than the rounding on your ROI slide. Discounting is not why the one-period objective is wrong.

Deep diveWhy the discounting correction is negligible, and when it isn't

A dollar of advertising buys one unit of goodwill, which yields \( (p-c)\,\partial Q/\partial G \) of profit per period and decays at \( \delta \). Its present value is \( (p-c)(\partial Q/\partial G)/(r+\delta) \). Setting that equal to its price of one dollar gives the user-cost condition above. The MMM's per-dollar total effect, by contrast, is \( \beta \sum_k w_k \), an undiscounted sum over the kernel, or \( (\partial Q/\partial G)/\delta \) in continuous terms. The ratio of the two is \( (r+\delta)/\delta = 1 + r/\delta \), so the MMM overstates the discounted value of a dollar by \( r/\delta \).

With weekly data and half-lives in the range a weekly MMM can actually estimate, \( \delta \) is two to three orders of magnitude larger than a weekly discount rate, and the correction vanishes. The exception is instructive. If the asset you care about is brand equity depreciating over 18 months, \( \delta = \ln 2/78 = 0.0089 \) per week, and the same 4% annual cost of capital now accounts for 8% of the user cost, or 25% at a 15% hurdle rate. This framework's own documentation is explicit that a weekly model does not see that asset: adstock half-lives "are a few weeks; even a generous l_max rarely exceeds a quarter," so long-run brand equity "is absorbed into the intercept / trend / baseline, i.e. it is credited to 'base,' not to the channel that built it." The discounting correction is negligible for the \( \delta \) your model estimates. That is a statement about the model's horizon, not a reassurance about brand.

So the classical dynamics do not rescue the objective, and they do not condemn it either. Under a concave response, a steady-state marginal condition really is a good approximation to the level decision, which is what the optimizer computes. The failures are elsewhere, and there are exactly two: what the window-sum objective does to the edge of the window, and what it presumes about the shape of the path inside it. Both are checkable against your own fit.

The Window-Edge Tax

The objective this framework's optimizer maximizes is a sum. Response curves are built by scaling the historical spend matrix by a multiplier, evaluating the model's per-observation channel contributions, and summing over observations. That last step is contrib.sum(axis=1) in the code. The docstring calls it "total (window-summed) original-scale contribution," which is exactly right and exactly the problem.

A dollar spent in period \( s \) delivers its effect over lags \( 0, 1, 2, \ldots \) according to the kernel. If the window ends at \( T \), that dollar's effect is credited only for the lags that land inside it: it receives \( \sum_{k \le T-s} w_k \) of its own kernel, and the rest falls off the edge of the accounting. A dollar in the final week of the window is credited with \( w_0 \) alone. Channels therefore get ranked by the fraction of their productivity that happens to fit inside the reporting frame, which is a different quantity from the long-run productivity the comparison is supposed to be about.

The size of the shortfall has a clean closed form. Scale the whole window's spend uniformly (precisely what the response-curve sampler does), and for any causal kernel normalized to sum to one, with a window at least as long as the kernel:

$$ \text{fraction of long-run effect lost} \;=\; \frac{\bar{k}}{T}, \qquad \bar{k} \;=\; \sum_k k\, w_k, $$

where \( \bar{k} \) is the kernel's mean lag. Nothing about the functional form matters. Geometric, delayed-peak and Weibull kernels all obey it. The tax is the average delay divided by the window length.

Deep diveThe mean-lag identity

Let \( W(j) = \sum_{k \lt j} w_k \) be the kernel's cumulative weight through lag \( j-1 \), with \( W(L) = 1 \) for a kernel of length \( L \). Scaling every period's spend by the same factor, the extra window-summed contribution attributable to period \( s \) is proportional to \( W(T-s+1) \), so the average capture across the window is \( \frac{1}{T}\sum_{j=1}^{T} W(j) \) after re-indexing \( j = T-s \). The loss is therefore

$$ \frac{1}{T}\sum_{j=1}^{T}\bigl(1 - W(j)\bigr) \;=\; \frac{1}{T}\sum_{j=1}^{T}\ \sum_{k \ge j} w_k \;=\; \frac{1}{T}\sum_{k} k\,w_k, $$

because the weight \( w_k \) appears in exactly \( k \) of the inner tail sums (those with \( j \le k \)). For \( T \ge L \) the outer sum saturates at \( L \) and the result is \( \bar{k}/T \) exactly. When the window is shorter than the kernel the identity no longer holds. The truncated sum \( \frac{1}{T}\sum_{j=1}^{T}\mathrm{tail}(j) \) is smaller than \( \bar{k}/T \), and a dollar can never lose more than \( 1 - w_0 \), so the figure below computes the general sum rather than the shortcut.

Two consequences worth stating. First, the tax is linear in the mean lag, so it ranks channels by carryover length in the wrong direction with no threshold or discontinuity to hide behind. Second, it is bounded by the model's own lag window: the framework's default l_max = 8 caps the mean lag at a few weeks, which caps the tax at roughly a quarter. That bound holds only because the model was never allowed to see a longer tail in the first place. Raising l_max to 26 to capture real TV carryover raises the tax on a six-week-half-life channel to 46% of its long-run effect over a 13-week window.

What the window edge costs a slow channel

Share of a channel's long-run contribution that falls outside the optimization window, for three window lengths, as a function of adstock half-life. Geometric kernel normalized over l_max lags, the framework's construction. The reference marker is fixed at a 0.7-week half-life (retention 0.37, paid search), and the second marker follows the slider. Read the last readout as how much better at the margin the slow channel must genuinely be, just to tie the fast one on the optimizer's own scoreboard.

6.0
8
Slow channel, 13-week window
Slow channel, 52-week window
Search, 13-week window
Advantage needed to tie

At the defaults (a six-week half-life against search, l_max = 8, a 13-week planning window) the slow channel forfeits 22.3% of its long-run contribution and search forfeits 4.5%. A brand channel that is genuinely 23% more productive at the margin still loses the comparison. Stretch the window to a year and the gap collapses to 4.5 points. Open l_max to 26 to model real TV carryover and the 13-week tax reaches 46%, with the advantage needed to tie rising to 77%.

⚠️ This is not the brand-horizon problem

Under-crediting long-run brand equity because a weekly model cannot see a multi-quarter asset is a well-documented limitation, and this framework says so in its own docs. The window-edge tax is a second, independent deflation stacked on top of it, and unlike the first it is purely mechanical: the model has already estimated the tail, the tail is inside l_max, and the objective throws it away at the boundary. That makes it the rare defect that is fully fixable without new data. Credit each dollar with its whole kernel, or optimize a steady-state per-period rate instead of a window total. It also makes it worse, in the sense that matters: nobody caveats it, because nobody sees it.

Curvature Decides the Rhythm

The second defect concerns the shape of the path inside the window rather than its edge, and it is where the fitted model most directly contradicts the plan.

Sasieni (1971) established the result that makes flat plans respectable: under a response function with everywhere-decreasing marginal returns, a uniform advertising rate beats any cyclic policy of the same cost. That is Jensen's inequality with a state variable attached, and it is the implicit justification for every even-spend calendar in the industry. Mahajan and Muller (1986) established the other half. Working on awareness generation for new products, they showed that the uniform policy is optimal under a concave response and an advertising pulsing policy is optimal under an S-shaped one. An S-shaped curve has a convex region at low spend, where the marginal return is still increasing. In a convex region, concentrating spend beats spreading it: you would rather be well inside the productive zone half the time than marginally productive all of it.

Now look at what your model estimated. This framework's Hill saturation is \( f(a) = a^{s}/(a^{s} + h^{s}) \), and the exponent \( s \) is exactly the concave/S-shaped switch: for \( s \le 1 \) the curve is concave everywhere, and for \( s \gt 1 \) it has an inflection at \( h\left(\frac{s-1}{s+1}\right)^{1/s} \), convex below and concave above. The default prior on that exponent is HalfNormal(sigma=1.5), which places 50.5% of its mass above 1. The shipped default is, to a rounding error, a coin flip on whether the response function makes your flat plan suboptimal.

Selecting a Hill and fitting a posterior that concentrates above \( s = 1 \) is a substantive claim about the world: this channel has a minimum effective dose, and spreading spend below it wastes money. Nothing downstream reads that claim. The allocator's docstring states its own assumption plainly: greedy marginal allocation, "which is exact for concave (saturating) response curves." The constrained solver's says "convex: a sum of concave channel curves under linear constraints." Both are true statements about a premise the model in front of them can refute. Meanwhile the calendar module offers six named patterns: even, front_loaded, back_loaded, pulsed, seasonal, custom, which come to four fixed shapes and two pass-throughs for a vector you supply. The module concedes the gap in its own opening lines: "a planner can lay out a forward calendar before (or independent of) a fit. The model contributes the budgets, not the schedule shape." pulsed takes a pulse_on and a pulse_off and defaults both to 1. There is no criterion anywhere for choosing it.

The rhythm your curvature implies

One channel, a fixed annual budget, 52 weeks. Left: the fitted Hill curve, its inflection point, and the stretch of it the winning schedule actually visits. Right: the best periodic on/off block schedule found by search, against the framework's even and pulsed(1, 1) patterns. The operating level is set as a fraction of the half-saturation point, so 1.0 means the plan currently runs at \( h \). Search is over on/off block cycles that tile the year, subject to a cap on consecutive dark weeks.

2.00
2.00
0.50
4
Best schedule
KPI vs. even spend
pulsed(1, 1) vs. even
Verdict

At the defaults, the same annual budget re-timed to 9 weeks on / 4 weeks dark buys 3.6% more KPI. The shipped pulsed(1, 1) captures 0.5 points of that, roughly an eighth, because a one-week pulse is smoothed away by a two-week memory before it reaches the response curve. Two reversals to try. Drag \( s \) down to 1.4: the curve is still S-shaped, but the operating point has moved above the inflection, even spend becomes optimal, and pulsed now loses 0.55%. Raise the operating level to 1.0 and the same thing happens at \( s = 2 \). An S-shape is necessary for pulsing to pay, and on its own it is not enough. Where you sit on the curve decides it.

A taste menu cannot deliver any of what follows. Pulsing pays only when the operating point is on the convex side of the inflection, so the recommendation depends jointly on the fitted exponent and the current spend level. The optimal block length is set by the adstock memory, not by taste: short pulses against a long kernel are filtered into a flat line, which is why pulsed(1, 1) underperforms a 9-on/4-off block by seven-to-one at the defaults. And the sign of the recommendation can invert. A team that picks pulsed off a slide because pulsing sounds sophisticated destroys value whenever the fitted curvature says concave.

Which raises the stakes on a problem this blog has already documented. Curvature is the parameter an MMM identifies worst: on a smooth historical schedule the exponent is recovered with a spread wide enough that a re-run could land anywhere from "nearly linear" to "severely saturated." That post's conclusion was that you need designed multi-level variation to pin the shape down. This post supplies the reason the effort is worth paying for, and the payoff is something larger than a prettier ROI number. The curvature decides the rhythm of the plan, and the rhythm is worth a few percent of annual KPI at constant budget. The worst-identified parameter in the model is the one carrying the largest untaken decision.

💡 Pulsing hides the shape that justifies it

There is a trap in the loop. A pulsed schedule visits the extremes of the response curve and skips the middle, so the observed spend–response pairs sit at two levels and a straight line through two points cannot reveal an inflection. A channel that has been pulsed for years therefore looks close to linear in its own history, and the fit will not recover the S-shape that made pulsing correct. The pulsing literature has made this point since the 1980s. The version relevant here is narrower and it is mine: the same designed-variation program that identifies curvature (three or more distinct held levels, blocks at least as long as the adstock memory, both requirements stated in this framework's flighting_design docstring) is what breaks the circularity. You cannot infer the rhythm from a history generated by a rhythm.

The Optimum Nobody Can Run

The classical literature has to be handled carefully here, because its headline result arrives as a warning rather than a recipe. Feinberg (2001) took the pulsing question into continuous time and found that continuous-time models relying on strict response concavity "prescribe eventual spending at a constant rate," while under S-shaped response (the case where discrete analyses had suggested periodic optima) "continuous periodic optima cannot be supported." The continuum optimum degenerates: instead of a clean on/off cycle you get chattering, switching arbitrarily fast between extremes, which is not a media plan and not implementable by anything that buys inventory.

Feinberg's result condemns the continuous-time idealization rather than pulsing itself. The idealization is the wrong model of a media decision, and the discrete-time result is the one that applies. Media is bought in weeks or flights, the buying unit is real, and the minimum implementable block is set by the same adstock memory that the framework already computes to space sequential experiments. Its experiment planner derives a washout as "the smallest k with alpha**k < threshold" and notes that this "also sets the minimum flighting block length (a block shorter than the memory smears the contrast)." Constrain the schedule to implementable blocks and the pathology disappears, because chattering was an artifact of allowing infinitely fast switching. The figure above shows the same effect from the other side: push the dark-week cap to its maximum and the optimum becomes a 13-week blitz followed by a 13-week blackout, worth +16%, mathematically correct and commercially absurd. Constraining the schedule to implementable blocks is what makes the problem well-posed in the first place, and it gives up nothing that was ever available.

So the deliverable is a restricted, curvature-conditioned schedule: a block length, a duty cycle, a dark-week cap, and no Hamiltonian. That is a modest object, it is strictly more than a per-channel scalar plus a dropdown, and it is derivable from parameters already sitting in the posterior.

The Ratio Your Model Implies

One more classical result deserves to be cashed out, because it converts the optimizer's output into the units the person approving the budget actually thinks in. Dorfman and Steiner (1954) showed that a firm setting price and advertising jointly should hold its advertising-to-sales ratio at the advertising elasticity of demand divided by the absolute price elasticity. Equivalently: the optimal price pins the Lerner margin at the reciprocal of the price elasticity, so the target ratio is the elasticity times the price-cost margin.

The per-channel version is a two-line extension, and it is mine rather than theirs, so here is the derivation in full. With profit \( \pi = (p-c)\,Q(A_1,\ldots,A_C) - \sum_c A_c \), the first-order condition for channel \( c \) is \( (p-c)\,\partial Q/\partial A_c = 1 \). Multiply through by \( A_c/(pQ) \) and the advertising elasticity \( \eta_c = (\partial Q/\partial A_c)(A_c/Q) \) appears:

$$ \frac{A_c}{S} \;=\; m\,\eta_c, \qquad m = \frac{p-c}{p}, \quad S = pQ. $$

Every channel's share of sales should equal its elasticity times the margin. The assumptions are visible and restrictive: channels enter separably, the margin is constant, the interior optimum exists (so the curve is concave there), and the margin form presumes price is also set optimally.

This benchmark carries no new information, and that is the point of it. Rearranged, \( A_c/S = m\eta_c \) is identical to \( m \cdot \text{mROAS}_c = 1 \), the marginal-profit funding line the framework already solves for in its breakeven mode, whose gradient is literally -(value_per_kpi * marginals(s) - 1.0) and whose docstring says it "funds each channel until its marginal return hits the breakeven line." The condition is in the code. What is missing is that this mode is not the default (the default spends a fixed budget, reallocating rather than sizing), and that nobody prints the result as a ratio. That matters more than it should: the advertising-to-sales ratio is the one number a CFO already benchmarks against a category norm, and stating the model's implication in that unit turns "trust our optimizer" into a falsifiable claim about the firm's own P&L.

Dorfman–Steiner, per channel

A six-channel portfolio with fitted saturating curves. The horizontal axis is each channel's Dorfman–Steiner benchmark share of sales, \( m\,\eta_c \), computed from its marginal response at current spend. The vertical axis is the share it actually gets. The diagonal is the optimum, so a channel above the line is over-funded at the margin and one below it is under-funded. Both axes are logarithmic. Slide the contribution margin and watch channels change sides.

0.45
Actual advertising / sales
Dorfman–Steiner benchmark
Marginal profit per $1
Under-funded channels

At a 45% margin this plan spends 6.93% of sales on media where the fitted elasticities justify 6.20%. The spend-weighted marginal profit is $0.89 per dollar, so the portfolio is over-funded at the margin by roughly a tenth. But the aggregate hides the decision: retail media is under-funded and should be scaled, while print sits 3.7× above its benchmark. Raise the margin past 50.3% and the aggregate flips to under-funded while print stays more than three times over. A media plan is not separable from the P&L, which is why the margin belongs in the optimizer rather than in a footnote.

Not the Optimizer's Curse

There is a well-known family of critiques of budget optimizers that this post is deliberately not making, and the distinction is worth being explicit about because the two get conflated and the remedies are completely different.

That family attacks the statistics of the argmax. Optimize over noisy estimates and the winner is selected partly for being lucky, so the plan's projected uplift is biased upward and its composition is unstable across posterior draws. Call it the winner's curse applied to allocation instead of to coefficients. This framework takes that seriously: it re-optimizes per posterior draw to turn parameter uncertainty into decision uncertainty, reports the probability that the plan beats the current allocation, and prices the expected KPI "left on the table by committing to this single plan under parameter uncertainty." Those are the right instruments, and they all accept the objective and question the estimate.

This post questions the objective. Every claim above survives handing the optimizer the true response surface with zero uncertainty. Give me the exact kernel and the exact saturation curve and a window-summed objective still under-credits long-carryover channels by the mean lag over the window length, and still returns a flat plan when the true curvature makes a pulsed one better. Perfect knowledge does not fix a misspecified decision problem. The two critiques stack: you can have a well-calibrated posterior, honest decision-uncertainty reporting, and a plan that is confidently optimal for a question nobody asked.

What This Does Not Establish

It does not establish that your channel should be pulsed. The switch is the fitted curvature, and that is the quantity a conventional MMM estimates least reliably. On smooth historical spend the exponent's posterior can straddle 1 with room to spare, in which case the honest output is a schedule recommendation reported with its posterior, and often the finding that the data cannot tell. A point-estimate exponent of 1.4 is not a mandate to go dark for four weeks.

It does not deliver an optimal control solution. The figure searches a deliberately restricted family (periodic on/off blocks that tile a year, under a dark-week cap) rather than the space of all schedules, and the true continuous-time optimum under S-shaped response does not exist in implementable form. Treat the block schedule as a better-than-flat heuristic conditioned on the fit, not as an optimum.

The window-edge tax is a property of the objective, not evidence about the world. It says the optimizer discards tail effect the model already estimated inside its lag window. It does not say the true tail is longer than l_max, and it emphatically does not license inflating brand's ROI by 22%. If the kernel is truncated at eight lags, effect beyond eight lags was never in the model to begin with, and that is a separate, harder problem about horizons and identification.

The Dorfman–Steiner benchmark is a first-order condition evaluated at the current point, not a solution. It presumes separable channels with no cross-effects, a constant margin, an interior concave optimum, and (for the margin form) that price is optimally set. Read it as a directional gauge and a communication device, not as a target to solve for.

Finally, all of the classical results cited here are monopoly results. Competitive response changes them, sometimes qualitatively, and none of the arithmetic in this post accounts for a rival reacting to your dark weeks.

A Schedule, Not a Vector

The fix is unglamorous and mostly a matter of reading parameters the posterior already contains. Four changes, in rising order of effort.

Credit the whole kernel. Either optimize a steady-state per-period rate rather than a window total, or weight each period's incremental contribution by the kernel mass that falls outside the window and add it back. Both are arithmetic on the fitted weights and neither requires a new posterior pass. Until one of them ships, report the tax: two numbers per channel, the mean lag and the window length, and their ratio.

Print the curvature verdict next to the plan. The posterior for the Hill exponent, the inflection point in spend units, the current operating point relative to it, and the resulting recommendation: even spend, or a block schedule with a stated duty cycle. Concave posterior and flat plan is a consistent answer worth stating. S-shaped posterior and flat plan is an unexamined contradiction.

Set block length from the memory, not from taste. The washout calculation already exists for experiment planning. Reuse it: minimum on-block and off-block at least the channel's memory, or the pulse is filtered away before it reaches the response curve.

Print Dorfman–Steiner as a sanity check. Actual advertising-to-sales against elasticity times margin, per channel and in total. It costs one line of arithmetic on numbers the optimizer already has, it states the model's recommendation in the unit the budget is approved in, and when it disagrees violently with a category benchmark, that disagreement is a finding rather than an embarrassment.

None of this is a research program. It is the observation that a model with a state variable was asked a question with no time in it, and that the answer has been quietly deflating the channels whose whole point is that they keep working after the flight ends.

Takeaways

  • Adstock makes an MMM a dynamic system with a state, so its decision object is a policy over time. Optimizers return a per-channel scalar and delegate timing to a pattern menu that never reads the fit. In the framework's own words, "the model contributes the budgets, not the schedule shape."
  • Nerlove–Arrow's dynamic correction is numerically negligible at MMM horizons: the discounting term is 0.3–2.4% of goodwill's user cost \( r+\delta \) for any plausible hurdle rate and any half-life a weekly model estimates. Discounting is not the problem. It becomes one only for the 18-month brand asset the model cannot see.
  • A window-summed objective credits a dollar with only the kernel mass landing inside the window. The loss is exactly the kernel's mean lag divided by the window length: 22% for a six-week half-life over 13 weeks against 5% for search, so brand must be 23% better at the margin just to tie. 46% if l_max is opened to 26 for real TV carryover.
  • Uniform spend is optimal under a concave response (Sasieni) and pulsing under an S-shaped one (Mahajan–Muller). The Hill exponent is that switch, and its default prior HalfNormal(1.5) puts 50.5% of its mass on \( s \gt 1 \). That is a coin flip on whether your flat plan is suboptimal, checked by nothing downstream.
  • An S-shape is necessary but not sufficient: pulsing pays only when the operating point sits on the convex side of the inflection. At the figure's defaults, re-timing a fixed annual budget to 9 weeks on / 4 dark buys 3.6% more KPI, while the shipped pulsed(1, 1) captures an eighth of it and loses money when the curvature says concave.
  • The continuous-time optimum under S-shaped response chatters and cannot be implemented (Feinberg). The honest deliverable is a constrained block schedule with block length set by the adstock memory, not a Hamiltonian.
  • Dorfman–Steiner's \( A_c/S = m\,\eta_c \) is the framework's own breakeven funding line in share-of-sales units. It adds no information and enormous legibility: it states the model's recommendation in the ratio a CFO already benchmarks.
  • This is not the optimizer's curse. Every defect here survives an exactly known response surface, so decision-uncertainty reporting (per-draw re-optimization, expected regret, EVPI) cannot detect it. Those tools question the estimate. This post questions the objective.

References