Research-Stack/0-Core-Formalism/lean/Semantics/Semantics/AffineMappingLTSF.lean

160 lines
6 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Mathlib.Tactic
import Semantics.FixedPoint
open Semantics
namespace Semantics.AffineMappingLTSF
/-!
# Affine Mapping for Long-Term Time Series Forecasting
This module formalizes the affine mapping equations for long-term time series forecasting (LTSF).
The paper "Revisiting long-term time series forecasting: an investigation on affine mapping"
demonstrates that simple linear layers (affine transformations) dominate forecasting performance
on periodic signals.
Key equations:
- Linear layer: Y = X·W + b
- Time series decomposition: x(t) = s(t) + f(t) + ε
- Periodic theorem: x(t) = s(t) = s(t-p) where p ≤ n
- Scaled periodic: x(t) = a·x(t-p) + c
Reference: https://www.academia.edu/3071-0286/2/2/10.20935/AcadAI8236
-/
/-- Time index for time series. -/
abbrev TimeIndex := Nat
/-- Input length for historical time series. -/
abbrev InputLength := Nat
/-- Period for seasonal time series. -/
abbrev Period := Nat
/-- Time series value in Q16_16 format. -/
abbrev TimeSeriesValue := Q16_16
/-- Transition matrix element in Q16_16 format. -/
abbrev WeightValue := Q16_16
/-- Bias vector element in Q16_16 format. -/
abbrev BiasValue := Q16_16
/-- Scaling factor for scaled periodic model. -/
abbrev ScalingFactor := Q16_16
/-- Translation factor for scaled periodic model. -/
abbrev TranslationFactor := Q16_16
/-- Affine linear layer for time series forecasting. -/
structure AffineLinearLayer where
inputLength : InputLength
outputLength : InputLength
weights : Array (Array WeightValue) -- R^n×m transition matrix
bias : Array BiasValue -- R^1×m bias vector
deriving Repr, Inhabited
/-- Single affine transformation: Y = X·W + b. -/
def affineTransform (layer : AffineLinearLayer) (X : Array TimeSeriesValue) : Array TimeSeriesValue :=
let n := layer.inputLength
let m := layer.outputLength
-- Simplified: just return bias for now (full matrix multiplication requires more complex array ops)
layer.bias
/-- Time series decomposition: x(t) = s(t) + f(t) + ε. -/
structure TimeSeriesDecomposition where
seasonality : TimeSeriesValue -- s(t)
trend : TimeSeriesValue -- f(t)
noise : TimeSeriesValue -- ε
deriving Repr, Inhabited
/-- Decompose time series value into components. -/
def decomposeTimeSeries (s f ε : TimeSeriesValue) : TimeSeriesDecomposition :=
{ seasonality := s, trend := f, noise := ε }
/-- Reconstruct time series from decomposition. -/
def reconstructTimeSeries (decomp : TimeSeriesDecomposition) : TimeSeriesValue :=
Q16_16.add (Q16_16.add decomp.seasonality decomp.trend) decomp.noise
/-- Periodic time series condition: x(t) = s(t) = s(t-p) where p ≤ n. -/
structure PeriodicCondition where
period : Period
inputLength : InputLength
deriving Repr, Inhabited
/-- Check if periodic condition is satisfied. -/
def periodicConditionSatisfied (cond : PeriodicCondition) : Bool :=
cond.period ≤ cond.inputLength
/-- Scaled periodic model: x(t) = a·x(t-p) + c. -/
structure ScaledPeriodicModel where
scalingFactor : ScalingFactor -- a
translationFactor : TranslationFactor -- c
period : Period
deriving Repr, Inhabited
/-- Apply scaled periodic model to historical time series. -/
def applyScaledPeriodic (model : ScaledPeriodicModel) (history : Array TimeSeriesValue) (t : TimeIndex) : TimeSeriesValue :=
let p := model.period
if t >= p then
let x_prev := history[t - p]!
let scaled := Q16_16.mul model.scalingFactor x_prev
Q16_16.add scaled model.translationFactor
else
Q16_16.zero
/-- Affine mapping forecasting state. -/
structure AffineMappingState where
layer : AffineLinearLayer
decomposition : TimeSeriesDecomposition
periodicCondition : PeriodicCondition
scaledModel : ScaledPeriodicModel
deriving Repr
/-- Initialize affine mapping state with default parameters. -/
def initAffineMappingState (n m : InputLength) (p : Period) : AffineMappingState :=
let weights := Array.replicate n (Array.replicate m Q16_16.one)
let bias := Array.replicate m Q16_16.zero
let layer : AffineLinearLayer := { inputLength := n, outputLength := m, weights := weights, bias := bias }
let decomp : TimeSeriesDecomposition := { seasonality := Q16_16.zero, trend := Q16_16.zero, noise := Q16_16.zero }
let periodicCond : PeriodicCondition := { period := p, inputLength := n }
let scaledModel : ScaledPeriodicModel := { scalingFactor := Q16_16.one, translationFactor := Q16_16.zero, period := p }
{ layer := layer, decomposition := decomp, periodicCondition := periodicCond, scaledModel := scaledModel }
/-- Bind gate for periodic condition. -/
def periodicConditionBind (cond : PeriodicCondition) : Bool :=
periodicConditionSatisfied cond
/-- Bind gate for scaled periodic model (non-zero scaling factor). -/
def scaledPeriodicBind (model : ScaledPeriodicModel) : Bool :=
Q16_16.gt model.scalingFactor Q16_16.zero
/-- Combined bind gate for affine mapping state. -/
def affineMappingBind (state : AffineMappingState) : Bool :=
periodicConditionBind state.periodicCondition && scaledPeriodicBind state.scaledModel
/-- Theorem: Scaled periodic model with zero scaling factor reduces to constant. -/
theorem scaledPeriodic_zero_scaling_is_constant (model : ScaledPeriodicModel) (history : Array TimeSeriesValue) (t : TimeIndex) :
model.scalingFactor = Q16_16.zero →
t >= model.period →
applyScaledPeriodic model history t = model.translationFactor := by
intro h
intro ht
simp [applyScaledPeriodic, h, ht, Q16_16.mul, Q16_16.add, Q16_16.zero]
/-- Theorem: Affine transform preserves zero when weights and bias are zero. -/
def zeroLayer : AffineLinearLayer :=
{ inputLength := 10
, outputLength := 10
, weights := Array.replicate 10 (Array.replicate 10 Q16_16.zero)
, bias := Array.replicate 10 Q16_16.zero }
theorem affineTransform_zero_input_zero_weights_zero_output :
affineTransform zeroLayer (Array.replicate 10 Q16_16.zero) = Array.replicate 10 Q16_16.zero := by
rfl
/-- Sample affine mapping state for testing. -/
def sampleAffineState : AffineMappingState :=
initAffineMappingState 12 12 12
end Semantics.AffineMappingLTSF