Optimization (opt.py)

This module provides functions for optimizing marketing budget allocation across different channels based on estimated contribution curves (response curves). It supports both Michaelis-Menten and Sigmoid saturation functions.

Functions

calculate_expected_contribution

def calculate_expected_contribution(
    method: str,
    parameters: Dict[str, Tuple[float, float]],
    budget: Dict[str, float],
) -> Dict[str, float]:

Calculates the expected contribution for each channel given a specific budget allocation and a chosen response curve model (method).

Parameters:

  • method (str): The response curve model to use. Must be either "michaelis-menten" or "sigmoid".

  • parameters (Dict[str, Tuple[float, float]]): Dictionary containing the parameters for the chosen method for each channel.

    • For "michaelis-menten": {channel: (L, k)} where L is max contribution and k is the half-saturation point.

    • For "sigmoid": {channel: (alpha, lam)} where alpha controls slope and lam is the transition point. (Uses abacus.core.utils.sigmoid_saturation).

  • budget (Dict[str, float]): Dictionary mapping each channel name to its allocated budget.

Returns:

  • Dict[str, float]: A dictionary mapping each channel name to its calculated contribution, plus a "total" key with the sum of contributions across all channels.

Raises:

  • ValueError: If method is not "michaelis-menten" or "sigmoid".

objective_distribution

def objective_distribution(
    x: List[float],
    method: str,
    channels: List[str],
    parameters: Dict[str, Tuple[float, float]],
) -> float:

Calculates the negative total expected contribution for a given budget distribution x. This function serves as the objective function to be minimized by the optimization algorithm (minimizing the negative contribution maximizes the actual contribution).

Parameters:

  • x (List[float]): A list representing the proposed budget for each channel, in the same order as channels.

  • method (str): The response curve model ("michaelis-menten" or "sigmoid").

  • channels (List[str]): List of channel names corresponding to the budgets in x.

  • parameters (Dict[str, Tuple[float, float]]): Dictionary of response curve parameters for each channel (same format as calculate_expected_contribution).

Returns:

  • float: The negative of the total expected contribution for the budget distribution x.

Raises:

  • ValueError: If method is not "michaelis-menten" or "sigmoid".

optimize_budget_distribution

def optimize_budget_distribution(
    method: str,
    total_budget: Union[int, float],
    budget_ranges: Optional[Dict[str, Tuple[float, float]]],
    parameters: Dict[str, Tuple[float, float]],
    channels: List[str],
) -> Dict[str, float]:

Finds the optimal budget allocation across the specified channels that maximizes the total expected contribution, given a total_budget and optional budget_ranges for each channel. Uses the SLSQP (Sequential Least Squares Quadratic Programming) optimization algorithm via scipy.optimize.minimize.

Constraints:

  1. The sum of allocated budgets must equal total_budget.

  2. The budget for each channel must be within the bounds specified in budget_ranges (or between 0 and L for Michaelis-Menten / total_budget if budget_ranges is None).

Parameters:

  • method (str): The response curve model ("michaelis-menten" or "sigmoid").

  • total_budget (Union[int, float]): The total amount of budget to distribute.

  • budget_ranges (Optional[Dict[str, Tuple[float, float]]]): Optional dictionary specifying the (min_budget, max_budget) tuple for each channel. If None, defaults are applied (0 to L or total_budget).

  • parameters (Dict[str, Tuple[float, float]]): Dictionary of response curve parameters for each channel.

  • channels (List[str]): List of channel names to include in the optimization.

Returns:

  • Dict[str, float]: A dictionary mapping each channel name to its optimal budget allocation.

Raises:

  • TypeError: If budget_ranges is provided but is not a dictionary.

budget_allocator

def budget_allocator(
    method: str,
    total_budget: Union[int, float],
    channels: List[str],
    parameters: Dict[str, Tuple[float, float]],
    budget_ranges: Optional[Dict[str, Tuple[float, float]]],
) -> pd.DataFrame:

A convenient wrapper function that performs budget optimization and calculates the expected contribution for the optimal allocation.

  1. Calls optimize_budget_distribution to find the optimal budget split.

  2. Calls calculate_expected_contribution using the optimal budget.

  3. Returns the results in a pandas DataFrame.

Parameters:

  • method (str): The response curve model ("michaelis-menten" or "sigmoid").

  • total_budget (Union[int, float]): The total budget to allocate.

  • channels (List[str]): List of channel names.

  • parameters (Dict[str, Tuple[float, float]]): Dictionary of response curve parameters.

  • budget_ranges (Optional[Dict[str, Tuple[float, float]]]): Optional dictionary specifying budget bounds for each channel.

Returns:

  • pd.DataFrame: A DataFrame with channels (plus “total”) as the index and columns “estimated_contribution” and “optimal_budget”.