diff --git a/docs/CHANGELOG.asciidoc b/docs/CHANGELOG.asciidoc index 5c02c03815..d9b92b2a30 100644 --- a/docs/CHANGELOG.asciidoc +++ b/docs/CHANGELOG.asciidoc @@ -62,6 +62,9 @@ Fix corner case failing to calculate lgamma values and the correspoinding log er Influence count per bucket for metric population analyses was wrong and lead to wrong influencer scoring ({pull}#150[#150]) Fix a possible SIGSEGV for jobs with multivariate by fields enabled which would lead to the job failing ({pull}#170[#170]) +Correct the model bounds and typical value calculation for time series models which use a multimodal distribution. +This issue could cause "Unable to bracket left percentile =..." errors to appear in the logs. ({pull}#176[#176]) + === Regressions === Known Issues diff --git a/include/maths/CMultimodalPriorUtils.h b/include/maths/CMultimodalPriorUtils.h index 76a907f64b..120d0f275b 100644 --- a/include/maths/CMultimodalPriorUtils.h +++ b/include/maths/CMultimodalPriorUtils.h @@ -206,8 +206,8 @@ class MATHS_EXPORT CMultimodalPriorUtils : private core::CNonInstantiatable { return support; } - double p1 = std::log((1.0 - percentage) / 2.0); - double p2 = std::log((1.0 + percentage) / 2.0); + double p1 = maths_t::count(weights) * std::log((1.0 - percentage) / 2.0); + double p2 = maths_t::count(weights) * std::log((1.0 + percentage) / 2.0); CLogCdf fl(CLogCdf::E_Lower, prior, weights); CLogCdf fu(CLogCdf::E_Upper, prior, weights); diff --git a/lib/maths/unittest/CMultimodalPriorTest.cc b/lib/maths/unittest/CMultimodalPriorTest.cc index 143f8b9c5e..49d512625a 100644 --- a/lib/maths/unittest/CMultimodalPriorTest.cc +++ b/lib/maths/unittest/CMultimodalPriorTest.cc @@ -941,10 +941,10 @@ void CMultimodalPriorTest::testMarginalLikelihoodConfidenceInterval() { using TMeanAccumulator = maths::CBasicStatistics::SSampleMean::TAccumulator; + test::CRandomNumbers rng; + LOG_DEBUG(<< "Synthetic"); { - test::CRandomNumbers rng; - double w1 = 0.2; double location1 = 0.1; double squareScale1 = 0.2; @@ -1047,6 +1047,33 @@ void CMultimodalPriorTest::testMarginalLikelihoodConfidenceInterval() { CPPUNIT_ASSERT_DOUBLES_EQUAL(-111.0, i90.first, 0.5); CPPUNIT_ASSERT_DOUBLES_EQUAL(158952.0, i90.second, 0.5); } + + LOG_DEBUG(<< "Non-unit count weight"); + { + // The confidence interval should be independent of the sample + // count weight. + + TDoubleVec modes[2]; + rng.generateNormalSamples(10.0, 2.0, 50, modes[0]); + rng.generateNormalSamples(20.0, 2.0, 50, modes[1]); + TDoubleVec samples(modes[0].begin(), modes[0].end()); + samples.insert(samples.end(), modes[1].begin(), modes[1].end()); + + CMultimodalPrior filter(makePrior()); + filter.addSamples(samples); + + CPPUNIT_ASSERT_EQUAL(std::size_t(2), filter.numberModes()); + + TDoubleDoublePr interval{filter.marginalLikelihoodConfidenceInterval( + 90.0, maths_t::countWeight(1.0))}; + TDoubleDoublePr weightedInterval{filter.marginalLikelihoodConfidenceInterval( + 90.0, maths_t::countWeight(0.3))}; + LOG_DEBUG(<< "interval = " << core::CContainerPrinter::print(interval)); + LOG_DEBUG(<< "weightedInterval = " + << core::CContainerPrinter::print(weightedInterval)); + CPPUNIT_ASSERT_EQUAL(core::CContainerPrinter::print(interval), + core::CContainerPrinter::print(weightedInterval)); + } } void CMultimodalPriorTest::testSampleMarginalLikelihood() { diff --git a/lib/model/unittest/CEventRateModelTest.cc b/lib/model/unittest/CEventRateModelTest.cc index 9e1637c00f..3316619a22 100644 --- a/lib/model/unittest/CEventRateModelTest.cc +++ b/lib/model/unittest/CEventRateModelTest.cc @@ -2504,8 +2504,8 @@ void CEventRateModelTest::testComputeProbabilityGivenDetectionRule() { CPartitioningFields partitioningFields(EMPTY_STRING, EMPTY_STRING); SAnnotatedProbability annotatedProbability; - CPPUNIT_ASSERT(model->computeProbability(0 /*pid*/, now, now + bucketLength, partitioningFields, - 1, annotatedProbability)); + CPPUNIT_ASSERT(model->computeProbability(0 /*pid*/, now, now + bucketLength, + partitioningFields, 1, annotatedProbability)); CPPUNIT_ASSERT_DOUBLES_EQUAL(annotatedProbability.s_Probability, 1.0, 0.00001); }