Implements the full Norton-Bass Multigenerational Diffusion Model for transceiver technology lifecycle forecasting. Math: Bass diffusion F(t) + logistic adoption S(t) = L / (1 + e^(-k(t-t0))) Parameters: p (innovation ~0.03), q (imitation ~0.3-0.5), m (market potential) Phase Classification Engine (composite score): 30% Port shipment share + 20% ASP decline rate + 15% Standards maturity + 15% Interop validation + 10% Vendor trajectory + 10% Media sentiment 11 technologies tracked: 1G → 10G → 25G → 40G → 100G → 400G → 800G → 1.6T + CPO, LPO, 400ZR Coherent 5-year adoption forecast per technology API: GET /api/hype-cycle (all) + GET /api/hype-cycle/:tech (detail) Live: https://transceiver-db.context-x.org/api/hype-cycle
469 lines
16 KiB
TypeScript
469 lines
16 KiB
TypeScript
/**
|
||
* Norton-Bass Multigenerational Diffusion Model
|
||
*
|
||
* Mathematical engine for forecasting transceiver technology adoption.
|
||
* Based on Norton & Bass (1987, Management Science).
|
||
*
|
||
* Key equations:
|
||
* Bass: f(t) / [1 - F(t)] = p + q * F(t)
|
||
* Logistic: S(t) = L / (1 + e^(-k(t - t0)))
|
||
*
|
||
* Parameters:
|
||
* p = Innovation coefficient (~0.03 for network hardware)
|
||
* q = Imitation coefficient (~0.3–0.5)
|
||
* m = Total market potential (addressable port shipments)
|
||
*/
|
||
|
||
/** Technology generation definition */
|
||
export interface TechGeneration {
|
||
readonly name: string; // e.g. "100G QSFP28"
|
||
readonly speedGbps: number;
|
||
readonly formFactor: string;
|
||
readonly introYear: number; // Year first shipped commercially
|
||
readonly peakYear: number; // Year of peak shipments (estimated)
|
||
readonly p: number; // Innovation coefficient
|
||
readonly q: number; // Imitation coefficient
|
||
readonly m: number; // Market potential (millions of ports)
|
||
readonly k: number; // Logistic growth rate
|
||
readonly t0: number; // Inflection point year
|
||
}
|
||
|
||
/** Hype Cycle phases per Gartner methodology */
|
||
export type HypeCyclePhase =
|
||
| "INNOVATION_TRIGGER"
|
||
| "PEAK_OF_INFLATED_EXPECTATIONS"
|
||
| "TROUGH_OF_DISILLUSIONMENT"
|
||
| "SLOPE_OF_ENLIGHTENMENT"
|
||
| "PLATEAU_OF_PRODUCTIVITY"
|
||
| "LEGACY_DECLINE";
|
||
|
||
/** Result of a hype cycle computation */
|
||
export interface HypeCycleResult {
|
||
readonly technology: string;
|
||
readonly phase: HypeCyclePhase;
|
||
readonly phaseLabel: string;
|
||
readonly positionPct: number; // 0–100 on the hype cycle curve
|
||
readonly adoptionPct: number; // Current market adoption %
|
||
readonly compositeScore: number; // 0–100
|
||
readonly forecast: AdoptionForecast;
|
||
readonly metrics: PhaseMetrics;
|
||
}
|
||
|
||
export interface AdoptionForecast {
|
||
readonly currentYear: number;
|
||
readonly yearsToPlateauFromNow: number;
|
||
readonly peakShipmentYear: number;
|
||
readonly cumulativeAdoptionPct: number;
|
||
readonly yearlyAdoptionPct: number;
|
||
readonly fiveYearProjection: ReadonlyArray<{ year: number; adoptionPct: number; phase: HypeCyclePhase }>;
|
||
}
|
||
|
||
export interface PhaseMetrics {
|
||
readonly shipmentShare: number; // 0–1
|
||
readonly aspDeclineRate: number; // % per year
|
||
readonly standardsMaturity: number; // 0–100
|
||
readonly interopLevel: number; // 0–100
|
||
readonly vendorCount: number;
|
||
readonly vendorTrend: "increasing" | "stable" | "decreasing";
|
||
readonly mediaHypeIndex: number; // 0–100
|
||
}
|
||
|
||
// ============================================================
|
||
// Known technology generations (seed data, March 2026)
|
||
// ============================================================
|
||
export const TECH_GENERATIONS: ReadonlyArray<TechGeneration> = [
|
||
{
|
||
name: "1G SFP",
|
||
speedGbps: 1,
|
||
formFactor: "SFP",
|
||
introYear: 2001,
|
||
peakYear: 2012,
|
||
p: 0.03, q: 0.38, m: 500, k: 0.45, t0: 2008,
|
||
},
|
||
{
|
||
name: "10G SFP+",
|
||
speedGbps: 10,
|
||
formFactor: "SFP+",
|
||
introYear: 2006,
|
||
peakYear: 2018,
|
||
p: 0.03, q: 0.42, m: 600, k: 0.50, t0: 2014,
|
||
},
|
||
{
|
||
name: "40G QSFP+",
|
||
speedGbps: 40,
|
||
formFactor: "QSFP+",
|
||
introYear: 2010,
|
||
peakYear: 2019,
|
||
p: 0.025, q: 0.35, m: 150, k: 0.40, t0: 2016,
|
||
},
|
||
{
|
||
name: "25G SFP28",
|
||
speedGbps: 25,
|
||
formFactor: "SFP28",
|
||
introYear: 2015,
|
||
peakYear: 2022,
|
||
p: 0.04, q: 0.45, m: 200, k: 0.55, t0: 2019,
|
||
},
|
||
{
|
||
name: "100G QSFP28",
|
||
speedGbps: 100,
|
||
formFactor: "QSFP28",
|
||
introYear: 2014,
|
||
peakYear: 2024,
|
||
p: 0.03, q: 0.40, m: 400, k: 0.48, t0: 2020,
|
||
},
|
||
{
|
||
name: "400G QSFP-DD",
|
||
speedGbps: 400,
|
||
formFactor: "QSFP-DD",
|
||
introYear: 2020,
|
||
peakYear: 2027,
|
||
p: 0.035, q: 0.50, m: 300, k: 0.52, t0: 2025,
|
||
},
|
||
{
|
||
name: "800G OSFP",
|
||
speedGbps: 800,
|
||
formFactor: "OSFP",
|
||
introYear: 2023,
|
||
peakYear: 2029,
|
||
p: 0.04, q: 0.55, m: 250, k: 0.55, t0: 2027,
|
||
},
|
||
{
|
||
name: "1.6T OSFP-XD",
|
||
speedGbps: 1600,
|
||
formFactor: "OSFP-XD",
|
||
introYear: 2025,
|
||
peakYear: 2032,
|
||
p: 0.03, q: 0.45, m: 180, k: 0.48, t0: 2030,
|
||
},
|
||
];
|
||
|
||
// Special tech entries (not speed-generational)
|
||
export const SPECIAL_TECHS: ReadonlyArray<TechGeneration> = [
|
||
{
|
||
name: "CPO",
|
||
speedGbps: 1600,
|
||
formFactor: "CPO",
|
||
introYear: 2024,
|
||
peakYear: 2033,
|
||
p: 0.02, q: 0.30, m: 50, k: 0.35, t0: 2031,
|
||
},
|
||
{
|
||
name: "LPO",
|
||
speedGbps: 800,
|
||
formFactor: "LPO",
|
||
introYear: 2024,
|
||
peakYear: 2029,
|
||
p: 0.035, q: 0.48, m: 100, k: 0.50, t0: 2027,
|
||
},
|
||
{
|
||
name: "400ZR Coherent",
|
||
speedGbps: 400,
|
||
formFactor: "QSFP-DD",
|
||
introYear: 2021,
|
||
peakYear: 2026,
|
||
p: 0.04, q: 0.50, m: 80, k: 0.55, t0: 2024,
|
||
},
|
||
];
|
||
|
||
// ============================================================
|
||
// Core Bass Diffusion Functions
|
||
// ============================================================
|
||
|
||
/**
|
||
* Bass diffusion: cumulative adoption F(t)
|
||
*
|
||
* F(t) = (1 - e^(-(p+q)*t)) / (1 + (q/p)*e^(-(p+q)*t))
|
||
*/
|
||
export function bassCumulativeAdoption(
|
||
t: number,
|
||
p: number,
|
||
q: number,
|
||
): number {
|
||
const pq = p + q;
|
||
const exp = Math.exp(-pq * t);
|
||
return (1 - exp) / (1 + (q / p) * exp);
|
||
}
|
||
|
||
/**
|
||
* Bass diffusion: instantaneous adoption rate f(t)
|
||
*
|
||
* f(t) = ((p+q)^2 / q) * (e^(-(p+q)*t) / (1 + (q/p)*e^(-(p+q)*t))^2)
|
||
*/
|
||
export function bassAdoptionRate(
|
||
t: number,
|
||
p: number,
|
||
q: number,
|
||
): number {
|
||
const pq = p + q;
|
||
const exp = Math.exp(-pq * t);
|
||
const denominator = 1 + (q / p) * exp;
|
||
return (pq * pq / q) * (exp / (denominator * denominator));
|
||
}
|
||
|
||
/**
|
||
* Logistic adoption curve: S(t) = L / (1 + e^(-k*(t - t0)))
|
||
*/
|
||
export function logisticAdoption(
|
||
year: number,
|
||
L: number,
|
||
k: number,
|
||
t0: number,
|
||
): number {
|
||
return L / (1 + Math.exp(-k * (year - t0)));
|
||
}
|
||
|
||
// ============================================================
|
||
// Phase Classification Engine
|
||
// ============================================================
|
||
|
||
export function classifyPhase(metrics: PhaseMetrics): HypeCyclePhase {
|
||
const { shipmentShare, aspDeclineRate, standardsMaturity, vendorTrend, mediaHypeIndex } = metrics;
|
||
|
||
if (shipmentShare < 0.01 && standardsMaturity < 30) {
|
||
return "INNOVATION_TRIGGER";
|
||
}
|
||
|
||
if (shipmentShare < 0.05 && mediaHypeIndex > 70 && vendorTrend === "increasing") {
|
||
return "PEAK_OF_INFLATED_EXPECTATIONS";
|
||
}
|
||
|
||
if (aspDeclineRate > 30 && vendorTrend === "decreasing" && mediaHypeIndex < 40) {
|
||
return "TROUGH_OF_DISILLUSIONMENT";
|
||
}
|
||
|
||
if (
|
||
shipmentShare >= 0.05 && shipmentShare <= 0.30 &&
|
||
aspDeclineRate >= 10 && aspDeclineRate <= 25 &&
|
||
(vendorTrend === "stable" || vendorTrend === "increasing")
|
||
) {
|
||
return "SLOPE_OF_ENLIGHTENMENT";
|
||
}
|
||
|
||
if (shipmentShare > 0.30 && aspDeclineRate < 10) {
|
||
return "PLATEAU_OF_PRODUCTIVITY";
|
||
}
|
||
|
||
// Fallback: use composite score approach
|
||
return compositePhaseClassification(metrics);
|
||
}
|
||
|
||
/**
|
||
* Composite score phase classification
|
||
*
|
||
* Phase_Score = 0.30 * Normalize(PortShipment_share)
|
||
* + 0.20 * Normalize(ASP_decline_rate)
|
||
* + 0.15 * Normalize(Standards_maturity)
|
||
* + 0.15 * Normalize(InteropValidation_level)
|
||
* + 0.10 * Normalize(VendorCount_trajectory)
|
||
* + 0.10 * Normalize(MediaSentiment_score)
|
||
*/
|
||
function compositePhaseClassification(metrics: PhaseMetrics): HypeCyclePhase {
|
||
const score =
|
||
0.30 * normalize(metrics.shipmentShare, 0, 0.5) +
|
||
0.20 * normalize(metrics.aspDeclineRate, 0, 50) +
|
||
0.15 * normalize(metrics.standardsMaturity, 0, 100) +
|
||
0.15 * normalize(metrics.interopLevel, 0, 100) +
|
||
0.10 * (metrics.vendorTrend === "increasing" ? 0.3 : metrics.vendorTrend === "stable" ? 0.6 : 0.9) +
|
||
0.10 * normalize(100 - metrics.mediaHypeIndex, 0, 100);
|
||
|
||
if (score < 0.15) return "INNOVATION_TRIGGER";
|
||
if (score < 0.30) return "PEAK_OF_INFLATED_EXPECTATIONS";
|
||
if (score < 0.45) return "TROUGH_OF_DISILLUSIONMENT";
|
||
if (score < 0.70) return "SLOPE_OF_ENLIGHTENMENT";
|
||
return "PLATEAU_OF_PRODUCTIVITY";
|
||
}
|
||
|
||
function normalize(value: number, min: number, max: number): number {
|
||
return Math.max(0, Math.min(1, (value - min) / (max - min)));
|
||
}
|
||
|
||
// ============================================================
|
||
// Phase position on the hype cycle curve (0–100)
|
||
// ============================================================
|
||
|
||
const PHASE_POSITIONS: Record<HypeCyclePhase, [number, number]> = {
|
||
INNOVATION_TRIGGER: [0, 15],
|
||
PEAK_OF_INFLATED_EXPECTATIONS: [15, 30],
|
||
TROUGH_OF_DISILLUSIONMENT: [30, 50],
|
||
SLOPE_OF_ENLIGHTENMENT: [50, 80],
|
||
PLATEAU_OF_PRODUCTIVITY: [80, 95],
|
||
LEGACY_DECLINE: [95, 100],
|
||
};
|
||
|
||
const PHASE_LABELS: Record<HypeCyclePhase, string> = {
|
||
INNOVATION_TRIGGER: "Innovation Trigger",
|
||
PEAK_OF_INFLATED_EXPECTATIONS: "Peak of Inflated Expectations",
|
||
TROUGH_OF_DISILLUSIONMENT: "Trough of Disillusionment",
|
||
SLOPE_OF_ENLIGHTENMENT: "Slope of Enlightenment",
|
||
PLATEAU_OF_PRODUCTIVITY: "Plateau of Productivity",
|
||
LEGACY_DECLINE: "Legacy / Decline",
|
||
};
|
||
|
||
// ============================================================
|
||
// Main computation: compute hype cycle for a technology
|
||
// ============================================================
|
||
|
||
export function computeHypeCycle(
|
||
tech: TechGeneration,
|
||
currentYear: number = new Date().getFullYear(),
|
||
overrideMetrics?: Partial<PhaseMetrics>,
|
||
): HypeCycleResult {
|
||
// Time since introduction
|
||
const t = currentYear - tech.introYear;
|
||
const tNorm = Math.max(0, t);
|
||
|
||
// Bass diffusion adoption
|
||
const cumulativeAdoption = bassCumulativeAdoption(tNorm, tech.p, tech.q);
|
||
const adoptionRate = bassAdoptionRate(tNorm, tech.p, tech.q);
|
||
|
||
// Logistic adoption (port shipments)
|
||
const logisticShipments = logisticAdoption(currentYear, tech.m, tech.k, tech.t0);
|
||
const shipmentShare = logisticShipments / 1000; // Normalize to 0–1 range (1000M total market)
|
||
|
||
// Estimate metrics from model
|
||
const yearsAfterIntro = currentYear - tech.introYear;
|
||
const yearsToPeak = tech.peakYear - tech.introYear;
|
||
const progressRatio = yearsAfterIntro / yearsToPeak;
|
||
|
||
const metrics: PhaseMetrics = {
|
||
shipmentShare: overrideMetrics?.shipmentShare ?? Math.min(0.5, shipmentShare),
|
||
aspDeclineRate: overrideMetrics?.aspDeclineRate ?? estimateAspDecline(progressRatio),
|
||
standardsMaturity: overrideMetrics?.standardsMaturity ?? estimateStandardsMaturity(progressRatio),
|
||
interopLevel: overrideMetrics?.interopLevel ?? estimateInteropLevel(progressRatio),
|
||
vendorCount: overrideMetrics?.vendorCount ?? estimateVendorCount(progressRatio),
|
||
vendorTrend: overrideMetrics?.vendorTrend ?? estimateVendorTrend(progressRatio),
|
||
mediaHypeIndex: overrideMetrics?.mediaHypeIndex ?? estimateMediaHype(progressRatio),
|
||
};
|
||
|
||
const phase = classifyPhase(metrics);
|
||
|
||
// Check for legacy/decline
|
||
const finalPhase = (currentYear > tech.peakYear + 8 && metrics.shipmentShare < 0.05)
|
||
? "LEGACY_DECLINE"
|
||
: phase;
|
||
|
||
// Position on curve (0-100)
|
||
const [phaseMin, phaseMax] = PHASE_POSITIONS[finalPhase];
|
||
const intraPhaseProgress = Math.min(1, cumulativeAdoption);
|
||
const positionPct = phaseMin + (phaseMax - phaseMin) * intraPhaseProgress;
|
||
|
||
// Composite score
|
||
const compositeScore = Math.round(
|
||
0.30 * normalize(metrics.shipmentShare, 0, 0.5) * 100 +
|
||
0.20 * normalize(metrics.aspDeclineRate, 0, 50) * 100 +
|
||
0.15 * normalize(metrics.standardsMaturity, 0, 100) * 100 +
|
||
0.15 * normalize(metrics.interopLevel, 0, 100) * 100 +
|
||
0.10 * (metrics.vendorTrend === "increasing" ? 30 : metrics.vendorTrend === "stable" ? 60 : 90) +
|
||
0.10 * (100 - metrics.mediaHypeIndex)
|
||
);
|
||
|
||
// 5-year forecast
|
||
const fiveYearProjection = Array.from({ length: 5 }, (_, i) => {
|
||
const futureYear = currentYear + i + 1;
|
||
const futureT = futureYear - tech.introYear;
|
||
const futureAdoption = bassCumulativeAdoption(futureT, tech.p, tech.q);
|
||
const futureProgressRatio = (futureYear - tech.introYear) / yearsToPeak;
|
||
const futureMetrics: PhaseMetrics = {
|
||
...metrics,
|
||
shipmentShare: Math.min(0.5, logisticAdoption(futureYear, tech.m, tech.k, tech.t0) / 1000),
|
||
aspDeclineRate: estimateAspDecline(futureProgressRatio),
|
||
vendorTrend: estimateVendorTrend(futureProgressRatio),
|
||
};
|
||
return {
|
||
year: futureYear,
|
||
adoptionPct: Math.round(futureAdoption * 100),
|
||
phase: (futureYear > tech.peakYear + 8 && futureMetrics.shipmentShare < 0.05)
|
||
? "LEGACY_DECLINE" as HypeCyclePhase
|
||
: classifyPhase(futureMetrics),
|
||
};
|
||
});
|
||
|
||
return {
|
||
technology: tech.name,
|
||
phase: finalPhase,
|
||
phaseLabel: PHASE_LABELS[finalPhase],
|
||
positionPct: Math.round(positionPct),
|
||
adoptionPct: Math.round(cumulativeAdoption * 100),
|
||
compositeScore,
|
||
forecast: {
|
||
currentYear,
|
||
yearsToPlateauFromNow: Math.max(0, tech.peakYear + 3 - currentYear),
|
||
peakShipmentYear: tech.peakYear,
|
||
cumulativeAdoptionPct: Math.round(cumulativeAdoption * 100),
|
||
yearlyAdoptionPct: Math.round(adoptionRate * 100),
|
||
fiveYearProjection,
|
||
},
|
||
metrics,
|
||
};
|
||
}
|
||
|
||
// ============================================================
|
||
// Estimation heuristics (from model parameters)
|
||
// ============================================================
|
||
|
||
function estimateAspDecline(progressRatio: number): number {
|
||
// ASP decline accelerates through Trough, stabilizes at Plateau
|
||
if (progressRatio < 0.3) return 5; // Early: slow decline
|
||
if (progressRatio < 0.6) return 35; // Peak/Trough: rapid decline
|
||
if (progressRatio < 1.0) return 15; // Slope: moderate decline
|
||
return 5; // Plateau: minimal decline
|
||
}
|
||
|
||
function estimateStandardsMaturity(progressRatio: number): number {
|
||
if (progressRatio < 0.2) return 20; // Draft standards
|
||
if (progressRatio < 0.5) return 60; // Published, some revisions
|
||
if (progressRatio < 0.8) return 85; // Mature standards
|
||
return 95; // Fully mature
|
||
}
|
||
|
||
function estimateInteropLevel(progressRatio: number): number {
|
||
if (progressRatio < 0.3) return 25; // Limited interop testing
|
||
if (progressRatio < 0.6) return 55; // Growing interop
|
||
if (progressRatio < 0.9) return 80; // Broad interop
|
||
return 95; // Universal interop
|
||
}
|
||
|
||
function estimateVendorCount(progressRatio: number): number {
|
||
if (progressRatio < 0.3) return 5;
|
||
if (progressRatio < 0.6) return 15;
|
||
if (progressRatio < 1.0) return 25;
|
||
return 20; // Consolidation
|
||
}
|
||
|
||
function estimateVendorTrend(progressRatio: number): "increasing" | "stable" | "decreasing" {
|
||
if (progressRatio < 0.5) return "increasing";
|
||
if (progressRatio < 1.2) return "stable";
|
||
return "decreasing";
|
||
}
|
||
|
||
function estimateMediaHype(progressRatio: number): number {
|
||
// Hype peaks early (Peak of Inflated Expectations)
|
||
if (progressRatio < 0.15) return 40; // Innovation: moderate buzz
|
||
if (progressRatio < 0.30) return 85; // Peak: maximum hype
|
||
if (progressRatio < 0.50) return 25; // Trough: hype collapse
|
||
if (progressRatio < 0.80) return 50; // Slope: balanced coverage
|
||
return 30; // Plateau: boring = good
|
||
}
|
||
|
||
// ============================================================
|
||
// Compute all technologies at once
|
||
// ============================================================
|
||
|
||
export function computeAllHypeCycles(
|
||
currentYear: number = new Date().getFullYear(),
|
||
): ReadonlyArray<HypeCycleResult> {
|
||
const allTechs = [...TECH_GENERATIONS, ...SPECIAL_TECHS];
|
||
return allTechs.map((tech) => computeHypeCycle(tech, currentYear));
|
||
}
|
||
|
||
export function findTechnology(query: string): TechGeneration | undefined {
|
||
const q = query.toLowerCase();
|
||
const allTechs = [...TECH_GENERATIONS, ...SPECIAL_TECHS];
|
||
return allTechs.find((t) =>
|
||
t.name.toLowerCase().includes(q) ||
|
||
q.includes(t.speedGbps.toString()) ||
|
||
q.includes(t.formFactor.toLowerCase())
|
||
);
|
||
}
|