Skip to content

Commit fc8b8c0

Browse files
committed
Ensure data are centered in SSID method
1 parent d2aa9aa commit fc8b8c0

1 file changed

Lines changed: 10 additions & 10 deletions

File tree

src/Bonsai.ML.Lds.Torch/KalmanFilter.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -729,7 +729,7 @@ public ExpectationMaximizationResult ExpectationMaximization(
729729
scalarType: _scalarType,
730730
device: _device);
731731

732-
// Compute log likelihood (avoid creating intermediate tensors)
732+
// Compute log likelihood
733733
var llSumDouble = filteredState.LogLikelihood.sum()
734734
.to_type(ScalarType.Float64).item<double>();
735735
var filteredLogLikelihoodSum = logLikelihoodConst + 0.5 * llSumDouble;
@@ -766,7 +766,7 @@ public ExpectationMaximizationResult ExpectationMaximization(
766766
var S11 = smoothedState.S11.sum([0]);
767767
var S10 = smoothedState.S10.sum([0]);
768768

769-
// Replace einsum with faster matmul
769+
// Compute cross-correlation between observations and smoothed states
770770
var crossCorrelationObservations = observationT.matmul(smoothedState.SmoothedMean);
771771

772772
// Update parameters
@@ -949,18 +949,19 @@ public static StochasticSubspaceIdentificationResult StochasticSubspaceIdentific
949949

950950
var timeBins = observations.size(0);
951951
var numObs = observations.size(1);
952+
var centered = observations - observations.mean([0], keepdim: true);
952953

953954
// Build Hankel matrices from observations
954955
var numCols = (int)(timeBins - 2 * maxLag + 1);
955956

956957
if (numCols <= 0)
957958
throw new ArgumentException($"Number of time bins ({timeBins}) must be greater than 2*maxLag ({2 * maxLag}) for subspace identification.");
958959

959-
var stride = observations.stride();
960-
var pastView = observations.as_strided([maxLag, numCols, numObs], [stride[0], stride[0], stride[1]]);
960+
var stride = centered.stride();
961+
var pastView = centered.as_strided([maxLag, numCols, numObs], [stride[0], stride[0], stride[1]]);
961962
var past = pastView.permute(0, 2, 1).reshape(maxLag * numObs, numCols);
962963

963-
var futureView = observations.narrow(0, maxLag, timeBins - maxLag)
964+
var futureView = centered.narrow(0, maxLag, timeBins - maxLag)
964965
.as_strided([maxLag, numCols, numObs], [stride[0], stride[0], stride[1]]);
965966
var future = futureView.permute(0, 2, 1).reshape(maxLag * numObs, numCols);
966967

@@ -973,8 +974,7 @@ public static StochasticSubspaceIdentificationResult StochasticSubspaceIdentific
973974

974975
// Compute the effective rank
975976
var effectiveRank = (S > (threshold * S[0])).to_type(ScalarType.Int64).sum().item<long>();
976-
977-
var effectiveStates = Math.Min(effectiveRank, targetNumStates ?? effectiveRank);
977+
var effectiveStates = Math.Max(Math.Min(effectiveRank, targetNumStates ?? effectiveRank), 1);
978978

979979
var Ur = U[TensorIndex.Colon, TensorIndex.Slice(0, effectiveStates)];
980980
var SrSqrt = S[TensorIndex.Slice(0, effectiveStates)].diag().sqrt();
@@ -999,13 +999,13 @@ public static StochasticSubspaceIdentificationResult StochasticSubspaceIdentific
999999

10001000
// Estimate noise covariances using residuals
10011001
var stateResiduals = statesNext - transitionMatrix.matmul(statesShifted);
1002-
var processNoiseCovariance = WrappedTensorDisposeScope(() => stateResiduals.matmul(stateResiduals.mT) / (numCols - 1));
1002+
var processNoiseCovariance = WrappedTensorDisposeScope(() => EnsureSymmetric(stateResiduals.matmul(stateResiduals.mT) / (numCols - 1)));
10031003

10041004
// Compute the observation residuals
10051005
var observationPredictions = measurementFunction.matmul(states);
1006-
var observationWindow = observations[TensorIndex.Slice(maxLag, maxLag + numCols)].mT;
1006+
var observationWindow = centered[TensorIndex.Slice(maxLag, maxLag + numCols)].mT;
10071007
var observationResiduals = observationWindow - observationPredictions;
1008-
var measurementNoiseCovariance = WrappedTensorDisposeScope(() => observationResiduals.matmul(observationResiduals.mT) / numCols);
1008+
var measurementNoiseCovariance = WrappedTensorDisposeScope(() => EnsureSymmetric(observationResiduals.matmul(observationResiduals.mT) / numCols));
10091009

10101010
// Initial state estimates
10111011
var initialMean = states[TensorIndex.Colon, 0];

0 commit comments

Comments
 (0)