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{split} \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} \end{split}\]

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)