fix(stats): keep hidden Markov fitting alive on a single observation

Assisted-by: Qwen 3.8 Flash
This commit is contained in:
2026-09-27 14:23:51 +02:00
parent 2764a18075
commit e507adeb64
3 changed files with 47 additions and 0 deletions
+32
View File
@@ -343,3 +343,35 @@ func TestHiddenMarkovZeroProbabilitySequence(t *testing.T) {
t.Fatalf("the certain sequence answered (%g, %v), want (0, [1])", ll, filtered[0])
}
}
func TestHiddenMarkovFitSingleObservation(t *testing.T) {
// One observation carries emission and initial evidence but no
// transition evidence: the re-estimation divided the zero count into
// NaN rows, the constructor refused them, and the sweep then called
// a method on the nil model and crashed the process. The fit must
// answer with a valid model whose transitions keep their starting
// estimate.
g := core.NewGenerator(11)
res, err := FitHiddenMarkovModel(g, []int{0}, 2, 2)
if err != nil {
t.Fatalf("FitHiddenMarkovModel on one observation: %v", err)
}
if res.Model == nil {
t.Fatal("FitHiddenMarkovModel on one observation returned no model")
}
if math.IsNaN(res.LogLikelihood) || math.IsInf(res.LogLikelihood, 0) {
t.Fatalf("log likelihood = %g, want a finite value", res.LogLikelihood)
}
for k := range 2 {
sum := 0.0
for _, v := range res.Model.Transition[k*2 : k*2+2] {
if math.IsNaN(v) || v < 0 {
t.Fatalf("transition row %d holds %g, want probabilities", k, v)
}
sum += v
}
if math.Abs(sum-1) > 1e-9 {
t.Fatalf("transition row %d sums to %g, want 1", k, sum)
}
}
}