Based on this week’s model, and the Markov simulation website, create a model in Simio to represent 4 possible states of a process. Impersonate the objects used in the model to resemble the process illustrated with the help of the symbols. Bring a small narrative of the process.

Markov Chain: Medical Claims Lifecycle

In this assignment I will model lifecycle of a medical claim at health insurance plan

States: Intake -> ClinicalReview -> FinancialReview -> Resolved (absorbing)

  1. Intake - Medical claim received form the provider
  2. Clinical Review - Policy Review (e.g prior auth or medical necessity; or if claim submitted in a timely manner)
  3. Financial Review - pricing/ repricing (e.g based on the NYS CMS Medicaid / Medicare fee schedule, or fee schedule provided in the contract between provider and health plan) ; benefits, COB
  4. Resolved - claim finalized ( Paid or Denied).

Logic

A claim enters Intake and may go straight to Financial Review - if claim is clean or Clinical Review when documentation and policy verification is needed. Either review can bounce the claim back to Intake ( in case if medical information is missing or incorrect) or hand it off to the other review. Once criteria and pricing clear, the claim moves to Resolved stage.

KPI from the model: probability a claim resolves without ever needing clinical review, expected # of touchpoints before the resolution, share of “rework” loops back to Intake stage, and distribution of time- to -resolution.

set.seed(42)
states <- c("Intake", "ClinicalReview", "FinancialReview", "Resolved")

Create Transition Matrix

P <- matrix(
c(
# From Intake:
# to Intake, Clin, Fin, Resolved
0.05, 0.70, 0.10, 0.15,
# From ClinicalReview:
0.15, 0.10, 0.60, 0.15,
# From FinancialReview:
0.10, 0.10, 0.10, 0.70,
# From Resolved (absorbing):
0.00, 0.00, 0.00, 1.00
),
nrow = 4, byrow = TRUE,
dimnames = list(from = states, to = states)
)
P<-as.matrix(P)

Build Markov chain object

mc <- new("markovchain", states = states, transitionMatrix = P, name = "ClaimsMC")
mc
## ClaimsMC 
##  A  4 - dimensional discrete Markov Chain defined by the following states: 
##  Intake, ClinicalReview, FinancialReview, Resolved 
##  The transition matrix  (by rows)  is defined as follows: 
##                  to
## from              Intake ClinicalReview FinancialReview Resolved
##   Intake            0.05            0.7             0.1     0.15
##   ClinicalReview    0.15            0.1             0.6     0.15
##   FinancialReview   0.10            0.1             0.1     0.70
##   Resolved          0.00            0.0             0.0     1.00

Absorbing-chain analytics (transient (T) vs absorbing (A) states)

absorbingIdx <- which(states == "Resolved")
transientIdx <- setdiff(seq_along(states), absorbingIdx)
Q <- P[transientIdx, transientIdx, drop = FALSE]
R <- P[transientIdx, absorbingIdx, drop = FALSE ]
I3 <- diag(nrow(Q))
Nmat <- solve(I3 - Q) # Fundamental matrix: expected # of visits to transient states
t_steps <- as.vector(Nmat %*% rep(1, nrow(Q))) # Expected number of steps to absorption starting from each transient state
names(t_steps) <- states[transientIdx]
# Absorption probabilities (from each transient state to Resolved)
B <- Nmat %*% R
rownames(B) <- states[transientIdx]
colnames(B) <- "Pr(Resolved)"

View Report

cat("\nExpected steps to resolution (by starting state):\n")
## 
## Expected steps to resolution (by starting state):
print(round(t_steps, 2))
##          Intake  ClinicalReview FinancialReview 
##            3.36            2.87            1.80
cat("\nProbability the claim eventually resolves (it should be 1):\n")
## 
## Probability the claim eventually resolves (it should be 1):
print(round(B, 4))
##                  to
##                   Pr(Resolved)
##   Intake                     1
##   ClinicalReview             1
##   FinancialReview            1

Simulation for business KPIs

simulate_claim <- function(P, start = "Intake", states) {
cur <- start
path <- cur
while (cur != "Resolved") {
cur <- sample(states, size = 1, prob = P[cur, ])
path <- c(path, cur)
# Safety net in case of a bad matrix
if (length(path) > 1000) break
}
path
}

nClaims <- 10000
paths <- vector("list", nClaims)

for (i in seq_len(nClaims)) {
paths[[i]] <- simulate_claim(P = P, start = "Intake", states = states)
}

KPI

steps_to_resolve <- sapply(paths, function(p) length(p) - 1) # transitions taken
ever_clinical <- sapply(paths, function(p) any(p == "ClinicalReview"))
ever_financial <- sapply(paths, function(p) any(p == "FinancialReview"))
loops_back_to_intake <- sapply(paths, function(p) {
# Count revisits to Intake after the first element
sum(p[-1] == "Intake") > 0
})

kpi <- list(
avg_steps_to_resolution = mean(steps_to_resolve),
median_steps_to_resolution = median(steps_to_resolve),
pct_without_clinical = mean(!ever_clinical),
pct_needing_clinical = mean(ever_clinical),
pct_needing_financial = mean(ever_financial),
pct_rework_back_to_intake = mean(loops_back_to_intake)
)

cat("\n--- Simulated KPIs (n =", nClaims, ") ---\n")
## 
## --- Simulated KPIs (n = 10000 ) ---
print(lapply(kpi, function(x) if(is.numeric(x)) round(x, 3) else x))
## $avg_steps_to_resolution
## [1] 3.355
## 
## $median_steps_to_resolution
## [1] 3
## 
## $pct_without_clinical
## [1] 0.244
## 
## $pct_needing_clinical
## [1] 0.756
## 
## $pct_needing_financial
## [1] 0.681
## 
## $pct_rework_back_to_intake
## [1] 0.246
# Distribution of steps 
dist_steps <- table(steps_to_resolve) / nClaims
cat("\nDistribution of steps to resolution:\n")
## 
## Distribution of steps to resolution:
print(round(dist_steps, 3))
## steps_to_resolve
##     1     2     3     4     5     6     7     8     9    10    11    12    13 
## 0.156 0.174 0.339 0.120 0.087 0.050 0.030 0.019 0.009 0.006 0.003 0.002 0.002 
##    14    15    16    17    18 
## 0.000 0.001 0.000 0.000 0.000
Simulation for business KPIs (speed up FinancialReview:higher chance to Resolve )
P_fast_fin <- P
P_fast_fin["FinancialReview", ] <- c(0.05, 0.05, 0.10, 0.80) # +10pp to Resolved, lower bounce-backs
row_sums <- rowSums(P_fast_fin)
stopifnot(all(abs(row_sums - 1) < 1e-8))

mc_fast <- new("markovchain", states = states, transitionMatrix = P_fast_fin)
Compare expected steps from Intake between base and faster-financial scenario
Q_base <- P[transientIdx, transientIdx, drop = FALSE]
Q_fast <- P_fast_fin[transientIdx, transientIdx, drop = FALSE]
t_base <- as.vector(solve(diag(nrow(Q_base)) - Q_base) %*% rep(1, nrow(Q_base)))[1]
t_fast <- as.vector(solve(diag(nrow(Q_fast)) - Q_fast) %*% rep(1, nrow(Q_fast)))[1]

cat("\nExpected steps from Intake (base vs faster FinancialReview): ",
round(t_base, 2), "vs", round(t_fast, 2), "\n")
## 
## Expected steps from Intake (base vs faster FinancialReview):  3.36 vs 3.1

View of Matrix as %

percent <- function(x) sprintf("%.1f%%", 100 * x)
cat("\nTransition Matrix (%):\n")
## 
## Transition Matrix (%):
print(apply(P, 2, percent))
##       to
##        Intake  ClinicalReview FinancialReview Resolved
##   [1,] "5.0%"  "70.0%"        "10.0%"         "15.0%" 
##   [2,] "15.0%" "10.0%"        "60.0%"         "15.0%" 
##   [3,] "10.0%" "10.0%"        "10.0%"         "70.0%" 
##   [4,] "0.0%"  "0.0%"         "0.0%"          "100.0%"

Summary

In this assignment I modeled the medical claims lifecycle as a 4-state absorbing Markov chain (Intake → ClinicalReview → FinancialReview → Resolved). The model estimates operational efficiency (steps to resolution) and rework (loops back to Intake). Results indicate the average claim takes ~3.36 steps to reach Resolved, with 76% passing through Clinical Review and 68% through Financial Review. The absorbing analysis confirms Pr(Resolved) = 1 for all transient starting states, i.e., process stability.

“Every rework loop in the claims process translates to time, cost, and provider dissatisfaction — Markov modeling helps identify exactly where inefficiencies occur and how they can impact company’s financial performance.”

Claims Process KPIs from Simulation
KPI Result Interpretation
Avg steps to resolution 3.36 Touchpoints per claim (lower is better)
Median steps to resolution 3 Typical path length
% without clinical 24% Straight-through clean claims
% needing clinical 76% Share needing medical policy review
% needing financial 68% Share needing pricing/COB/edits
% rework back to Intake 25% Rework/returns to Intake (documentation/edits)