Close Menu
AI News TodayAI News Today

    Subscribe to Updates

    Get the latest creative news from FooBar about art, design and business.

    What's Hot

    An unreleased Anthropic model made progress on one of math’s biggest unsolved problems

    Tracking extreme heat by the hour makes climate change seem even worse

    Apple could help you prove your iPhone photos aren’t deepfakes

    Facebook X (Twitter) Instagram
    • About Us
    • Contact Us
    Facebook X (Twitter) Instagram Pinterest Vimeo
    AI News TodayAI News Today
    • Home
    • AI News
    • AI Reviews
    • AI Tools
    • AI Tutorials
    • Chatbots
    • Free AI Tools
    • Artificial Intelligence
    AI News TodayAI News Today
    Home»AI Tools»The Budget Split That Explains Itself
    AI Tools

    The Budget Split That Explains Itself

    By No Comments7 Mins Read
    Share Facebook Twitter Pinterest LinkedIn Tumblr Reddit Telegram Email
    The Budget Split That Explains Itself
    Share
    Facebook Twitter LinkedIn Pinterest Email

    handed the entire £20,000 budget to a single channel. Add one rule that forced a little onto a second channel and it complied, but it also returned a quiet negative number beside that rule. The number meant the floor you set is working against you, and it said exactly what that costs.

    Many optimisation workflows stop at the allocation itself. The reason behind the split is already sitting in the math, for free, and the only thing standing between you and it is one modelling decision that looks completely harmless. This is about not throwing it away.

    Start with why the easy version fails. The obvious approach is to score each channel by return per pound and fund the winners. It makes a clean table and a plan nobody can use. Ranking assumes each choice stands alone, but budget allocation is one connected decision. Every pound you give one channel is a pound the others lose. Add a single rule, “keep at least £3,000 here,” and a sorted list has nothing to say.

    So it is a constrained optimisation problem, and Linear Programming is the standard tool for those. The reason to reach for it, though, is not the allocation. It is a byproduct almost everyone ignores. When a continuous LP finishes, it can also provide the dual value of its constraints: how much the objective would move if you loosened a rule by one unit. That byproduct is the explanation. And it is only valid while the model stays a continuous LP.

    Which is exactly where the harmless-looking decision comes in. A plain LP will dump the whole budget on the single best channel and starve the rest. The natural fix is an on/off switch, a binary “run this channel or not” variable. It solves the dumping. It also turns the model into a mixed-integer program, where the LP shadow prices that made the explanation possible are no longer directly available. You get the split and lose the reason.

    The way out is to make diversification without a switch. Slice each channel’s budget into bands, and make each band pay less than the one before it.

    from dataclasses import dataclass
    
    @dataclass
    class Platform:
        name: str
        productivity: float        # historical KPI per unit of spend
        min_spend: float = 0.0     # optional policy floor
    
    # Successive slices of the total budget, each earning a lower marginal yield.
    BRACKETS = [(0.25, 1.00), (0.35, 0.65), (0.40, 0.35)]  # (budget fraction, marginal yield)

    The first slice of a strong channel is worth a lot. Its third slice is worth less than the first slice of a weaker rival, so the optimiser spreads the money on its own. No binary variables, so the familiar LP shadow prices remain available. Diminishing returns become geometry instead of logic.

    Without diminishing returns, the LP concentrates the entire budget on the highest-performing channel. Adding decreasing marginal yields lets the same LP model diversify without introducing binary decisions.

    One caveat on productivity: it is just historical KPI per pound, an observed ratio, not a causal estimate. That keeps the data demand low enough for a small team, at the price of inheriting whatever bias is already in the numbers. A fair trade when it is a deliberate one.

    The model stays short. PuLP keeps it close to the plain description of the problem.

    import pulp
    
    def allocate(budget, platforms):
        model = pulp.LpProblem("budget_allocation", pulp.LpMaximize)
    
        slices = {}
        for p in platforms:
            slices[p.name] = [
                (pulp.LpVariable(f"x_{p.name}_b{i}", lowBound=0, upBound=frac * budget), y)
                for i, (frac, y) in enumerate(BRACKETS)
            ]
    
        model += pulp.lpSum(
            p.productivity * y * var for p in platforms for (var, y) in slices[p.name]
        )
    
        model += (pulp.lpSum(var for p in platforms for (var, _) in slices[p.name]) <= budget,
                  "total_budget")
        for p in platforms:
            if p.min_spend > 0:
                model += (pulp.lpSum(var for (var, _) in slices[p.name]) >= p.min_spend,
                          f"min_{p.name}")
    
        model.solve(pulp.PULP_CBC_CMD(msg=False))
        return model, slices

    One detail earns its keep at the end: every constraint has a name. An unnamed constraint still binds, it just cannot tell you afterwards that it did. Naming them is what lets the split talk back.

    And talking back is the whole point. Each named constraint reports two things: whether it is tight, and its shadow price.

    def interpret(model):
        binding, shadow = [], {}
        for name, con in model.constraints.items():
            shadow[name] = round(con.pi, 4) if con.pi is not None else None
            if con.slack is not None and abs(con.slack) < 1e-6:
                binding.append(name)
        return binding, shadow

    Read the signs and the plan starts speaking plainly. A binding budget cap is positive: another pound would earn this much more. A binding minimum-spend floor is negative: forcing money onto a weaker channel cost this much. A shadow price of zero is a rule you wrote down that never touched this decision.

    Watch it flip. Give it £20,000, two platforms, one lead-gen goal, and a £3,000 floor on each. LinkedIn turned £9,500 into 300 leads, Facebook £8,000 into 150.

    platforms = [
        Platform("linkedin", productivity=300/9500, min_spend=3000),
        Platform("facebook", productivity=150/8000, min_spend=3000),
    ]
    model, slices = allocate(20000, platforms)

    They are close, so the split settles near 60/40 toward LinkedIn. Only the budget binds, and both floors sit idle. Productivity drove it; no rule interfered.

    Now make LinkedIn clearly stronger, 380 leads on £9,500 against 30 on £4,200, and drop Facebook’s floor to £500. LinkedIn receives £19,500 while Facebook stays at exactly its £500 minimum. That floor now binds, with a negative shadow price. This is the number from the opening: the model, telling you your own safety rule has a cost, and printing it next to the plan instead of hiding it inside.

    From Optimiser to Decision Engine

    A thirty-line function is not yet a tool. The gap is filled by plainer layers: a check that catches contradictory inputs before the solver runs, since most “infeasible” results are really a floor set above a cap; a pass that re-solves under conservative, base, and optimistic assumptions to see if a recommendation holds up; a forecast that turns the split back into expected outcomes with an uncertainty band sized by how much history backs it; and a step that names the binding constraints and sets a safer, more diversified alternative beside the optimum with its efficiency cost stated. Keeping those apart is what keeps the answer honest, and keeps the explanation trustworthy.

    I built these ideas into an open-source project called CLARO (Constrained Linear Allocation and Resource Optimiser). It wraps the optimisation with scenario analysis, forecasting, and constraint interpretation while keeping the LP core reusable for other allocation problems. Everything in this article runs on plain PuLP; CLARO is simply one complete implementation.

    pip install claro-engine

    The package is available on PyPI, and the source code, tests, and examples are on GitHub.

    The on/off switch would have made the model quieter, and it was the one thing worth refusing. Refusing it is what let the plan keep explaining itself: which of your rules shaped it, and what each one cost. That holds anywhere a fixed pool gets split under rules that bite, whether the pool is a budget, a team, compute, or shelf space.

    Optimising a budget has become easy. Explaining the optimisation is still surprisingly rare. The explanation was already inside the optimisation model. The only trick was not throwing it away.

    budget explains split
    Share. Facebook Twitter Pinterest LinkedIn Tumblr Email
    Previous ArticleThinking of ACE? We Can Do It with Fewer Tokens
    Next Article New surveillance tech links your phone to your license plate
    • Website

    Related Posts

    AI Tools

    Should AI Developers Make the Switch from Polars to Pandas?

    AI Tools

    Can a Local LLM Run My AI Assistant?

    AI Tools

    How to Effectively Deploy Code With Claude Code

    Add A Comment
    Leave A Reply Cancel Reply

    Top Posts

    An unreleased Anthropic model made progress on one of math’s biggest unsolved problems

    0 Views

    Tracking extreme heat by the hour makes climate change seem even worse

    0 Views

    Apple could help you prove your iPhone photos aren’t deepfakes

    0 Views
    Stay In Touch
    • Facebook
    • YouTube
    • TikTok
    • WhatsApp
    • Twitter
    • Instagram
    Latest Reviews
    AI Tutorials

    Quantization from the ground up

    AI Tools

    David Sacks is done as AI czar — here’s what he’s doing instead

    AI Reviews

    Judge sides with Anthropic to temporarily block the Pentagon’s ban

    Subscribe to Updates

    Get the latest tech news from FooBar about tech, design and biz.

    Most Popular

    An unreleased Anthropic model made progress on one of math’s biggest unsolved problems

    0 Views

    Tracking extreme heat by the hour makes climate change seem even worse

    0 Views

    Apple could help you prove your iPhone photos aren’t deepfakes

    0 Views
    Our Picks

    Quantization from the ground up

    David Sacks is done as AI czar — here’s what he’s doing instead

    Judge sides with Anthropic to temporarily block the Pentagon’s ban

    Subscribe to Updates

    Get the latest creative news from FooBar about art, design and business.

    Facebook X (Twitter) Instagram Pinterest
    • About Us
    • Contact Us
    • Terms & Conditions
    • Privacy Policy
    • Disclaimer

    © 2026 ainewstoday.co. All rights reserved. Designed by DD.

    Type above and press Enter to search. Press Esc to cancel.