# Basic Markdown Optimization Model

This model optimizes prices for N consecutive time periods 
each of which has its own demand function. It is assumed that 
the stock level of the product is limited and the goal
is to sell out the stock in time maximizing the revenue.

| Description | See [Introduction to Algorithmic Marketing](https://algorithmicweb.wordpress.com/ ) book |
|--|:--|
| Dataset | Generated internally, no external dependencies |
| Libs | Sympy, Numpy, Scikit-learn |


```python
%matplotlib inline
import sympy as sy
import numpy as np
from tabulate import tabulate
import matplotlib.pyplot as plt
from scipy.optimize import linprog

def tabprint(msg, A):
    print(msg)
    print(tabulate(A, tablefmt="fancy_grid"))
```


```python
plevels = [89, 79, 69, 59, 49] # allowed price levels
C = 700                        # stock level

price = sy.symbols("price")

def rectified(f):
    return sy.Piecewise( (0, f < 0), (f, True))

# Demand functions estimated for each week of the sale
demands = [rectified(1500 - 10*price), # week 1
           rectified(1300 - 15*price), # week 2
           rectified(1200 - 15*price), # week 3
           rectified(1100 - 18*price)] # week 4
```


```python
# Evaluate values of demand functions for each price level
D = np.array([[q.subs(price, p) for p in plevels] for q in demands])
tabprint("D =", D)

# Evaluate revenue for each demand function and each price level
R = np.array([[p*q.subs(price, p) for p in plevels] for q in demands])
tabprint("R =", R)
```

    D =
    ╒═════╤═════╤═════╤═════╤══════╕
    │ 610 │ 710 │ 810 │ 910 │ 1010 │
    ├─────┼─────┼─────┼─────┼──────┤
    │   0 │ 115 │ 265 │ 415 │  565 │
    ├─────┼─────┼─────┼─────┼──────┤
    │   0 │  15 │ 165 │ 315 │  465 │
    ├─────┼─────┼─────┼─────┼──────┤
    │   0 │   0 │   0 │  38 │  218 │
    ╘═════╧═════╧═════╧═════╧══════╛
    R =
    ╒═══════╤═══════╤═══════╤═══════╤═══════╕
    │ 54290 │ 56090 │ 55890 │ 53690 │ 49490 │
    ├───────┼───────┼───────┼───────┼───────┤
    │     0 │  9085 │ 18285 │ 24485 │ 27685 │
    ├───────┼───────┼───────┼───────┼───────┤
    │     0 │  1185 │ 11385 │ 18585 │ 22785 │
    ├───────┼───────┼───────┼───────┼───────┤
    │     0 │     0 │     0 │  2242 │ 10682 │
    ╘═══════╧═══════╧═══════╧═══════╧═══════╛



```python
# Now we solve the following optimization problem:
# (q is demand, P is price, T is the number of time periods, and K is the number of price levels)
```

$\text{max} \quad \sum_{t=1}^{T} \sum_{i=1}^{K} z_{it} \cdot P_{i} \cdot q\left(P_{i},t\right)$ 

$\text{subject to}$

$\quad \sum_{t=1}^{T} \sum_{i=1}^{K} z_{it} \cdot q\left(P_{i},t\right) \le C $

$\quad \sum_{i=1}^{K} z_{it} = 1, \quad \text{for}\ t=1,\ldots,T $

$\quad z_{it}\ge 0 $


```python
L = len(demands)*len(plevels)

# First, we generate a binary mask to ensure that all z's 
# in one time interval sum up to 1.0, that is z.M = B
M = np.array([[
    1 if i >= len(plevels)*j and i < len(plevels)*(j+1) else 0
    for i in range(L)
] for j in range(len(demands))])

tabprint("M = ", M)

B = [1 for i in range(len(demands))]

# Second, we ensure that the sum of all demands is less than the availbale stock level,
# that is z.Df <= C
Df = np.array(D).reshape(1, L)

res = linprog(-np.array(R).flatten(), 
              A_eq=M, 
              b_eq=B, 
              A_ub=Df, 
              b_ub=np.array([C]), 
              bounds=(0, None))

print("Revenue value: $", -res.fun)

# Each column of the solution matrix corresponds to a time period (one week).  
# Each row corresponds to z value that can be interpreted as the percentage 
# of time z't price level should be used in the corresponding time period. 
tabprint("Price schedule:", np.array(res.x).reshape(len(demands), len(plevels)).T)
```

    M = 
    ╒═══╤═══╤═══╤═══╤═══╤═══╤═══╤═══╤═══╤═══╤═══╤═══╤═══╤═══╤═══╤═══╤═══╤═══╤═══╤═══╕
    │ 1 │ 1 │ 1 │ 1 │ 1 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │
    ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤
    │ 0 │ 0 │ 0 │ 0 │ 0 │ 1 │ 1 │ 1 │ 1 │ 1 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │
    ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤
    │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 1 │ 1 │ 1 │ 1 │ 1 │ 0 │ 0 │ 0 │ 0 │ 0 │
    ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤
    │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 1 │ 1 │ 1 │ 1 │ 1 │
    ╘═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╛
    Revenue value: $ 61400.0
    Price schedule:
    ╒═══╤══════════╤═══╤═══╕
    │ 1 │ 0.217391 │ 1 │ 1 │
    ├───┼──────────┼───┼───┤
    │ 0 │ 0.782609 │ 0 │ 0 │
    ├───┼──────────┼───┼───┤
    │ 0 │ 0        │ 0 │ 0 │
    ├───┼──────────┼───┼───┤
    │ 0 │ 0        │ 0 │ 0 │
    ├───┼──────────┼───┼───┤
    │ 0 │ 0        │ 0 │ 0 │
    ╘═══╧══════════╧═══╧═══╛


3e7722ebf489ebfd8c67a3cea87250dea96ba009
ipynb
Jupyter Notebook
pricing/markdown-optimization.ipynb
mpamey/tensor-house
e2688fd523236ebce9f56c46e0389b816a6663b8
Apache-2.0
2020-06-05T17:43:21.000Z
2020-06-05T17:43:21.000Z
pricing/markdown-optimization.ipynb
DSivaji/tensor-house
b0afac992b39d31d51f43eb96b887b654d18c847
Apache-2.0
pricing/markdown-optimization.ipynb
DSivaji/tensor-house
b0afac992b39d31d51f43eb96b887b654d18c847
Apache-2.0
2020-05-23T04:10:41.000Z
2020-05-23T04:10:41.000Z
Qwen/Qwen-72B
1. YES
2. YES