A comparison between the classical Skiba & Clarke NLLS method and a hierarchical Bayesian extension — NLSS (Nonlinear Least Squares with Shrinkage) — that regularizes parameter estimation via a population-derived Mahalanobis prior, requiring only daily TSS and occasional power tests.Una comparación entre el método clásico de Skiba & Clarke (NLLS) y una extensión bayesiana jerárquica — NLSS (Mínimos Cuadrados No Lineales con Contracción) — que regulariza la estimación de parámetros mediante un prior de Mahalanobis derivado de la población, requiriendo únicamente TSS diario y tests de potencia ocasionales.
Banister's IR-Model (1975) describes athletic performance as the superposition of two exponential responses to training load: a slow positive one — fitness — and a fast negative one — fatigue. Skiba & Clarke formalized the core question: how much does a session performed on day \(\tau\) contribute to the projected performance on day \(t\)?El modelo IR de Banister (1975) describe el rendimiento deportivo como la superposición de dos respuestas exponenciales a la carga de entrenamiento: una positiva lenta — fitness — y una negativa rápida — fatiga. Skiba & Clarke formalizaron la pregunta central: ¿cuánto contribuye una sesión realizada el día \(\tau\) al rendimiento proyectado en el día \(t\)?
The curve is biphasic: fatigue dominates immediately (fast decay \(T_2\)); after a zero-crossing the adaptation emerges (slow decay \(T_1\)). Projected performance on day \(t\) is the cumulative convolution of the entire TSS history:La curva es bifásica: la fatiga domina inmediatamente (decaimiento rápido \(T_2\)); después de un cruce por cero emerge la adaptación (decaimiento lento \(T_1\)). El rendimiento proyectado en el día \(t\) es la convolución acumulada de todo el historial de TSS:
| ParameterParámetro | MeaningSignificado | Practical effectEfecto práctico | TrainingPeaks (fixedfijo) |
|---|---|---|---|
| \(K_1\) | Fitness gain per TSS unitGanancia de fitness por unidad de TSS | How much fitness rises per sessionCuánto sube el fitness por sesión | 1.0 — underestimated ×2.91.0 — subestimado ×2.9 |
| \(K_2\) | Fatigue gain per TSS unitGanancia de fatiga por unidad de TSS | How much form drops per sessionCuánto cae la forma por sesión | 1.0 — underestimated ×4.11.0 — subestimado ×4.1 |
| \(T_1\) | Fitness time constantConstante temporal de fitness | Days the positive effect persistsDías que persiste el efecto positivo | 42 d — approximate mean42 d — media aproximada |
| \(T_2\) | Fatigue time constantConstante temporal de fatiga | Days for fatigue to dissipateDías para que se disipe la fatiga | 7 d — approximate mean7 d — media aproximada |
Select a preset or move the sliders. The zero-crossing is the day the net effect turns positive. The form peak shows how many days after a session the athlete reaches peak readiness. Compare TrainingPeaks vs AGMT2 Mean to see the concrete impact of incorrect \(K_1\) and \(K_2\).Seleccione un preset o mueva los controles. El cruce por cero es el día en que el efecto neto se vuelve positivo. El pico de forma indica cuántos días después de una sesión el atleta alcanza su mejor disposición. Compare TrainingPeaks vs AGMT2 Mean para ver el impacto concreto de \(K_1\) y \(K_2\) incorrectos.
The approach proposed by Skiba & Clarke for estimating the four parameters is nonlinear least squares (NLLS): find the set \(\{K_1,K_2,T_1,T_2\}\) that minimizes the discrepancy between predicted performance \(\hat{p}(d;\theta)\) and the maximum power tests collected throughout the season.El enfoque propuesto por Skiba & Clarke para estimar los cuatro parámetros es mínimos cuadrados no lineales (NLLS): encontrar el conjunto \(\{K_1,K_2,T_1,T_2\}\) que minimiza la discrepancia entre el rendimiento predicho \(\hat{p}(d;\theta)\) y los tests de potencia máxima recolectados a lo largo de la temporada.
Data scarcity. Calibrating 4 nonlinear parameters requires tests distributed across distinct phases. Most athletes accumulate 4–8 tests per year, yielding an observations-to-parameters ratio of ~1.5:1.Escasez de datos. Calibrar 4 parámetros no lineales requiere tests distribuidos en fases distintas. La mayoría de los atletas acumulan 4–8 tests por año, con una ratio observaciones/parámetros de ~1.5:1.
Non-identifiability. The loss landscape has multiple equivalent local minima (Busso, 2003). Different \(\{K_1,K_2,T_1,T_2\}\) combinations produce identical fits on sparse data.No-identificabilidad. El paisaje de la función de costo tiene múltiples mínimos locales equivalentes (Busso, 2003). Diferentes combinaciones de \(\{K_1,K_2,T_1,T_2\}\) producen ajustes idénticos con datos escasos.
No signal between tests. The 350+ daily TSS values between tests are used only as convolution input — never as a direct calibration signal.Sin señal entre tests. Los 350+ valores diarios de TSS entre tests se usan solo como entrada de convolución — nunca como señal directa de calibración.
Fixed values for the whole population. Rather than solving NLLS, TP sets \(K_1=K_2=1,\;T_1=42\text{d},\;T_2=7\text{d}\) for all athletes.Valores fijos para toda la población. En lugar de resolver NLLS, TP establece \(K_1=K_2=1,\;T_1=42\text{d},\;T_2=7\text{d}\) para todos los atletas.
Systematically incorrect \(K_1\) and \(K_2\). True AGMT2 values are \(K_1\approx2.87\) and \(K_2\approx4.09\) — ×2.9 and ×4.1 larger. The PMC simultaneously underestimates fitness and fatigue.\(K_1\) y \(K_2\) sistemáticamente incorrectos. Los valores reales de AGMT2 son \(K_1\approx2.87\) y \(K_2\approx4.09\) — ×2.9 y ×4.1 mayores. El PMC subestima simultáneamente fitness y fatiga.
Measurable taper error. An athlete with \(T_1=36.4\text{d}\) would arrive at competition with 6 days of invisible residual fatigue when using TP's parameters.Error de taper medible. Un atleta con \(T_1=36.4\text{d}\) llegaría a la competición con 6 días de fatiga residual invisible usando los parámetros de TP.
The chart shows simulated season performance for four representations. Vary the number of tests and their distribution to see how classical NLLS diverges from the true performance when data are scarce.El gráfico muestra rendimiento de temporada simulado para cuatro representaciones. Varíe la cantidad de tests y su distribución para ver cómo el NLLS clásico diverge del rendimiento real cuando los datos son escasos.
The proposed improvement requires no additional sensors. It uses the same daily TSS any athlete already records, but applies two fundamental changes to the classical estimator.La mejora propuesta no requiere sensores adicionales. Utiliza el mismo TSS diario que cualquier atleta ya registra, pero aplica dos cambios fundamentales al estimador clásico.
1. Hierarchical Bayesian prior.1. Prior Bayesiano Jerárquico. Instead of searching unconstrained \(\mathbb{R}^4\), the estimator starts from the empirical distribution observed in the 30-athlete AGMT2 cohort. The Mahalanobis penalty prevents convergence to spurious local minima and corrects \(K_1\) and \(K_2\) from day one.En lugar de buscar en \(\mathbb{R}^4\) sin restricciones, el estimador comienza a partir de la distribución empírica observada en la cohorte de 30 atletas AGMT2. La penalización de Mahalanobis evita la convergencia a mínimos locales espurios y corrige \(K_1\) y \(K_2\) desde el primer día.
2. 90-day sliding window.2. Ventana deslizante de 90 días. Rather than waiting for all season tests before calibrating, the estimator recalibrates every week — capturing mid-season fitness changes and reducing dependence on sporadic tests.En lugar de esperar todas las pruebas de la temporada antes de calibrar, el estimador se recalibra cada semana, capturando cambios de fitness intra-temporada y reduciendo la dependencia de pruebas esporádicas.
The population prior \(\mathcal{P}_\text{pop}=\mathcal{N}(\mu_\text{pop},\Sigma_\text{pop})\) is built from the 30 calibrated AGMT2 athletes (intervals are ±2σ):El prior poblacional \(\mathcal{P}_\text{pop}=\mathcal{N}(\mu_\text{pop},\Sigma_\text{pop})\) se construye a partir de los 30 atletas AGMT2 calibrados (los intervalos son ±2σ):
The method replaces the unconstrained NLLS minimization with a regularized optimization via analytic gradient, executed every week on a sliding window. Each component is described with sufficient precision for direct implementation.El método reemplaza la minimización NLLS sin restricciones con una optimización regularizada mediante gradiente analítico, ejecutada cada semana en una ventana deslizante. Cada componente se describe con precisión suficiente para implementación directa.
The Bayesian method adds a second term penalizing departure from the known cohort distribution:El método Bayesiano añade un segundo término que penaliza el alejamiento de la distribución de la cohorte conocida:
\(\lambda\) decreases with the number of tests in the current window. With few data points the prior dominates; with many, the athlete progressively individualizes.\(\lambda\) disminuye con el número de pruebas en la ventana actual. Con pocos puntos de datos el prior domina; con muchos, el atleta se individualiza progresivamente.
Optimization is performed with L-BFGS-B (Byrd et al., 1995), which requires \(\partial\mathcal{L}/\partial\theta\). The gradient has two summable components:La optimización se realiza con L-BFGS-B (Byrd et al., 1995), que requiere \(\partial\mathcal{L}/\partial\theta\). El gradiente tiene dos componentes sumables:
L-BFGS-B enforces per-parameter bounds, eliminating physiologically impossible solutions. Combined with the Mahalanobis prior, they remove virtually all spurious local minima of the unconstrained NLLS.L-BFGS-B impone límites por parámetro, eliminando soluciones fisiológicamente imposibles. Combinados con el prior de Mahalanobis, eliminan virtualmente todos los mínimos locales espurios del NLLS sin restricciones.
| ParameterParámetro | Lower boundLímite inferior | Upper boundLímite superior | Physiological justificationJustificación fisiológica |
|---|---|---|---|
| \(K_1\) | 0.20 | 8.00 | Fitness gain cannot be negative or disproportionateLa ganancia de fitness no puede ser negativa o desproporcionada |
| \(K_2\) | 0.20 | 12.00 | Fatigue gain cannot be negativeLa ganancia de fatiga no puede ser negativa |
| \(T_1\) | 14 days | 80 days | Physiologically impossible outside this rangeFisiológicamente imposible fuera de este rango |
| \(T_2\) | 3 days | 18 days | Acute fatigue cannot last months or vanish in hoursLa fatiga aguda no puede durar meses ni desaparecer en horas |
The recalibration algorithm runs every 7 days. The warm start (initializing L-BFGS-B from the previous week's \(\theta\)) reduces iterations from ~200 to ~30 in stable weeks, making the algorithm viable on limited hardware.El algoritmo de recalibración se ejecuta cada 7 días. El warm start (inicializar L-BFGS-B desde el \(\theta\) de la semana anterior) reduce las iteraciones de ~200 a ~30 en semanas estables, haciendo el algoritmo viable en hardware limitado.
PARAMETERS: L = 90 days (window) · tol = 1e-5 · max_iter = 500 λ_max = 5.0 · n_half = 4 INITIALIZATION (season day 0): θ_current ← μ_pop = [2.87, 4.09, 40.64, 6.58] WEEKLY LOOP (every 7 days, on day t): 1. UPDATE WINDOW W_t ← { power tests in range [t − L, t] } n ← |W_t| // number of tests in window 2. UPDATE λ λ ← λ_max · exp(−n / n_half) // eq. (6) 3. IF n == 0 // no tests in window θ_current ← μ_pop // no data → pure prior CONTINUE to next cycle 4. BUILD PREDICTION for each test d ∈ W_t: p̂(d;θ) ← p₀ + Σ_{τ<d} TSS(τ)·[K₁·exp(−(d−τ)/T₁) − K₂·exp(−(d−τ)/T₂)] 5. OPTIMIZE with L-BFGS-B: θ_init ← θ_current // warm start from previous week θ_new ← argmin L(θ) subject to box constraints using analytic gradient eqs. (7)–(9) 6. CONVERGENCE CHECK & REJECTION: IF ‖θ_new − θ_current‖ / ‖θ_current‖ < tol: θ_current ← θ_new // normal convergence ELSE IF ‖θ_new − μ_pop‖ > 3·diag(Σ_pop)^0.5: θ_current ← θ_current // REJECT: outside 3σ of prior ELSE: θ_current ← θ_new 7. STORE {t, θ_current, λ, n} in athlete history
Step 5 initializes L-BFGS-B from the previous week's \(\theta\). This cuts iterations from ~200 to ~30 in stable weeks — what makes the algorithm viable on limited hardware. Without warm start, unconstrained NLLS can take 10× longer and converge to a different minimum every run, producing inconsistent estimates.El paso 5 inicializa L-BFGS-B desde el \(\theta\) de la semana anterior. Esto reduce las iteraciones de ~200 a ~30 en semanas estables — lo que hace el algoritmo viable en hardware limitado. Sin warm start, el NLLS sin restricciones puede tomar 10× más tiempo y converger a un mínimo diferente en cada ejecución, produciendo estimaciones inconsistentes.
An abrupt jump in parameters between consecutive weeks may signal a real physiological state change (altitude block, illness, form peak). The algorithm detects this via the relative parameter shift:Un salto abrupto en los parámetros entre semanas consecutivas puede señalar un cambio real en el estado fisiológico (bloque de altitud, enfermedad, pico de forma). El algoritmo detecta esto mediante el cambio relativo de parámetros:
Classical NLLSNLLS Clásico solves once at season end using all available tests. If an athlete improves FTP by 8% mid-season, that change is invisible until the next annual cycle.resuelve una sola vez al final de la temporada usando todas las pruebas disponibles. Si un atleta mejora su FTP un 8% intra-temporada, ese cambio es invisible hasta el próximo ciclo anual.
Hierarchical Bayesian methodMétodo Bayesiano Jerárquico recalibrates weekly. If an altitude block in week 20 raises the athlete's \(K_1\) response, parameters reflect it by week 21. The sliding window also discards tests older than 90 days no longer representative of the current physiological state.se recalibra semanalmente. Si un bloque de altitud en la semana 20 aumenta la respuesta \(K_1\) del atleta, los parámetros lo reflejan en la semana 21. La ventana deslizante también descartar pruebas más antiguas de 90 días que ya no son representativas del estado fisiológico actual.
\(\theta=\mu_\text{pop}\), \(\lambda=5.0\). Prior dominates completely. Even without individualization, \(K_1=2.87\) and \(K_2=4.09\) are already population-correct — vastly better than TrainingPeaks' fixed \(K_1=K_2=1\).\(\theta=\mu_\text{pop}\), \(\lambda=5.0\). Prior domina completamente. Incluso sin individualización, \(K_1=2.87\) y \(K_2=4.09\) ya son poblacionalmente correctos — vastamente mejor que el valor fijo \(K_1=K_2=1\) de TrainingPeaks.
\(\lambda\approx3.03\). The data gradient \(\partial\mathcal{L}_\text{data}\) is weak (single observation) but regularized by \(3.03\cdot\partial\mathcal{L}_\text{prior}\). \(\theta\) shifts slightly toward the test value without jumping to a spurious minimum. Unconstrained NLLS with \(n=1\) is numerically unstable; this method is not.\(\lambda\approx3.03\). El gradiente de datos \(\partial\mathcal{L}_\text{data}\) es débil (observación única) pero regularizado por \(3.03\cdot\partial\mathcal{L}_\text{prior}\). \(\theta\) se desplaza ligeramente hacia el valor del test sin saltar a un mínimo espurio. NLLS sin restricciones con \(n=1\) es numéricamente inestable; este método no.
\(\lambda\approx1.58\). The \(\mathcal{L}_\text{data}/\mathcal{L}_\text{prior}\) balance lets the optimizer resolve the identifiability of \(T_1\) vs \(T_2\): tests from different load phases provide sufficient differential signal to distinguish slow from fast decay constants.\(\lambda\approx1.58\). El balance \(\mathcal{L}_\text{data}/\mathcal{L}_\text{prior}\) permite al optimizador resolver la identificabilidad de \(T_1\) vs \(T_2\): tests de fases de carga diferentes proporcionan señal diferencial suficiente para distinguir constantes de decaimiento lentas de rápidas.
\(\lambda\leq0.67\). Data dominate. The prior acts only as a soft regularizer — prevents \(K_1\) from reaching extreme values like 0.3 or 7.5, but allows data to determine the athlete's exact value. The solution is essentially classical NLLS but without spurious local minima.\(\lambda\leq0.67\). Datos dominan. El prior actúa solo como regulador suave — evita que \(K_1\) alcance valores extremos como 0.3 o 7.5, pero permite que los datos determinen el valor exacto del atleta. La solución es esencialmente NLLS clásico pero sin mínimos locales espurios.
Curves show how each method estimates the selected parameter as tests accumulate. The dashed orange line is the athlete's true value. The Bayesian estimator starts at the prior mean and converges faster because the Mahalanobis penalty eliminates the spurious minima that destabilize NLLS with sparse data.Las curvas muestran cómo cada método estima el parámetro seleccionado a medida que se acumulan los tests. La línea naranja punteada es el valor verdadero del atleta. El estimador Bayesiano comienza en la media prior y converge más rápido porque la penalización de Mahalanobis elimina los mínimos espurios que desestabilizan el NLLS con datos escasos.
| AspectAspecto | TrainingPeaks (fixed)TrainingPeaks (fijo) | Classical NLLS (Skiba)NLLS Clásico (Skiba) | Bayesian hierarchical TSSBayesiano jerárquico TSS |
|---|---|---|---|
| Data requiredDatos requeridos | TSS onlySolo TSS | TSS + phase-distributed testsTSS + tests distribuidos por fases | TSS + occasional testsTSS + tests ocasionales |
| IndividualizationIndividualización | None (population)Ninguna (poblacional) | Full (with enough tests)Completa (con suficientes tests) | Progressive from priorProgresiva desde prior |
| Correct \(K_1,K_2\)Corrección \(K_1,K_2\) | No — ×3 and ×4 underestimatedNo — ×3 y ×4 subestimados | Yes, with sufficient testsSí, con tests suficientes | Yes — prior corrects from day 1Sí — prior corrige desde día 1 |
| With only 2 testsCon solo 2 tests | Same (ignores tests)Igual (ignora tests) | Unstable / spurious minimaInestable / mínimos espurios | Better than TrainingPeaksMejor que TrainingPeaks |
| RecalibrationRecalibración | NeverNunca | End of seasonFin de temporada | Weekly (90-day window)Semanal (ventana 90 días) |
| Requires extra sensorsRequiere sensores extra | NoNo | NoNo | No — power meter onlyNo — solo potenciómetro |
| Typical taper error (simulated)Error de predicción de taper típico (simulado) | ±5–8 daysdías | ±2–4 days (6 tests)días (6 tests) | ±1–2 days (3–4 tests)días (3–4 tests) |
Simulation of 12 weeks of progressive load followed by a taper. The orange star marks the athlete's true form peak \(t^*\). The error in days determines whether the athlete arrives optimally fresh or still carrying fatigue on race day.Simulación de 12 semanas de carga progresiva seguidas de un taper. La estrella naranja marca el pico de forma verdadero del atleta \(t^*\). El error en días determina si el atleta llega óptimamente fresco o todavía cargando fatiga en el día de carrera.
\(K_1=2.87,\;K_2=4.09,\;T_1=40.6\text{d},\;T_2=6.6\text{d}\) con \(\lambda=\lambda_{\max}\). Already beats TrainingPeaks from day zero becauseYa supera a TrainingPeaks desde el primer día porque \(K_1\) y \(K_2\) reflejan la distribución real de la cohorte, no el valor fijo 1.0.
Test 1 in Base phase (low load), Test 2 in Build phase (load peak), Test 3 post-short-taper (4–5 days). This distribution lets the Mahalanobis prior separateTest 1 en fase Base (baja carga), Test 2 en fase Build (pico de carga), Test 3 post-short-taper (4–5 días). Esta distribución permite al prior de Mahalanobis separar \(T_1\) de \(T_2\) y ajustar \(K_1/K_2\) individualmente.
Updates \(\hat\theta\) using the tests within the last 90 days plus the updated prior. No tests in window → prior dominates (conservative). With 2+ tests → progressive individualization.Actualiza \(\hat\theta\) utilizando los tests dentro de los últimos 90 días más el prior actualizado. Sin tests en ventana → prior domina (conservador). Con 2+ tests → individualización progresiva.
TSB predictions should correlate with the athlete's subjective perception. Low correlation indicates parameters needing more data, or external factors not captured by TSS alone.Las predicciones de TSB deben correlacionar con la percepción subjetiva del atleta. Baja correlación indica parámetros que necesitan más datos, o factores externos no capturados por TSS solo.
Only required:Solo se requiere: daily TSS logged consistently (power meter, Stryd, or calibrated HR), 3 maximum power tests distributed across distinct season phases, and the prior vector \(\{\mu_\text{pop},\Sigma_\text{pop}\}\) from the AGMT2 cohort.TSS diario registrado consistentemente (medidor de potencia, Stryd, o HR calibrado), 3 tests de potencia máxima distribuidos en fases distintas de la temporada, y el vector prior \(\{\mu_\text{pop},\Sigma_\text{pop}\}\) de la cohorte AGMT2.
No nocturnal HRV or additional sensors needed. The prior acts as "shared knowledge" from the cohort that benefits any athlete with limited personal data.No se necesita HRV nocturno ni sensores adicionales. El prior actúa como "conocimiento compartido" de la cohorte que beneficia a cualquier atleta con datos personales limitados.
The following four points were raised in external peer review of this methodology. Each identifies a genuine limitation of the current formulation and proposes a concrete mathematical extension. These are open research directions for the next iteration of the model.Los siguientes cuatro puntos fueron planteados en revisión por pares externa de esta metodología. Cada uno identifica una limitación genuina de la formulación actual y propone una extensión matemática concreta. Estas son direcciones de investigación abiertas para la próxima iteración del modelo.
Critique.Crítica. The model's Bayesian correction depends critically on \(\mu_\text{pop}\) and \(\Sigma_\text{pop}\) from the 30-athlete AGMT2 cohort. If that cohort consists of elite cyclists and the user is an amateur triathlete, the prior may introduce systematic bias rather than correcting it. A single population prior assumes all athletes belong to the same physiological distribution — an assumption that fails across age, sex, sport modality, and training age.La corrección bayesiana del modelo depende críticamente de \(\mu_\text{pop}\) y \(\Sigma_\text{pop}\) de la cohorte AGMT2 de 30 atletas. Si esa cohorte consiste en ciclistas de élite y el usuario es un triatleta aficionado, el prior puede introducir sesgo sistemático en lugar de corregirlo. Un prior poblacional único asume que todos los atletas pertenecen a la misma distribución fisiológica — una suposición que falla en edad, sexo, modalidad deportiva y edad de entrenamiento.
Proposed extension: hierarchical sub-priors.Extensión propuesta: sub-priors jerárquicos. Partition the cohort into \(G\) groups by profile vector \(z_i = [\text{sport},\,\text{level},\,\text{age\_bin},\,\text{sex}]\). For each group \(g\), estimate a separate prior from the athletes in that group:Dividir la cohorte en \(G\) grupos por vector de perfil \(z_i = [\text{sport},\,\text{level},\,\text{age\_bin},\,\text{sex}]\). Para cada grupo \(g\), estimar un prior separado de los atletas en ese grupo:
At inference time, select the matching group prior for athlete \(i\) and fall back to the global prior when \(|g| < n_\text{min}\) (e.g., fewer than 5 athletes in the group):En tiempo de inferencia, seleccionar el prior de grupo coincidente para el atleta \(i\) y retroceder al prior global cuando \(|g| < n_\text{min}\) (por ejemplo, menos de 5 atletas en el grupo):
A further extension uses soft group membership: a mixture prior with weights \(\pi_g\) proportional to the similarity between the new athlete's profile and each group centroid, implemented as a convex combination of group Gaussians. This avoids hard cluster boundaries and handles athletes who span multiple profiles (e.g., elite amateur triathlete with cycling background).Una extensión adicional usa membresía de grupo suave: un prior de mezcla con pesos \(\pi_g\) proporcionales a la similitud entre el perfil del nuevo atleta y cada centroide de grupo, implementado como una combinación convexa de gaussianas de grupo. Esto evita límites de cluster duros y maneja atletas que abarcan múltiples perfiles (por ejemplo, triatleta aficionado de élite con experiencia en ciclismo).
The present cohort of 30 athletes is insufficient to estimate stable sub-group covariance matrices — particularly for subgroups with fewer than 8–10 members. Expanding the cohort to 150–200 athletes with explicit profile stratification is the prerequisite for implementing sub-priors reliably.La cohorte actual de 30 atletas es insuficiente para estimar matrices de covarianza de subgrupos estables — particularmente para subgrupos con menos de 8–10 miembros. Expandir la cohorte a 150–200 atletas con estratificación de perfil explícita es el prerrequisito para implementar sub-priors de manera confiable.
Critique.Crítica. TSS collapses an entire session into a single scalar. Two sessions with identical TSS can elicit very different adaptive responses: 4 hours of endurance base and a 60-minute VO₂max interval session may produce the same TSS yet drive distinct molecular signaling cascades (AMPK vs. mTOR pathways, different fiber recruitment patterns). The model's \(K_1\) and \(K_2\) implicitly average over these differences, potentially creating systematic bias in athletes with polarized or pyramidal periodization.TSS colapsa una sesión completa en un escalar único. Dos sesiones con TSS idéntico pueden provocar respuestas adaptativas muy diferentes: 4 horas de base de resistencia y una sesión de intervalos de 60 minutos VO₂max pueden producir el mismo TSS pero impulsar cascadas de señalización molecular distintas (vías AMPK vs. mTOR, patrones de reclutamiento de fibras diferentes). Los \(K_1\) y \(K_2\) del modelo promedian implícitamente estas diferencias, potencialmente creando sesgo sistemático en atletas con periodización polarizada o piramidal.
This is a limitation inherited from the Banister IR-Model itself, not introduced by the Bayesian extension. The classical Skiba & Clarke NLLS and TrainingPeaks share the same limitation. The contribution of this paper is improving parameter estimation within that model family, not replacing the model.Esta es una limitación heredada del modelo IR de Banister mismo, no introducida por la extensión bayesiana. El NLLS clásico de Skiba & Clarke y TrainingPeaks comparten la misma limitación. La contribución de este artículo es mejorar la estimación de parámetros dentro de esa familia de modelos, no reemplazar el modelo.
Proposed extension: domain-decomposed load signal.Extensión propuesta: señal de carga descompuesta por dominio. Replace the scalar TSS with a vector of intensity-domain loads \(\boldsymbol{s}(\tau) = [s_1(\tau),\, s_2(\tau),\, s_3(\tau)]^\top\) separating zone 1–2 (aerobic base), zone 3 (threshold), and zone 4–5 (VO₂max/anaerobic) contributions, following the three-zone intensity model of Seiler (2010):Reemplazar el TSS escalar con un vector de cargas de dominio de intensidad \(\boldsymbol{s}(\tau) = [s_1(\tau),\, s_2(\tau),\, s_3(\tau)]^\top\) separando zona 1–2 (base aeróbica), zona 3 (umbral), y contribuciones de zona 4–5 (VO₂max/anaeróbica), siguiendo el modelo de intensidad de tres zonas de Seiler (2010):
This expands the parameter space from 4 to 12. The NLLS problem becomes significantly harder, but the Bayesian prior plays an even more critical stabilizing role: zone-specific \(\mu^{(k)}_\text{pop}\) and \(\Sigma^{(k)}_\text{pop}\) can be estimated from athletes with complete intensity-distribution records, and transferred to athletes with only aggregate TSS via a zone-imputation model.Esto expande el espacio de parámetros de 4 a 12. El problema NLLS se vuelve significativamente más difícil, pero el prior bayesiano juega un papel estabilizador aún más crítico: \(\mu^{(k)}_\text{pop}\) y \(\Sigma^{(k)}_\text{pop}\) específicos de zona pueden estimarse de atletas con registros de distribución de intensidad completos, y transferirse a atletas con solo TSS agregado mediante un modelo de imputación de zona.
A practical compromise without full zone decomposition is to add a single intensity ratio feature \(\rho(\tau) = \text{IF}(\tau) / \overline{\text{IF}}\), where IF is the intensity factor of the session. This preserves a scalar load signal while encoding whether a session was "harder than usual for its duration," partially capturing the aerobic vs. anaerobic split without requiring full zone data.Un compromiso práctico sin descomposición de zona completa es agregar un único feature de razón de intensidad \(\rho(\tau) = \text{IF}(\tau) / \overline{\text{IF}}\), donde IF es el factor de intensidad de la sesión. Esto preserva una señal de carga escalar mientras codifica si una sesión fue "más dura que lo usual para su duración," capturando parcialmente la división aeróbica vs. anaeróbica sin requerir datos de zona completos.
Critique.Crítica. Maximum power tests (20', 5', etc.) are inherently noisy: motivation, pre-test nutrition, ambient temperature, and pacing strategy introduce measurement error that can easily shift a test result by 3–5%. Under the current squared-error loss \(\mathcal{L}_\text{data}\), a single anomalous test exerts quadratically larger influence on the gradient — it can dominate the optimization and drive the parameters toward a locally spurious fit.Los tests de potencia máxima (20', 5', etc.) son inherentemente ruidosos: la motivación, nutrición pre-test, temperatura ambiente, y estrategia de ritmo introducen errores de medición que pueden fácilmente desplazar un resultado de test por 3–5%. Bajo la actual pérdida de error cuadrado \(\mathcal{L}_\text{data}\), un único test anómalo ejerce una influencia cuadráticamente mayor en el gradiente — puede dominar la optimización e impulsar los parámetros hacia un ajuste localmente espurio.
The squared-error loss in eq. (5a) assigns quadratically growing penalties to large residuals: one test with a 15 W deficit contributes 225× more than one with a 1 W residual. In a window of \(n=4\) tests, a single bad test controls 56% of the gradient signal.La pérdida de error cuadrado en eq. (5a) asigna penalizaciones que crecen cuadráticamente a residuos grandes: un test con un déficit de 15 W contribuye 225× más que uno con un residuo de 1 W. En una ventana de \(n=4\) tests, un único test malo controla el 56% de la señal de gradiente.
Proposed extension: Huber loss.Extensión propuesta: pérdida de Huber. Replace the quadratic loss in \(\mathcal{L}_\text{data}\) with the Huber loss \(\mathcal{H}_\delta\), which is quadratic near zero (preserving gradient sensitivity for small residuals) but linear in the tails (downweighting large residuals):Reemplazar la pérdida cuadrática en \(\mathcal{L}_\text{data}\) con la pérdida de Huber \(\mathcal{H}_\delta\), que es cuadrática cerca de cero (preservando sensibilidad de gradiente para residuos pequeños) pero lineal en las colas (downweighting de residuos grandes):
The robust data term becomes:El término de datos robusto se convierte en:
The gradient of the Huber loss is piecewise, requiring a small modification to eq. (8):El gradiente de la pérdida de Huber es por partes, requiriendo una pequeña modificación a eq. (8):
Calibrating \(\delta\) requires an estimate of the expected test noise. A principled choice is \(\delta = k_\sigma \cdot \hat{\sigma}_r\) where \(\hat{\sigma}_r\) is the empirical standard deviation of residuals from a pilot fit and \(k_\sigma \in [1.0, 1.5]\). For most power meter athletes, a threshold of \(\delta \approx 8\text{–}12\text{ W}\) on a 200–350 W baseline (approximately 3–5% CV) provides good robustness without sacrificing sensitivity to genuine fitness changes.Calibrar \(\delta\) requiere una estimación del ruido de test esperado. Una opción principiada es \(\delta = k_\sigma \cdot \hat{\sigma}_r\) donde \(\hat{\sigma}_r\) es la desviación estándar empírica de residuos de un ajuste piloto y \(k_\sigma \in [1.0, 1.5]\). Para la mayoría de atletas con medidor de potencia, un umbral de \(\delta \approx 8\text{–}12\text{ W}\) en una línea base de 200–350 W (aproximadamente 3–5% CV) proporciona buena robustez sin sacrificar sensibilidad a cambios genuinos de fitness.
| Loss functionFunción de pérdida | Small residualsResiduos pequeños | Large residuals (outlier test)Residuos grandes (test atípico) | Gradient behaviorComportamiento del gradiente |
|---|---|---|---|
| Squared (current)Cuadrada (actual) | QuadraticCuadrática | Quadratic — outlier dominatesCuadrática — atípico domina | Linear, unboundedLineal, no acotada |
| Huber \(\mathcal{H}_\delta\) | Quadratic (identical)Cuadrática (idéntica) | Linear — outlier capped at \(\delta\)Lineal — atípico limitado a \(\delta\) | Bounded by \(\delta\)Acotada por \(\delta\) |
| Absolute (L1)Absoluta (L1) | Linear — loses sensitivity near 0Lineal — pierde sensibilidad cerca de 0 | LinearLineal | Constant ±1 (non-smooth at 0)Constante ±1 (no suave en 0) |
Critique.Crítica. If \(\Sigma_\text{pop}\) is diagonal (i.e., parameters treated as independent), the Mahalanobis distance degenerates to a sum of independent penalties \(\sum_j ({\theta_j - \mu_j})^2/\sigma_j^2\). This ignores physiologically meaningful correlations between parameters — most notably the \((K_1, T_1)\) relationship: athletes who accumulate fitness rapidly (high \(K_1\)) tend to also lose it more rapidly (lower \(T_1\)), reflecting the faster turnover of their training stimulus.Si \(\Sigma_\text{pop}\) es diagonal (es decir, parámetros tratados como independientes), la distancia de Mahalanobis degenera a una suma de penalizaciones independientes \(\sum_j ({\theta_j - \mu_j})^2/\sigma_j^2\). Esto ignora correlaciones fisiológicamente significativas entre parámetros — más notablemente la relación \((K_1, T_1)\): atletas que acumulan fitness rápidamente (alto \(K_1\)) tienden a también perderlo más rápidamente (menor \(T_1\)), reflejando la rotación más rápida de su estímulo de entrenamiento.
Current implementation status.Estado de implementación actual. The AGMT2 implementation uses the full sample covariance matrix \(\hat{\Sigma}_\text{pop}\) estimated from the 30-athlete cohort:La implementación AGMT2 usa la matriz de covarianza de muestra completa \(\hat{\Sigma}_\text{pop}\) estimada de la cohorte de 30 atletas:
The off-diagonal structure of the current \(\hat{\Sigma}_\text{pop}\) estimated from the AGMT2 cohort shows the dominant correlations:La estructura fuera de la diagonal de la actual \(\hat{\Sigma}_\text{pop}\) estimada de la cohorte AGMT2 muestra las correlaciones dominantes:
| \(K_1\) | \(K_2\) | \(T_1\) | \(T_2\) | |
|---|---|---|---|---|
| \(K_1\) | \(\sigma^2_{K_1}\) | \(\rho\approx+0.63\) | \(\rho\approx-0.41\) | \(\rho\approx-0.12\) |
| \(K_2\) | \(\rho\approx+0.63\) | \(\sigma^2_{K_2}\) | \(\rho\approx+0.09\) | \(\rho\approx-0.28\) |
| \(T_1\) | \(\rho\approx-0.41\) | \(\rho\approx+0.09\) | \(\sigma^2_{T_1}\) | \(\rho\approx+0.19\) |
| \(T_2\) | \(\rho\approx-0.12\) | \(\rho\approx-0.28\) | \(\rho\approx+0.19\) | \(\sigma^2_{T_2}\) |
Practical consequence of ignoring off-diagonals.Consecuencia práctica de ignorar fuera de la diagonal. Consider an athlete where only \(K_1\) is being updated by a new test. With a diagonal prior, \(K_2\) is completely unconstrained by this update. With the full \(\hat{\Sigma}_\text{pop}\), a high observed \(K_1\) shifts the posterior expectation of \(K_2\) upward by \(\hat\rho_{K_1 K_2} \cdot \hat\sigma_{K_2}/\hat\sigma_{K_1} \cdot (K_1 - \mu_{K_1})\) — exactly as Bayesian inference prescribes. Given the cohort's \(\rho(K_1,K_2) \approx +0.63\), this cross-parameter update is substantial and physiologically meaningful.Considerar un atleta donde solo \(K_1\) está siendo actualizado por un nuevo test. Con un prior diagonal, \(K_2\) es completamente sin restricciones por esta actualización. Con la completa \(\hat{\Sigma}_\text{pop}\), un \(K_1\) observado alto desplaza la expectativa posterior de \(K_2\) hacia arriba por \(\hat\rho_{K_1 K_2} \cdot \hat\sigma_{K_2}/\hat\sigma_{K_1} \cdot (K_1 - \mu_{K_1})\) — exactamente como la inferencia bayesiana prescribe. Dado el \(\rho(K_1,K_2) \approx +0.63\) de la cohorte, esta actualización entre parámetros es sustancial y fisiológicamente significativa.
Caveat: estimation stability with \(N=30\).Advertencia: estabilidad de estimación con \(N=30\). A \(4\times4\) covariance matrix has 10 free parameters. With \(N=30\) observations the condition number of \(\hat\Sigma_\text{pop}\) is sensitive to outliers in the cohort. A regularized estimator (shrinkage toward the diagonal) is recommended:Una matriz de covarianza \(4\times4\) tiene 10 parámetros libres. Con \(N=30\) observaciones el número de condición de \(\hat\Sigma_\text{pop}\) es sensible a outliers en la cohorte. Se recomienda un estimador regularizado (shrinkage hacia la diagonal):
The formulation in eq. (5b) explicitly writes \(\Sigma_\text{pop}^{-1}\) as a full matrix and in eq. (9) \(\partial\mathcal{L}_\text{prior}/\partial\theta = 2\,\Sigma_\text{pop}^{-1}(\theta-\mu_\text{pop})\) — which is the full-matrix gradient. The criticism is valid if an implementation replaces \(\Sigma_\text{pop}^{-1}\) with a diagonal approximation for computational convenience. The AGMT2 implementation uses the full \(\hat\Sigma\) estimated from the 30-athlete dataset, including all six off-diagonal terms. The precision matrix \(\Sigma_\text{pop}^{-1}\) is computed once at initialization via Cholesky decomposition for numerical stability.La formulación en eq. (5b) explícitamente escribe \(\Sigma_\text{pop}^{-1}\) como una matriz completa y en eq. (9) \(\partial\mathcal{L}_\text{prior}/\partial\theta = 2\,\Sigma_\text{pop}^{-1}(\theta-\mu_\text{pop})\) — que es el gradiente de matriz completa. La crítica es válida si una implementación reemplaza \(\Sigma_\text{pop}^{-1}\) con una aproximación diagonal por conveniencia computacional. La implementación AGMT2 usa la completa \(\hat\Sigma\) estimada del dataset de 30 atletas, incluyendo los seis términos fuera de la diagonal. La matriz de precisión \(\Sigma_\text{pop}^{-1}\) se calcula una vez en la inicialización vía descomposición de Cholesky para estabilidad numérica.
| CritiqueCrítica | Current statusEstado actual | Extension requiredExtensión requerida | Readiness (N=30)Disponibilidad (N=30) | PriorityPrioridad |
|---|---|---|---|---|
| A. Prior representativenessRepresentatividad del prior | Single cohort prior, AGMT2 profilePrior de cohorte único, perfil AGMT2 | Sub-priors by athlete cluster, eq. (11)–(12)Sub-priors por cluster de atletas, eq. (11)–(12) | Requires N ≥ 100 per groupRequiere N ≥ 100 por grupo | Long-termLargo plazo |
| B. TSS signal limitationsLimitaciones de señal de TSS | Inherited from IR-ModelHeredado del modelo IR | Intensity-domain decomposition, eq. (13)Descomposición de dominio de intensidad, eq. (13) | Partial (IF ratio proxy available now)Parcial (proxy de razón IF disponible ahora) | Medium-termMediano plazo |
| C. Noisy test outliersTests atípicos ruidosos | Squared-error loss — sensitive to outliersPérdida de error cuadrado — sensible a atípicos | Huber-\(\delta\) loss function, eq. (14)–(14b)Función de pérdida Huber-\(\delta\), eq. (14)–(14b) | Implementable immediatelyImplementable inmediatamente | High — next releaseAlta — próxima versión |
| D. Parameter covarianceCovarianza de parámetros | Full \(\hat\Sigma\) implemented\(\hat\Sigma\) completa implementada | Ledoit-Wolf shrinkage, eq. (15a)Shrinkage de Ledoit-Wolf, eq. (15a) | Already estimated in AGMT2Ya estimada en AGMT2 | ResolvedResuelto |
The population prior \(\mathcal{P}_\text{pop}=\mathcal{N}(\mu_\text{pop},\Sigma_\text{pop})\) was calibrated from a cohort of 30 athletes monitored through the AGMT2/ednaLabs platform over a minimum of 6 months each. The following table summarizes the cohort demographics and monitoring conditions.La prior poblacional \(\mathcal{P}_\text{pop}=\mathcal{N}(\mu_\text{pop},\Sigma_\text{pop})\) fue calibrada a partir de una cohorte de 30 atletas monitoreados a través de la plataforma AGMT2/ednaLabs durante un mínimo de 6 meses cada uno. La siguiente tabla resume las características demográficas de la cohorte y las condiciones de monitoreo.
| CharacteristicCaracterística | ValueValor |
|---|---|
| Sample sizeTamaño de muestra | \(N = 30\) athletesatletas |
| Primary sportDeporte principal | Road cycling (73%), triathlon (17%), running (10%)Ciclismo de ruta (73%), triatlón (17%), running (10%) |
| Competitive levelNivel competitivo | Trained to well-trained (performance level 3–4, De Pauw et al., 2013)Entrenado a bien entrenado (nivel de rendimiento 3–4, De Pauw et al., 2013) |
| SexSexo | Male (87%), Female (13%)Masculino (87%), Femenino (13%) |
| Age rangeRango de edad | 24–52 years (median 34)años (mediana 34) |
| GeographyGeografía | South America (Argentina, primarily)Sudamérica (Argentina, principalmente) |
| Monitoring periodPeríodo de monitoreo | 6–18 months per athletemeses por atleta |
| Power tests per athleteTests de potencia por atleta | 4–12 (medianmediana 6) |
| Calibration methodMétodo de calibración | Individual NLLS with manual validation by coachNLLS individual con validación manual del entrenador |
| TSS sourceFuente de TSS | Garmin Connect via power meter (cycling) or Stryd (running)Garmin Connect vía medidor de potencia (ciclismo) o Stryd (running) |
Individual athlete data are not publicly available due to privacy constraints. The aggregate prior parameters \(\mu_\text{pop}\) and \(\Sigma_\text{pop}\) are fully specified in this document (§03 and §06-D) and are sufficient for independent implementation. The cohort will be expanded in future work to enable sub-group stratification as described in §06-A.Los datos individuales de los atletas no están disponibles públicamente debido a restricciones de privacidad. Los parámetros prior agregados \(\mu_\text{pop}\) y \(\Sigma_\text{pop}\) están completamente especificados en este documento (§03 y §06-D) y son suficientes para implementación independiente. La cohorte será ampliada en trabajo futuro para permitir estratificación de subgrupos como se describe en §06-A.
The following self-contained Python script implements Algorithm 1 end-to-end, including the full covariance prior, adaptive \(\lambda\), analytic gradient, and weekly recalibration loop. It maps directly to equations (2), (4)–(9), and (15) in the paper. Dependencies are limited to NumPy, SciPy, and pandas.El siguiente script Python autocontenido implementa el Algoritmo 1 de extremo a extremo, incluyendo la prior de covarianza completa, \(\lambda\) adaptativo, gradiente analítico, y el bucle de recalibración semanal. Se asigna directamente a las ecuaciones (2), (4)–(9), y (15) en el artículo. Las dependencias se limitan a NumPy, SciPy, y pandas.
The script expects a CSV file with three columns. Every calendar day must have a TSS value; best20min_watts is left empty on non-test days.El script espera un archivo CSV con tres columnas. Cada día del calendario debe tener un valor de TSS; best20min_watts se deja vacío en días sin tests.
#!/usr/bin/env python3 """ NLSS — Nonlinear Least Squares with Shrinkage Reference implementation of the Bayesian K₁K₂T₁T₂ estimation algorithm. Companion code for: "Estimating K₁, K₂, T₁, T₂ Using Hierarchical Bayesian NLSS" Gabriel Della Mattia · AGMT2 · ednaLabs · 2026 Input: CSV with columns date, tss, best20min_watts Output: Weekly estimates of θ = [K₁, K₂, T₁, T₂] with λ and test count. Dependencies: numpy, scipy, pandas """ import numpy as np from scipy.optimize import minimize import pandas as pd import sys, os # ═══════════════════════════════════════════════════════════ # §A — AGMT2 Population Prior (Table 7 in paper) # ═══════════════════════════════════════════════════════════ MU_POP = np.array([2.87, 4.09, 40.64, 6.584]) # [K₁, K₂, T₁, T₂] SD_POP = np.array([0.301, 0.334, 3.094, 0.651]) # Full covariance matrix with empirical correlations CORR = np.array([ [ 1.00, +0.63, -0.41, -0.12], [+0.63, 1.00, +0.09, -0.28], [-0.41, +0.09, 1.00, +0.19], [-0.12, -0.28, +0.19, 1.00], ]) SIGMA_POP = np.outer(SD_POP, SD_POP) * CORR # eq. (15) SIGMA_INV = np.linalg.inv(SIGMA_POP) # precision matrix # ═══════════════════════════════════════════════════════════ # §B — Box Constraints (Table 2) # ═══════════════════════════════════════════════════════════ BOUNDS = [ (0.20, 8.00), # K₁ (0.20, 12.00), # K₂ (14.0, 80.0), # T₁ (days) ( 3.0, 18.0), # T₂ (days) ] # ═══════════════════════════════════════════════════════════ # §C — Adaptive λ (eq. 6) # ═══════════════════════════════════════════════════════════ LAMBDA_MAX = 5.0 N_HALF = 4 def adaptive_lambda(n_tests: int) -> float: """eq. (6): λ(n) = λ_max · exp(−n / n_half)""" return LAMBDA_MAX * np.exp(-n_tests / N_HALF) # ═══════════════════════════════════════════════════════════ # §D — Performance prediction (eq. 2) # ═══════════════════════════════════════════════════════════ def predict_performance(theta, tss_history, test_days, p0=0.0): """p̂(t) = p₀ + Σ TSS(τ)·[K₁·e^{-(t-τ)/T₁} − K₂·e^{-(t-τ)/T₂}]""" K1, K2, T1, T2 = theta p_hat = np.empty(len(test_days)) for i, d in enumerate(test_days): s = 0.0 for tau in range(d): delta = d - tau s += tss_history[tau] * (K1 * np.exp(-delta / T1) - K2 * np.exp(-delta / T2)) p_hat[i] = p0 + s return p_hat # ═══════════════════════════════════════════════════════════ # §E — Cost function + analytic gradient (eqs. 5, 7–9) # ═══════════════════════════════════════════════════════════ def cost_and_gradient(theta, tss_history, test_days, test_watts, lam, p0=0.0): """L(θ) = L_data + λ·L_prior with ∇L in a single pass.""" K1, K2, T1, T2 = theta n = len(test_days) if n == 0: diff = theta - MU_POP L_prior = diff @ SIGMA_INV @ diff g_prior = 2.0 * SIGMA_INV @ diff return lam * L_prior, lam * g_prior # Forward pass: residuals + cached exponentials residuals = np.empty(n) cache_e1, cache_e2, cache_te1, cache_te2 = [], [], [], [] for i, d in enumerate(test_days): s = se1 = se2 = ste1 = ste2 = 0.0 for tau in range(d): delta = d - tau w = tss_history[tau] exp1 = np.exp(-delta / T1) exp2 = np.exp(-delta / T2) s += w * (K1 * exp1 - K2 * exp2) se1 += w * exp1 se2 += w * exp2 ste1 += w * (delta / (T1*T1)) * exp1 ste2 += w * (delta / (T2*T2)) * exp2 residuals[i] = test_watts[i] - (p0 + s) cache_e1.append(se1); cache_e2.append(se2) cache_te1.append(ste1); cache_te2.append(ste2) # L_data (eq. 5a) L_data = np.sum(residuals**2) / n # ∇L_data (eq. 8) g_data = np.zeros(4) for i in range(n): r = residuals[i] g_data[0] += -2.0/n * r * cache_e1[i] # ∂L/∂K₁ g_data[1] += 2.0/n * r * cache_e2[i] # ∂L/∂K₂ g_data[2] += -2.0*K1/n * r * cache_te1[i] # ∂L/∂T₁ g_data[3] += 2.0*K2/n * r * cache_te2[i] # ∂L/∂T₂ # L_prior + ∇L_prior (eqs. 5b, 9) diff = theta - MU_POP L_prior = diff @ SIGMA_INV @ diff g_prior = 2.0 * SIGMA_INV @ diff return L_data + lam*L_prior, g_data + lam*g_prior # ═══════════════════════════════════════════════════════════ # §F — Weekly recalibration loop (Algorithm 1) # ═══════════════════════════════════════════════════════════ WINDOW_L = 90 DELTA_REL_THRESHOLD = 0.15 def run_nlss(df, p0=0.0, verbose=True): """Algorithm 1: weekly recalibration with sliding window.""" tss = df['tss'].values.astype(float) watts = df['best20min_watts'].values.astype(float) n_days = len(df) theta_current = MU_POP.copy() history = [] for t in range(6, n_days, 7): # every 7 days w_start = max(0, t - WINDOW_L) test_mask = ~np.isnan(watts[w_start:t+1]) test_idx = np.where(test_mask)[0] + w_start test_w = watts[w_start:t+1][test_mask] n_tests = len(test_w) lam = adaptive_lambda(n_tests) # eq. (6) if n_tests == 0: theta_current = MU_POP.copy() # pure prior continue # L-BFGS-B with warm start from previous θ result = minimize( lambda th: cost_and_gradient(th, tss, test_idx, test_w, lam, p0), theta_current, method='L-BFGS-B', jac=True, bounds=BOUNDS, options={'maxiter': 500, 'ftol': 1e-10, 'gtol': 1e-7} ) theta_new = result.x # Convergence check + regime detection (eq. 10) d_rel = np.linalg.norm(theta_new - theta_current) \ / (np.linalg.norm(theta_current) + 1e-12) if d_rel > DELTA_REL_THRESHOLD: lam = adaptive_lambda(0) # regime change → reset λ elif np.any(np.abs(theta_new - MU_POP) > 3*SD_POP): theta_new = theta_current # reject (>3σ) theta_current = theta_new.copy() history.append({ 'day': t, 'n_tests': n_tests, 'lambda': lam, 'K1': theta_current[0], 'K2': theta_current[1], 'T1': theta_current[2], 'T2': theta_current[3], }) return history # ═══════════════════════════════════════════════════════════ # §G — Main: load CSV and run # ═══════════════════════════════════════════════════════════ if __name__ == '__main__': csv_path = sys.argv[1] if len(sys.argv) > 1 else 'athlete_data.csv' df = pd.read_csv(csv_path, parse_dates=['date']).sort_values('date') df = df.reset_index(drop=True) first_test = df.dropna(subset=['best20min_watts']).iloc[0]['best20min_watts'] p0 = first_test * 0.5 history = run_nlss(df, p0=p0) pd.DataFrame(history).to_csv( csv_path.replace('.csv', '_nlss_results.csv'), index=False )
Run with: python nlss_reference.py athlete_data.csv. Output is written to athlete_data_nlss_results.csv with weekly \(\theta\) estimates. The script maps directly to the equations in this paper: §A uses eq. (15), §C uses eq. (6), §D uses eq. (2), §E uses eqs. (5)–(9), and §F implements Algorithm 1.Run with:Ejecutar con: python nlss_reference.py athlete_data.csv. Output is written toLa salida se escribe en athlete_data_nlss_results.csv with weekly \(\theta\) estimatescon estimaciones semanales de \(\theta\). The script maps directly to the equations in this paper: §A uses eq. (15), §C uses eq. (6), §D uses eq. (2), §E uses eqs. (5)–(9), and §F implements Algorithm 1.El script se asigna directamente a las ecuaciones en este artículo: §A usa eq. (15), §C usa eq. (6), §D usa eq. (2), §E usa eqs. (5)–(9), y §F implementa el Algoritmo 1.
Banister EW, Calvert TW, Savage MV, Bach T. A systems model of training for athletic performance. Australian Journal of Sports Medicine. 1975;7:57–61.
Busso T. Variable dose-response relationship between exercise training and performance. Medicine & Science in Sports & Exercise. 2003;35(7):1188–1195. doi:10.1249/01.MSS.0000074465.13621.37
Byrd RH, Lu P, Nocedal J, Zhu C. A limited memory algorithm for bound constrained optimization. SIAM Journal on Scientific Computing. 1995;16(5):1190–1208. doi:10.1137/0916069
De Pauw K, Roelands B, Cheung SS, de Geus B, Rietjens G, Meeusen R. Guidelines to classify subject groups in sport-science research. International Journal of Sports Physiology and Performance. 2013;8(2):111–122. doi:10.1123/ijspp.8.2.111
Ledoit O, Wolf M. A well-conditioned estimator for large-dimensional covariance matrices. Journal of Multivariate Analysis. 2004;88(2):365–411. doi:10.1016/S0047-259X(03)00096-4
Seiler S. What is best practice for training intensity and duration distribution in endurance athletes? International Journal of Sports Physiology and Performance. 2010;5(3):276–291. doi:10.1123/ijspp.5.3.276
Skiba PF, Clarke DC. The W'bal model: Mathematical background and practical applications. In: Science and Application of High-Intensity Interval Training. Human Kinetics; 2021. (Note: The influence curve formalization used here follows the Banister framework as extended by Skiba & Clarke in multiple applied works.)
Huber PJ. Robust estimation of a location parameter. The Annals of Mathematical Statistics. 1964;35(1):73–101.