Skip to content

Commit 37330c3

Browse files
authored
[ML] Ensure estimated latitude is within the allowed range (#2586)
Fixes #2578
1 parent fec6e91 commit 37330c3

File tree

4 files changed

+95
-2
lines changed

4 files changed

+95
-2
lines changed

docs/CHANGELOG.asciidoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@
3434

3535
* Upgrade Boost libraries to version 1.83. (See {ml-pull}2560[#2560].)
3636

37+
=== Bug Fixes
38+
39+
* Ensure the estimated latitude is within the allowed range (See {ml-pull}#2586[2586].)
40+
3741
== {es} version 8.11.0
3842

3943
=== Enhancements

include/model/CIndividualModel.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
namespace ml {
2929
namespace model {
3030
class CAnnotatedProbabilityBuilder;
31+
class CIndividualModelTestHelper;
3132
class CProbabilityAndInfluenceCalculator;
3233

3334
//! \brief The most basic individual model interface.
@@ -303,6 +304,8 @@ class MODEL_EXPORT CIndividualModel : public CAnomalyDetectorModel {
303304

304305
//! The memory estimator.
305306
mutable CMemoryUsageEstimator m_MemoryEstimator;
307+
308+
friend class CIndividualModelTestHelper;
306309
};
307310
}
308311
}

lib/model/ModelTypes.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -902,7 +902,7 @@ TDouble1VecDouble1VecPr support(EFeature feature) {
902902
case E_IndividualHighVarianceByPerson:
903903
return {TDouble1Vec(d, 0.0), TDouble1Vec(d, MAX_DOUBLE)};
904904
case E_IndividualMeanLatLongByPerson:
905-
return {TDouble1Vec(d, -180.0), TDouble1Vec(d, 180.0)};
905+
return {TDouble1Vec{-90, -180.0}, TDouble1Vec{90, 180.0}};
906906

907907
case E_PopulationAttributeTotalCountByPerson:
908908
case E_PopulationCountByBucketPersonAndAttribute:
@@ -945,7 +945,7 @@ TDouble1VecDouble1VecPr support(EFeature feature) {
945945
case E_PopulationHighVarianceByPersonAndAttribute:
946946
return {TDouble1Vec(d, 0.0), TDouble1Vec(d, MAX_DOUBLE)};
947947
case E_PopulationMeanLatLongByPersonAndAttribute:
948-
return {TDouble1Vec(d, -180.0), TDouble1Vec(d, 180.0)};
948+
return {TDouble1Vec{-90, -180.0}, TDouble1Vec{90, 180.0}};
949949
}
950950

951951
return {TDouble1Vec(d, MIN_DOUBLE), TDouble1Vec(d, MAX_DOUBLE)};

lib/model/unittest/CMetricModelTest.cc

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <model/CDataGatherer.h>
2828
#include <model/CDetectionRule.h>
2929
#include <model/CEventData.h>
30+
#include <model/CIndividualModel.h>
3031
#include <model/CInterimBucketCorrector.h>
3132
#include <model/CMetricModel.h>
3233
#include <model/CMetricModelFactory.h>
@@ -50,6 +51,18 @@
5051
#include <utility>
5152
#include <vector>
5253

54+
namespace ml {
55+
namespace model {
56+
class CIndividualModelTestHelper {
57+
public:
58+
static void setFeature(ml::model::CIndividualModel& model) {
59+
auto& feature = model.m_FeatureModels[0];
60+
feature.s_Models.emplace_back(feature.s_NewModel->clone(0));
61+
}
62+
};
63+
}
64+
}
65+
5366
BOOST_AUTO_TEST_SUITE(CMetricModelTest)
5467

5568
using namespace ml;
@@ -2349,4 +2362,77 @@ BOOST_FIXTURE_TEST_CASE(testIgnoreSamplingGivenDetectionRules, CTestFixture) {
23492362
modelNoSkipTime);
23502363
}
23512364

2365+
class MyFakeModel : public ml::maths::common::CModelStub {
2366+
public:
2367+
MyFakeModel(model_t::TDouble2Vec latLong) : m_LatLong(latLong) {}
2368+
2369+
model_t::TDouble2Vec predict(core_t::TTime /*time*/,
2370+
const TSizeDoublePr1Vec& /*correlated*/,
2371+
TDouble2Vec /*hint*/) const override {
2372+
return m_LatLong;
2373+
}
2374+
2375+
CModelStub* clone(std::size_t /*id*/) const override {
2376+
return new MyFakeModel(m_LatLong);
2377+
}
2378+
2379+
private:
2380+
model_t::TDouble2Vec m_LatLong;
2381+
};
2382+
2383+
BOOST_FIXTURE_TEST_CASE(testLatLongNotMalformed, CTestFixture) {
2384+
// This test ensures that the latitudes and longitudes generated by the model are within the
2385+
// expected range.
2386+
2387+
// initialize the model
2388+
core_t::TTime startTime{45};
2389+
core_t::TTime bucketLength{5};
2390+
model_t::TFeatureVec features{model_t::E_IndividualMeanLatLongByPerson};
2391+
SModelParams params(bucketLength);
2392+
params.s_InitialDecayRateMultiplier = 1.0;
2393+
params.s_MaximumUpdatesPerBucket = 0.0;
2394+
size_t sampleCount{1};
2395+
2396+
this->makeModel(params, features, startTime, sampleCount);
2397+
2398+
ml::model::CAnomalyDetectorModel::TFeatureMultivariatePriorSPtrPrVec newFeatureCorelateModelPriors;
2399+
ml::model::CAnomalyDetectorModel::TFeatureCorrelationsPtrPrVec featureCorrelatesModels;
2400+
ml::model::CAnomalyDetectorModel::TFeatureInfluenceCalculatorCPtrPrVecVec influenceCalculators;
2401+
2402+
// generate random numbers for latitudes and longitudes in the range [-360, 360]
2403+
test::CRandomNumbers rng;
2404+
int numberOfTrials{100};
2405+
std::vector<double> latitudes;
2406+
std::vector<double> longitudes;
2407+
rng.generateUniformSamples(-360.0, 360.0, numberOfTrials, latitudes);
2408+
rng.generateUniformSamples(-360.0, 360.0, numberOfTrials, longitudes);
2409+
2410+
for (auto i = 0; i < numberOfTrials; ++i) {
2411+
ml::model::CAnomalyDetectorModel::TFeatureMathsModelSPtrPrVec newFeatureModels = {
2412+
std::make_pair(model_t::E_IndividualMeanLatLongByPerson,
2413+
std::make_shared<MyFakeModel>(model_t::TDouble2Vec(
2414+
{latitudes[i], longitudes[i]})))};
2415+
ml::model::CMetricModel model{params,
2416+
m_Gatherer,
2417+
newFeatureModels,
2418+
newFeatureCorelateModelPriors,
2419+
std::move(featureCorrelatesModels),
2420+
influenceCalculators,
2421+
m_InterimBucketCorrector};
2422+
CIndividualModelTestHelper::setFeature(model);
2423+
model_t::CResultType type(model_t::CResultType::E_Unconditional |
2424+
model_t::CResultType::E_Final);
2425+
core_t::TTime time{startTime};
2426+
auto result = model.baselineBucketMean(features[0], 0, 0, type, NO_CORRELATES, time);
2427+
// ensure the result is withing the expected range
2428+
BOOST_REQUIRE_EQUAL(result.size(), 2);
2429+
// Check latitude in [-90, 90]
2430+
BOOST_TEST_REQUIRE(result[0] >= -90.0);
2431+
BOOST_TEST_REQUIRE(result[0] <= 90.0);
2432+
// Check longitude in [-180, 180]
2433+
BOOST_TEST_REQUIRE(result[1] >= -180.0);
2434+
BOOST_TEST_REQUIRE(result[1] <= 180.0);
2435+
}
2436+
}
2437+
23522438
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)