Learning Curve Modeling Approaches
Learning curves are mathematical models predicting improvements in productivity and efficiency as experience with a task increases. These curves are essential tools for:
- Estimating project costs and timelines.
- Analyzing historical data for efficiency trends.
- Forecasting and decision-making.
The following documentation covers three popular methods implemented using Julia with multiple dispatch:
Learning Curve Methods
The learning curve methods are implemented as an abstract type hierarchy:
LearningCurveMethod: Abstract base typeWrightMethod: Wright's cumulative-average modelCrawfordMethod: Crawford's unit-time modelExperienceMethod: Experience curve model
Wright's Curve
MCHammer.WrightMethod — TypeWright learning curve method.
struct WrightMethod <: LearningCurveMethod endIntroduced by T.P. Wright in 1936 in his seminal work on airplane production cost analysis (Wright, 1936). This model observes that with each doubling of cumulative production, the unit cost decreases by a fixed percentage. It is well‑suited for processes where learning is continuous and gradual.
Parameters (package-wide convention)
Learning= progress ratio LR in(0,1](e.g.0.85= 85%)b = log(Learning)/log(2)(negative when there is learning)InitialEffort= first‑unit costC₁
Implementation returns cumulative total cost (cumulative‑average form):
\[\mathrm{Total}(N)=C_1\,N^{\,b+1},\qquad b=\frac{\log(\mathrm{LR})}{\log 2}\]
Unit‑cost form (reference):
\[C_N = C_1\,N^{\,b}\]
When to Use
- When historical data show a smooth, predictable decline in unit costs as production doubles.
When to Avoid
- When cost reductions occur in discrete steps or when the production process experiences structural changes.
Crawford's Curve
MCHammer.CrawfordMethod — TypeCrawford learning curve method.
struct CrawfordMethod <: LearningCurveMethod endCrawford models per‑unit learning discretely and accumulates unit costs.
Parameters
Learning= progress ratio LR in(0,1]b = log(Learning)/log(2)InitialEffort= first‑unit costC₁
Implementation returns cumulative total cost (discrete sum):
\[\mathrm{Total}(N)=\sum_{i=1}^N C_1\, i^{\,b}\]
Per‑unit cost (reference):
\[C_i = C_1\, i^{\,b}\]
Experience Curve
MCHammer.ExperienceMethod — TypeExperience learning curve method.
struct ExperienceMethod <: LearningCurveMethod endBCG/Excel-style experience curve where the exponent equals the progress ratio LR. This matches spreadsheet formulas like C1 * N^(LR); e.g., with LR=0.8 the total is C1 * N^0.8.
Parameters
Learning= progress ratio LR in(0,1]InitialEffort= average cost atN=1(ar{C}_1)
Implementation returns cumulative total cost:
\[\mathrm{Total}(N)=\bar{C}_1\,N^{\,\mathrm{LR}}\]
When to Use
- Strategic/competitive planning when broad efficiency improvements are observed and spreadsheet convention is desired.
When to Avoid
- When per‑unit discreteness dominates (see
CrawfordMethod).
Cumulative Cost Analysis
To compute cumulative cost analytically for an experience curve, here are the functions to accomplish this.
Wright Learning Curve
# compute the cumulative total cost for 500 units with a progress ratio of 0.85
result = lc_analytic(WrightMethod(), 200, 500, 0.85)
println(result)23290.84865742685Crawford Learning Curve
# compute the cumulative total cost for 500 units with a progress ratio of 0.85
result = lc_analytic(CrawfordMethod(), 200, 500, 0.85)
println(result)30290.08483739403Experience Curve
# compute the cumulative total cost for 500 units with a progress ratio of 0.85
result = lc_analytic(ExperienceMethod(), 200, 500, 0.85)
println(result)39369.01049745011Curve Functions
Generates detailed DataFrame including cumulative, incremental, and average costs.
MCHammer.lc_curve — Functionlc_curve(method::LearningCurveMethod,
InitialEffort::Real,
StartUnit::Integer,
TotalUnits::Integer,
Learning::Real;
steps::Integer=1) -> DataFrameBuild a table of cumulative and per-unit costs.
Columns:
Units— cumulative units at this row (StartUnit:steps:TotalUnits)CumulativeCost— total cost up toUnits(vialc_analytic)Incremental— per-unit cost atUnits(C_N)- Wright/Experience:
T(N) - T(N-1)whereT(·)is the cumulative total - Crawford: direct unit cost
C_N = C₁ · N^b, withb = log(LR)/log(2)
- Wright/Experience:
AvgCost—CumulativeCost / UnitsMethod— method name
Wright Learning Curve
Generate a table of cumulative and per‑unit costs for Wright’s model using initial cost 200, from unit 1 to 500, progress ratio 0.85, sampling every 25 units
df = lc_curve(WrightMethod(), 200, 1, 500, 0.85; steps=25)
println(first(df, 5))5×5 DataFrame
Row │ Units CumulativeCost Incremental AvgCost Method
│ Float64 Float64 Float64 Float64 String
─────┼────────────────────────────────────────────────────────
1 │ 1.0 200.0 200.0 200.0 Wright
2 │ 26.0 2422.37 71.6503 93.1682 Wright
3 │ 51.0 4057.27 61.0428 79.5544 Wright
4 │ 76.0 5506.28 55.5498 72.451 Wright
5 │ 101.0 6845.54 51.9466 67.7776 WrightCrawford Learning Curve
Generate a table of cumulative and per‑unit costs for Crawford’s model using initial cost 200, from unit 1 to 500, progress ratio 0.85, sampling every 25 units
df = lc_curve(CrawfordMethod(), 200, 1, 500, 0.85; steps=25)
println(first(df, 5))5×5 DataFrame
Row │ Units CumulativeCost Incremental AvgCost Method
│ Float64 Float64 Float64 Float64 String
─────┼──────────────────────────────────────────────────────────
1 │ 1.0 200.0 200.0 200.0 Crawford
2 │ 26.0 3053.31 93.1682 117.435 Crawford
3 │ 51.0 5182.18 79.5544 101.611 Crawford
4 │ 76.0 7071.44 72.451 93.0452 Crawford
5 │ 101.0 8818.55 67.7776 87.3124 CrawfordExperience Curve
Generate a table of cumulative and per‑unit costs for the Experience model using initial cost 200, from unit 1 to 500, progress ratio 0.85, sampling every 25 units
df = lc_curve(ExperienceMethod(), 200, 1, 500, 0.85; steps=25)
println(first(df, 5))5×5 DataFrame
Row │ Units CumulativeCost Incremental AvgCost Method
│ Float64 Float64 Float64 Float64 String
─────┼───────────────────────────────────────────────────────────
1 │ 1.0 200.0 200.0 200.0 Experience
2 │ 26.0 3189.76 104.586 122.683 Experience
3 │ 51.0 5655.42 94.3966 110.891 Experience
4 │ 76.0 7938.19 88.8705 104.45 Experience
5 │ 101.0 10108.9 85.1382 100.088 ExperienceAnalysis Functions
Fitting Functions
MCHammer.lc_fit — Functionlc_fit(::LearningCurveMethod, n1, x1, n2, x2) -> (b, p)Generic two‑point fit for a power law x = a n^b. Returns the slope b and the progress ratio p = 2^b.
Example:
The function lc_fit estimates the slope $b$ and progress ratio $p = 2^b$ from two observations. Given two points at units $n_1$ and $n_2$ with corresponding costs $x_1$ and $x_2$, we can estimate $b$ and $p$ for any of the learning‑curve methods. The following example uses the same data for Wright, Crawford and Experience models. Though the results are identical, the derivations are different.
# two observation points
n1, x1 = 8, 200.0
n2, x2 = 32, 140.0
# estimate (b, progress ratio) for each method using the same inputs
(b_w, L_w) = lc_fit(WrightMethod(), n1, x1, n2, x2)
(b_c, L_c) = lc_fit(CrawfordMethod(), n1, x1, n2, x2)
(b_e, L_e) = lc_fit(ExperienceMethod(),n1, x1, n2, x2)
((b_w, L_w), (b_c, L_c), (b_e, L_e))((-0.2572865864148791, 0.8366600265340756), (-0.2572865864148791, 0.8366600265340756), (-0.2572865864148791, 0.8366600265340756))Learning Rate Estimation
Estimates learning rates using Wright's method from two data points.
MCHammer.learn_rate — Functionlearn_rate(::WrightMethod, n1, y1, n2, y2; mode=:avg) -> Float64Estimate Wright progress ratio p from two observations.
- If
mode = :avg(default),yvalues are cumulative averages atn1andn2:
\[b = \frac{\ln(y_2/y_1)}{\ln(n_2/n_1)}, \quad p = 2^b.\]
- If
mode = :totalormode = :cumulative,yvalues are cumulative totals atn1andn2. First converts to averages, then applies average formula. - If
mode = :unit,yvalues are unit times atn1andn2. The function solves forbfrom the unit‑increment relation and returnsp = 2^b. - If
mode = :auto(default), automatically detects data type: ify2 > y1, assumes:totalmode; otherwise:avgmode.
learn_rate(::WrightMethod, n::AbstractVector, y::AbstractVector; mode=:auto) -> Float64Estimate Wright progress ratio p from ≥3 points. If mode = :auto (default), fits both average and unit models and picks the lower SSE. Set mode=:avg or :unit to force a specific interpretation.
learn_rate(::CrawfordMethod, n1, y1, n2, y2; mode=:auto) -> Float64Two-point fit for Crawford's model with multiple data types.
- If
mode = :unit,yvalues are per-unit costs at unitsn1andn2:y(i) = C1 * i^b. - If
mode = :avg,yvalues are cumulative averages atn1andn2. Uses the exact discrete-sum relation A(N) = (C₁/N) · Sb(N) with Sb(N) = ∑ i^b and solves for b. - If
mode = :totalormode = :cumulative,yvalues are cumulative totals atn1andn2. Uses T(N) = C₁ · S_b(N) and solves for b. - If
mode = :auto(default), automatically detects: ify2 > y1, assumes:total; otherwise:unit.
Returns the learning rate p.
learn_rate(::ExperienceMethod, n1, y1, n2, y2; mode=:auto) -> Float64Two‑point fit for the Experience curve with multiple data types.
- If
mode = :avg,yvalues are cumulative averages atn1andn2. For T(N) = C₁ * N^LR, avg(N) = C₁ * N^(LR-1). - If
mode = :totalormode = :cumulative,yvalues are cumulative totals atn1andn2. T(N) = C₁ * N^LR. - If
mode = :unit,yvalues are per-unit costs atn1andn2. Uses the exact incremental relation ΔT(N) = T(N) − T(N−1) = C̄₁ · [N^LR − (N−1)^LR] and solves numerically for LR from two points. - If
mode = :auto(default), automatically detects: ify2 > y1, assumes:total; otherwise:avg.
Returns LR (e.g., 0.8).
rate = learn_rate(WrightMethod(), 1, 2000, 144, 1600)
println(rate)0.9693571494126194Comparison Utility
Compares learning curves across a range of learning rates.
MCHammer.learn_rates — Functionlearn_rates(InitialEffort, TotalUnits; LR_step=0.1) -> DataFrameCompare cumulative total cost across methods for a sweep of progress ratios LR in (0, 1].
Totals at N = TotalUnits:
- Wright: T_W(N) = C₁ * N^(1 + b), with b = log(LR) / log(2)
- Crawford: TC(N) = ∑{i=1}^N C₁ * i^b, with b = log(LR) / log(2)
- Experience: T_E(N) = C₁ * N^(LR)
Example:
rates_df = learn_rates(100, 500; α_step=0.05)
println(first(rates_df, 5))Picking the right curve
Sometimes picking the right curve is challenging and in these cases plotting a comparison of average costs across methods using Plots.jl can be very helpful.
# Generate sample tables for each method and plot the average cost across units.
CC = lc_curve(CrawfordMethod(), 50, 1, 1000, 0.85; steps=25)
WC = lc_curve(WrightMethod(), 50, 1, 1000, 0.85; steps=25)
EC = lc_curve(ExperienceMethod(), 50, 1, 1000, 0.85; steps=25)
# Combine the results
GraphResults = vcat(CC, WC, EC)
first(GraphResults,5)| Row | Units | CumulativeCost | Incremental | AvgCost | Method |
|---|---|---|---|---|---|
| Float64 | Float64 | Float64 | Float64 | String | |
| 1 | 1.0 | 50.0 | 50.0 | 50.0 | Crawford |
| 2 | 26.0 | 763.328 | 23.2921 | 29.3588 | Crawford |
| 3 | 51.0 | 1295.54 | 19.8886 | 25.4028 | Crawford |
| 4 | 76.0 | 1767.86 | 18.1128 | 23.2613 | Crawford |
| 5 | 101.0 | 2204.64 | 16.9444 | 21.8281 | Crawford |
Create and display the plot
using Plots
plot(GraphResults.Units, GraphResults.AvgCost, group=GraphResults.Method,
xlabel="Units", ylabel="Average Cost per Unit",
title="Learning Curves Comparison",
lw=2, legend=:topright)Mathematical Notes
Symbols:
- α: learning exponent (progress ratio = $2^{b}$)
- N: number of units
Wright's Law (Cumulative Average Model):
\[\text{Cumulative Average Cost}_N = \text{Initial} \times N^{-\alpha}\]
Crawford's Law (Unit Cost Model):
\[\text{Unit Cost}_N = \text{Initial} \times N^{-\alpha}\]
Experience Curve:
\[\text{Cost}_N = \text{Initial} \times N^{-\alpha}\]
Sources & References
- Eric Torkia, Decision Superhero Vol. 2, chapter 6 : SuperPower: The Laws of Nature that Predict, Technics Publishing, 2025
- Available on Amazon : https://a.co/d/4YlJFzY . Volumes 2 and 3 to be released in Spring and Fall 2025.
- Wright, T.P. (1936). Factors Affecting the Cost of Airplanes. Journal of the Aeronautical Sciences, 3(4), 122–128.
- Henderson, B.D. (1973). Industrial Experience, Technology Transfer, and Cost Behavior. Harvard Business School Working Paper.
- Crawford, D. (1982). Learning Curves: Theory and Practice. Journal of Cost Analysis.