Mathematical Model Description

We model firms as having two characteristics:

An AI tool with effectiveness \(\omega \in (0, 1)\) reduces labor demand to:

\[ \text{Effective headcount} = n(1 - \omega v) \]

The value (cost savings) for the firm is:

\[ \text{Value} = c \cdot n \cdot \omega v \]

The cost (price paid) to the vendor is:

\[ \text{Price paid} = p \cdot n \cdot (1 - \omega v) \]

The vendor also incurs a support cost:

\[ \text{Cost to vendor} = w \cdot n \cdot (1 - \omega v) \]

Participation Condition

A firm adopts the AI tool if:

\[ c n \omega v > p n (1 - \omega v) \quad \Rightarrow \quad v > \frac{1}{\omega} \frac{p}{c + p} \]

Let \(v^* = \frac{1}{\omega} \frac{p}{c + p}\) denote the threshold above which firms adopt.

Payment Function for Buyer Firm

Given \((n, v)\), if \(v > v^*\), the total payment by the firm is:

\[ \text{Payment}(n,v) = p \cdot n \cdot (1 - \omega v) \]

Profit of AI Vendor

The vendor’s profit from an \((n,v)\) adopter firm is \((p-w) n (1-\omega v)\). Total profit across all adopter firms (\(v > v^\ast)\) of given size \(n\) is \(\int_{v^\ast}^{1} (p-w) n (1-\omega v) dv\) = \((p-w) n \int_{v^\ast}^{1} (v - \omega \frac{v^2}{2}) \vert_{v^\ast}^{1} dv\) = \((p-w) n \left(1 - \frac{\omega}{2} - v^* + \frac{\omega}{2}(v^*)^2 \right)\).

Further, looking at this across all firm sizes involves a second integral over \(n\); that integral resolves differently depending on whether \(n\) follows a uniform or Beta distribution. For the Beta distribution, the integral \(\int_{Beta(n)} n dn\) resolves to the expected value of the Beta distribution, i.e., \(\frac{\alpha}{\alpha+\beta}\).

Introduction

Firms vary by size \(n \in [0,1] \sim ext{Beta}(\alpha, \beta)\) and ability to benefit \(v \in [0,1]\). The AI tool reduces workforce to \(n(1 - \omega v)\). Vendor charges \(p\) per supported seat and incurs cost \(w\).

Parameters

# omega <- 0.15
c <- 1000
w <- 0.3
alpha <- 5
beta <- 5

Functions

v_star <- function(p, omega, c) p / (omega*(c + p)) 

v_integral <- function(p, omega, c) {
  v_cut <- v_star(p, omega, c)
  1 - omega/2 - v_cut + (omega/2) * v_cut^2
}

profit_beta_cost <- function(p, omega, c, w, alpha, beta) {
  E_n <- alpha / (alpha + beta)
  E_n * (p - w) * v_integral(p, omega, c)
}

Profit and Demand Curve

p_vals <- seq(1, 1000, by = 1)
profits <- function(omega) {sapply(p_vals, profit_beta_cost, omega = omega, c = c, w = w, alpha = alpha, beta = beta)}

# Define your vector of omega values
omega_values <- c(0.4, 0.6, 0.8)  # Modify these values as needed

# Create data for all omega values
price_profit_data <- do.call(rbind, lapply(omega_values, function(omega) {
  data.frame(
    p = p_vals, 
    profit = profits(omega),
    omega = omega
  )
}))

# Create the plot with multiple curves
ggplot(price_profit_data, aes(x = p, y = profit, color = factor(omega))) +
  geom_line(size = 1) +
  labs(
    title = "Profit vs Price (Beta Distribution) for Different Omega Values", 
    x = "Price (p)", 
    y = "Profit",
    color = "Omega"
  ) +
  theme_minimal() +
  scale_color_brewer(type = "qual", palette = "Set1")
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

Optimization

# Lets compute p* given an omega value, and other parameters

opt_result <- function(omega) {
  optimize(function(p) -profit_beta_cost(p, omega, c, w, alpha, beta),
                       interval = c(1, 5000), maximum = FALSE)
}

p_star <- function(omega) {opt_result(omega)$minimum}
profit_star <- function(omega) {-opt_result(omega)$objective} 
v_cut_star <- function(omega) {v_star(p_star(omega), omega, c)}

# cat("Optimal p:", round(p_star, 2), "\n")
# cat("Optimal profit:", round(profit_star, 2), "\n")
# cat("Cutoff v*:", round(v_cut_star, 4), "\n")

Payment Grid

v_vals <- seq(from = 1, to = 0, by = -0.01) # should be to v_cut_star(omega) - see filter below
v_vals <- rev(v_vals)
n_vals <- seq(0.1, 1, by=0.01)
param_grid <- expand.grid(n = n_vals, v = v_vals)
# payment_grid$payment <- with(payment_grid, p_star * n * (1 - omega * v))

payment_grid <- function(omega) {
  param_grid %>% filter(v >= v_cut_star(omega)) %>% 
    mutate(nv = n*v, payment = p_star(omega) * n * (1 - omega * v)) %>% arrange(nv)
}

Sample Payments Table

kable(payment_grid[sample(nrow(payment_grid), 10), ]  %>% arrange(nv), digits = 4, caption = "Sample Buyer Payments (n, v > v*)")

With VC

# ggplot(payment_grid) + geom_point(aes(x=nv, y=payment)) 

# Plot
ggplot(payment_grid, aes(x = nv, y = payment)) +
  geom_line(color = "blue", size = 1) +
  geom_point(color = "darkblue") +
  labs(title = "Average Payment vs. nv",
       x = "n × v (nv)",
       y = "Average Payment") +
  theme_minimal()
# ggplot(payment_grid) + geom_point(aes(x=nv, y=payment)) 

# Compute average payment by nv
payment_summary <- payment_grid %>% mutate(nv = round(nv,1)) %>% 
  group_by(nv) %>%
  summarise(avg_payment = mean(payment, na.rm = TRUE)) %>%
  arrange(nv)

# Plot
ggplot(payment_summary, aes(x = nv, y = avg_payment)) +
  geom_line(color = "blue", size = 1) +
  geom_point(color = "darkblue") +
  labs(title = "Average Payment vs. nv",
       x = "n × v (nv)",
       y = "Average Payment") +
  theme_minimal()
# Define omega values
omega_values <- seq(0.1, 0.9, 0.1)

# Create payment summaries for all omega values
payment_summary_all <- do.call(rbind, lapply(omega_values, function(omega) {

  payment_summary <- payment_grid(omega) %>% 
    mutate(nv = round(nv, 1)) %>% 
    group_by(nv) %>%
    summarise(avg_payment = mean(payment, na.rm = TRUE), .groups = 'drop') %>%
    arrange(nv) %>%
    mutate(omega = omega)
  
  return(payment_summary)
}))

# Create the plot with multiple curves
ggplot(payment_summary_all, aes(x = nv, y = avg_payment, color = factor(omega))) +
  geom_line(size = 1) +
  geom_point() +
  labs(
    title = "Average Payment vs. nv for Different Omega Values",
    x = "n × v (nv)",
    y = "Average Payment",
    color = "Omega"
  ) +
  theme_minimal() +
  scale_color_brewer(type = "qual", palette = "Set1")

Result Visualizations

Define Parameters and Generate Data

# Define sequences for n and v
c <- 1000
w <- 0.3
alpha <- 12
beta <- 2

omegaValues <- seq(0.09,0.99,0.1) 
nValues <- seq(0,1,0.01)
vValues <- seq(0,1,0.01)

# Create a data frame with all combinations of n and v
df <- expand.grid(omega = omegaValues, n = nValues, v = vValues) 

# Define the payment function
payment <- function(p, omega, n, v) {
  p * omega * n * (1 - omega*v)
}

value <- function(emp_cost, n, v) { # n employees --> n*(1-v) employees
  emp_cost * n * v
}

# df <- df %>% filter()
  
# Compute nv and payment
df <- df %>%
  mutate(
    nv = n * v,
    payment = payment(omega, n, v)
  )

kable(df[sample(dim(df)[1] ,8), ], caption = "sample values in df")

Scatter Plot

ggplot(df, aes(x = nv, y = payment)) +
  geom_point(aes(color = as.factor(n)), shape = 21, alpha = 0.7) +
  facet_wrap(~ omega, ncol = 4, scales="free")  +
# ggplot(df, aes(x = nv, y = payment, color = as.factor(round(n)))) +
#  geom_point(alpha = 0.5, size = 0.7) +
  labs(
    title = "Scatterplot of Payment vs n × v",
    x = "n × v",
    y = "Payment",
    shape = "Rounded v",
    color = "Rounded n"
  ) +
  theme_minimal()