Pricing and target-market

pricing_and_target_market.ipynb Open In Colab Kaggle Gradient Open In SageMaker Studio Lab Hits

Description: Formulate a pricing optimization and target-market problem as a MILP.

Tags: industry, pricing, milp, mip

Notebook author: Gyorgy Matyasfalvi <gyorgy@ampl.com>

Model author: Erwin Kalvelagen

References:

  1. https://stackoverflow.com/questions/60669893/formulating-pricing-optimization-as-milp

# Install dependencies
%pip install -q amplpy
# Google Colab & Kaggle integration
from amplpy import AMPL, ampl_notebook

ampl = ampl_notebook(
    modules=["cplex"],  # modules to install
    license_uuid="default",  # license to use
)  # instantiate AMPL object and register magics

Problem formulation

We wish to find the optimal combination of a fixed fee $F$ and variable price $p$ for a product.

We have $N$ types of clients who each want to purchase quantity $q_i (i \in N)$ for which they are willing to pay a total price of $w_i (i \in N)$.

Our objective is to pick $F$ and $p$ such that we maximize revenue.

We can describe the above problem as a MILP using the following formulation:

$ \begin{align} \textrm{Objective:} & \ & \max \sum_{i \in N} y_i \ \textrm{Subject to:} & \ s_i & = 1 \Rightarrow y_i = F+p*q_i \ s_i & = 0 \Rightarrow y_i = 0 \ s_i & \in {0, 1} \ p & \geq 0 \ F & \geq 0 \ y_i &\in [0, w_i] \end{align} $

Above we introduced binary variables $s_i (i \in N)$, which indicate whether we can sell our product to client type $i$ (this will reveal our target market), along with defined variables $y_i (i \in N)$ indicating the total revenue received from client $i$.

Use amplpy to solve our pricing model

Predict customer behavior

# Load modules
from amplpy import AMPL


# Define dummy prediction functions (in reality these will be a sophisticated ML method)
def client_type():
    return ["genx", "geny", "genz"]


def quantity_pred(client):
    if client == "genx":
        return 5
    if client == "geny":
        return 10
    if client == "genz":
        return 20


def wpay_pred(client):
    if client == "genx":
        return 40
    if client == "geny":
        return 45
    if client == "genz":
        return 25

Data prep

# Generate dictionaries from predictions (to be loaded as AMPL data later)
quant_dict = {i: quantity_pred(i) for i in client_type()}
wpay_dict = {i: wpay_pred(i) for i in client_type()}

Load AMPL model

# Define AMPL model
ampl = AMPL()
ampl.eval(
    r"""
    set N;                       # Types of clients
    param q{N};                  # Purchase quantity per client type (e.g. GB of data)
    param w{N};                  # Client's willingness to pay (e.g. USD per month)
    var p >= 0;                  # Variable price (e.g. USD per GB of data)
    var F >= 0;                  # Fixed fee (e.g. USD per month)
    var s{i in N} binary;        # Ability to sell to client type product
    var y{i in N} >=0, <= w[i];  # Revenue received from client

    maximize total_revenue:
         sum {i in N} y[i];

    subject to ic_sell {i in N}:
         s[i] = 1 ==> y[i] = F + p*q[i];
    subject to ic_no_sell {i in N}:
         s[i] = 0 ==> y[i] = 0;
    """
)

Load data

# Load data into AMPL model
ampl.set["N"] = client_type()
ampl.param["q"] = quant_dict
ampl.param["w"] = wpay_dict

Solve problem and display solution

# Set solver to one that can handle logical constraints and MILPs
ampl.option["solver"] = "cplex"
# Solve our problem
ampl.solve()

# Print results
tr = ampl.get_objective("total_revenue")
print("\n")
print("Total revenue is:", tr.get().value(), "\n")
tm_df = ampl.get_variable("s").to_pandas()
print("Target market:")
tm_sol = tm_df.loc[tm_df["s.val"] == 1]
print(tm_sol)