Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ML] Prevent adding new seasonal and calendar components to comply with hard limit #2469

Merged
merged 20 commits into from May 27, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/CHANGELOG.asciidoc
Expand Up @@ -28,6 +28,12 @@

//=== Regressions

== {es} version 8.9.0

=== Enhancements

* Improved compliance with memory limitations. (See {ml-pull}2469[#2469].)

== {es} version 8.8.0

=== Enhancements
Expand Down
26 changes: 26 additions & 0 deletions include/maths/common/CModel.h
Expand Up @@ -329,6 +329,29 @@ struct MATHS_COMMON_EXPORT SModelProbabilityResult {
SAnomalyScoreExplanation s_AnomalyScoreExplanation;
};

//! \brief The allocator interface.
//!
//! DESCRIPTION:\n
//! The allocator interface is used to control the ability
//! of a model to allocate new components. This is used to
//! comply with the memory constraints of the system.
class MATHS_COMMON_EXPORT CModelAllocator {
valeriy42 marked this conversation as resolved.
Show resolved Hide resolved
public:
virtual ~CModelAllocator() = default;

//! Check if we can still allocate any components.
virtual bool areAllocationsAllowed() const = 0;
};

//! \brief The allocator stub.
//!
//! DESCRIPTION:\n
//! The allocator stub is used to allow all allocations.
class MATHS_COMMON_EXPORT CModelAllocatorStub : public CModelAllocator {
public:
bool areAllocationsAllowed() const override { return true; }
};

//! \brief The model interface.
//!
//! DESCRIPTION:\n
Expand Down Expand Up @@ -362,6 +385,7 @@ class MATHS_COMMON_EXPORT CModel {
using TDouble2VecWeightsAry = maths_t::TDouble2VecWeightsAry;
using TDouble2VecWeightsAry1Vec = maths_t::TDouble2VecWeightsAry1Vec;
using TTail2Vec = core::CSmallVector<maths_t::ETail, 2>;
using TModelAllocator = CModelAllocator;

//! Possible statuses for updating a model.
enum EUpdateResult {
Expand Down Expand Up @@ -414,6 +438,7 @@ class MATHS_COMMON_EXPORT CModel {

//! Update the model with new samples.
virtual EUpdateResult addSamples(const CModelAddSamplesParams& params,
const CModelAllocator& allocator,
valeriy42 marked this conversation as resolved.
Show resolved Hide resolved
TTimeDouble2VecSizeTrVec samples) = 0;

//! Advance time by \p gap.
Expand Down Expand Up @@ -586,6 +611,7 @@ class MATHS_COMMON_EXPORT CModelStub : public CModel {

//! No-op.
EUpdateResult addSamples(const CModelAddSamplesParams& params,
const CModelAllocator& allocator,
TTimeDouble2VecSizeTrVec samples) override;

//! No-op.
Expand Down
5 changes: 5 additions & 0 deletions include/maths/time_series/CTimeSeriesDecomposition.h
Expand Up @@ -105,6 +105,7 @@ class MATHS_TIME_SERIES_EXPORT EMPTY_BASE_OPT CTimeSeriesDecomposition
//!
//! \param[in] time The time of the data point.
//! \param[in] value The value of the data point.
//! \param[in] allocator The allocator to use for the decomposition.
//! \param[in] weights The weights of \p value. The smaller the count weight the
//! less influence \p value has on the decomposition.
//! \param[in] componentChangeCallback Supplied with samples of the prediction
Expand All @@ -115,6 +116,7 @@ class MATHS_TIME_SERIES_EXPORT EMPTY_BASE_OPT CTimeSeriesDecomposition
//! \param[in] firstValueTime The time of the first value added to the decomposition.
void addPoint(core_t::TTime time,
double value,
const common::CModelAllocator& allocator,
const maths_t::TDoubleWeightsAry& weights = TWeights::UNIT,
const TComponentChangeCallback& componentChangeCallback = noopComponentChange,
const maths_t::TModelAnnotationCallback& modelAnnotationCallback = noopModelAnnotation,
Expand Down Expand Up @@ -229,6 +231,9 @@ class MATHS_TIME_SERIES_EXPORT EMPTY_BASE_OPT CTimeSeriesDecomposition
//! Get the seasonal components.
const maths_t::TSeasonalComponentVec& seasonalComponents() const override;

//! Get the calendar components.
const maths_t::TCalendarComponentVec& calendarComponents() const override;

//! Get the time of the last value.
core_t::TTime lastValueTime() const;

Expand Down
60 changes: 40 additions & 20 deletions include/maths/time_series/CTimeSeriesDecompositionDetail.h
Expand Up @@ -15,6 +15,8 @@
#include <core/CStateMachine.h>
#include <core/CoreTypes.h>

#include <maths/common/CModel.h>

#include <maths/time_series/CCalendarComponent.h>
#include <maths/time_series/CCalendarCyclicTest.h>
#include <maths/time_series/CExpandingWindow.h>
Expand All @@ -37,6 +39,7 @@

namespace CTimeSeriesDecompositionTest {
class CNanInjector;
class CComponentsTest;
}

namespace ml {
Expand All @@ -54,6 +57,7 @@ class MATHS_TIME_SERIES_EXPORT CTimeSeriesDecompositionDetail
using TFilteredPredictor = std::function<double(core_t::TTime, const TBoolVec&)>;
using TMakeFilteredPredictor = std::function<TFilteredPredictor()>;
using TChangePointUPtr = std::unique_ptr<CChangePoint>;
using TModelAllocator = common::CModelAllocator;

// clang-format off
using TMakeTestForSeasonality =
Expand All @@ -68,12 +72,14 @@ class MATHS_TIME_SERIES_EXPORT CTimeSeriesDecompositionDetail

//! \brief The base message passed.
struct MATHS_TIME_SERIES_EXPORT SMessage {
SMessage(core_t::TTime time, core_t::TTime lastTime);
SMessage(core_t::TTime time, core_t::TTime lastTime, const TModelAllocator& allocator);

//! The message time.
core_t::TTime s_Time;
//! The last update time.
core_t::TTime s_LastTime;

const TModelAllocator& s_Allocator;
};

//! \brief The message passed to add a point.
Expand All @@ -91,7 +97,8 @@ class MATHS_TIME_SERIES_EXPORT CTimeSeriesDecompositionDetail
CTimeSeriesDecomposition& decomposition,
const TMakePredictor& makePredictor,
const TMakeFilteredPredictor& makeSeasonalityTestPreconditioner,
const TMakeTestForSeasonality& makeTestForSeasonality);
const TMakeTestForSeasonality& makeTestForSeasonality,
const TModelAllocator& allocator = common::CModelAllocatorStub());
valeriy42 marked this conversation as resolved.
Show resolved Hide resolved
SAddValue(const SAddValue&) = delete;
SAddValue& operator=(const SAddValue&) = delete;

Expand Down Expand Up @@ -125,7 +132,10 @@ class MATHS_TIME_SERIES_EXPORT CTimeSeriesDecompositionDetail
//! \brief The message passed to indicate periodic components have been
//! detected.
struct MATHS_TIME_SERIES_EXPORT SDetectedSeasonal : public SMessage {
SDetectedSeasonal(core_t::TTime time, core_t::TTime lastTime, CSeasonalDecomposition components);
SDetectedSeasonal(core_t::TTime time,
core_t::TTime lastTime,
CSeasonalDecomposition components,
const TModelAllocator& allocator);

//! The components found.
CSeasonalDecomposition s_Components;
Expand All @@ -137,7 +147,8 @@ class MATHS_TIME_SERIES_EXPORT CTimeSeriesDecompositionDetail
SDetectedCalendar(core_t::TTime time,
core_t::TTime lastTime,
CCalendarFeature feature,
core_t::TTime timeZoneOffset);
core_t::TTime timeZoneOffset,
const TModelAllocator& allocator);

//! The calendar feature found.
CCalendarFeature s_Feature;
Expand All @@ -148,15 +159,19 @@ class MATHS_TIME_SERIES_EXPORT CTimeSeriesDecompositionDetail
//! \brief The message passed to indicate the trend is being used for prediction.
struct MATHS_TIME_SERIES_EXPORT SDetectedTrend : public SMessage {
SDetectedTrend(const TPredictor& predictor,
const TComponentChangeCallback& componentChangeCallback);
const TComponentChangeCallback& componentChangeCallback,
const TModelAllocator& allocator);

TPredictor s_Predictor;
TComponentChangeCallback s_ComponentChangeCallback;
};

//! \brief The message passed to indicate a sudden change has occurred.
struct MATHS_TIME_SERIES_EXPORT SDetectedChangePoint : public SMessage {
SDetectedChangePoint(core_t::TTime time, core_t::TTime lastTime, TChangePointUPtr change);
SDetectedChangePoint(core_t::TTime time,
core_t::TTime lastTime,
TChangePointUPtr change,
const TModelAllocator& allocator);

//! The change description.
TChangePointUPtr s_Change;
Expand Down Expand Up @@ -522,7 +537,7 @@ class MATHS_TIME_SERIES_EXPORT CTimeSeriesDecompositionDetail
//! \brief Holds and updates the components of the decomposition.
class MATHS_TIME_SERIES_EXPORT CComponents : public CHandler {
public:
class CScopeAttachComponentChangeCallback {
class MATHS_TIME_SERIES_EXPORT CScopeAttachComponentChangeCallback {
public:
CScopeAttachComponentChangeCallback(CComponents& components,
TComponentChangeCallback componentChangeCallback,
Expand Down Expand Up @@ -770,6 +785,12 @@ class MATHS_TIME_SERIES_EXPORT CTimeSeriesDecompositionDetail
//! Get the combined size of the seasonal components.
std::size_t size() const;

//! Estimate the size change in the seasonal components if we
//! were to apply all changes from \p components.
std::ptrdiff_t estimateSizeChange(const CSeasonalDecomposition& components,
double decayRate,
double bucketLength) const;

//! Get the components.
const maths_t::TSeasonalComponentVec& components() const;
//! Get the components.
Expand All @@ -794,15 +815,7 @@ class MATHS_TIME_SERIES_EXPORT CTimeSeriesDecompositionDetail
bool initialized() const;

//! Add and initialize a new component.
void add(const CSeasonalTime& seasonalTime,
std::size_t size,
double decayRate,
double bucketLength,
core_t::TTime maxTimeShiftPerPeriod,
common::CSplineTypes::EBoundaryCondition boundaryCondition,
core_t::TTime startTime,
core_t::TTime endTime,
const TFloatMeanAccumulatorVec& values);
void add(CSeasonalComponent&& component);

//! Apply \p change to the components.
void apply(const CChangePoint& change);
Expand Down Expand Up @@ -934,10 +947,13 @@ class MATHS_TIME_SERIES_EXPORT CTimeSeriesDecompositionDetail
std::size_t maxSize() const;

//! Add new seasonal components.
void addSeasonalComponents(const CSeasonalDecomposition& components);
void addSeasonalComponents(const CSeasonalDecomposition& components,
const TModelAllocator& allocator);

//! Add a new calendar component.
void addCalendarComponent(const CCalendarFeature& feature, core_t::TTime timeZoneOffset);
void addCalendarComponent(const CCalendarFeature& feature,
const TModelAllocator& allocator,
core_t::TTime timeZoneOffset);

//! Fit the trend component \p component to \p values.
void fitTrend(core_t::TTime startTime,
Expand Down Expand Up @@ -1014,13 +1030,17 @@ class MATHS_TIME_SERIES_EXPORT CTimeSeriesDecompositionDetail
TComponentChangeCallback m_ComponentChangeCallback;

//! Supplied with an annotation if a component is added.
maths_t::TModelAnnotationCallback m_ModelAnnotationCallback;
maths_t::TModelAnnotationCallback m_ModelAnnotationCallback{
[](const std::string&) {}};

//! Set to true if the trend model should be used for prediction.
bool m_UsingTrendForPrediction = false;
bool m_UsingTrendForPrediction{false};

//! Befriend a helper class used by the unit tests
friend class CTimeSeriesDecompositionTest::CNanInjector;

// Befriend a helper class used by the unit tests
friend class CTimeSeriesDecompositionTest::CComponentsTest;
};
};

Expand Down
5 changes: 5 additions & 0 deletions include/maths/time_series/CTimeSeriesDecompositionInterface.h
Expand Up @@ -31,6 +31,7 @@
namespace ml {
namespace maths {
namespace common {
class CModelAllocator;
class CMultivariatePrior;
class CPrior;
}
Expand Down Expand Up @@ -108,6 +109,7 @@ class MATHS_TIME_SERIES_EXPORT CTimeSeriesDecompositionInterface
virtual void
addPoint(core_t::TTime time,
double value,
const common::CModelAllocator& allocator,
const maths_t::TDoubleWeightsAry& weights = TWeights::UNIT,
const TComponentChangeCallback& componentChangeCallback = noopComponentChange,
const maths_t::TModelAnnotationCallback& modelAnnotationCallback = noopModelAnnotation,
Expand Down Expand Up @@ -216,6 +218,9 @@ class MATHS_TIME_SERIES_EXPORT CTimeSeriesDecompositionInterface
//! Get the seasonal components.
virtual const maths_t::TSeasonalComponentVec& seasonalComponents() const = 0;

//! Get the calendar components.
virtual const maths_t::TCalendarComponentVec& calendarComponents() const = 0;

protected:
static void noopComponentChange(TFloatMeanAccumulatorVec) {}
static void noopModelAnnotation(const std::string&) {}
Expand Down
4 changes: 4 additions & 0 deletions include/maths/time_series/CTimeSeriesDecompositionStub.h
Expand Up @@ -48,6 +48,7 @@ class MATHS_TIME_SERIES_EXPORT CTimeSeriesDecompositionStub
//! No-op returning false.
void addPoint(core_t::TTime time,
double value,
const common::CModelAllocator& allocator,
const maths_t::TDoubleWeightsAry& weights = TWeights::UNIT,
const TComponentChangeCallback& componentChangeCallback = noopComponentChange,
const maths_t::TModelAnnotationCallback& modelAnnotationCallback = noopModelAnnotation,
Expand Down Expand Up @@ -120,6 +121,9 @@ class MATHS_TIME_SERIES_EXPORT CTimeSeriesDecompositionStub

//! Returns an empty vector.
const maths_t::TSeasonalComponentVec& seasonalComponents() const override;

//! Returns an empty vector.
const maths_t::TCalendarComponentVec& calendarComponents() const override;
};
}
}
Expand Down
8 changes: 6 additions & 2 deletions include/maths/time_series/CTimeSeriesModel.h
Expand Up @@ -97,6 +97,7 @@ class MATHS_TIME_SERIES_EXPORT CUnivariateTimeSeriesModel : public common::CMode

//! Update the model with new samples.
EUpdateResult addSamples(const common::CModelAddSamplesParams& params,
const TModelAllocator& allocator,
TTimeDouble2VecSizeTrVec samples) override;

//! Advance time by \p gap.
Expand Down Expand Up @@ -228,7 +229,8 @@ class MATHS_TIME_SERIES_EXPORT CUnivariateTimeSeriesModel : public common::CMode

//! Update the trend with \p samples.
EUpdateResult updateTrend(const common::CModelAddSamplesParams& params,
const TTimeDouble2VecSizeTrVec& samples);
const TTimeDouble2VecSizeTrVec& samples,
const TModelAllocator& allocator);

//! Update the residual models.
TTimeDouble2VecSizeTrVecDoublePr
Expand Down Expand Up @@ -583,6 +585,7 @@ class MATHS_TIME_SERIES_EXPORT CMultivariateTimeSeriesModel : public common::CMo

//! Update the model with new samples.
EUpdateResult addSamples(const common::CModelAddSamplesParams& params,
const TModelAllocator& allocator,
TTimeDouble2VecSizeTrVec samples) override;

//! Advance time by \p gap.
Expand Down Expand Up @@ -701,7 +704,8 @@ class MATHS_TIME_SERIES_EXPORT CMultivariateTimeSeriesModel : public common::CMo
private:
//! Update the trend with \p samples.
EUpdateResult updateTrend(const common::CModelAddSamplesParams& params,
const TTimeDouble2VecSizeTrVec& samples);
const TTimeDouble2VecSizeTrVec& samples,
const TModelAllocator& allocator);

//! Update the residual models.
void updateResidualModels(const common::CModelAddSamplesParams& params,
Expand Down
4 changes: 4 additions & 0 deletions include/maths/time_series/CTimeSeriesTestForSeasonality.h
Expand Up @@ -19,6 +19,7 @@
#include <maths/common/CFuzzyLogic.h>
#include <maths/common/CLinearAlgebra.h>

#include <maths/time_series/CSeasonalComponent.h>
#include <maths/time_series/CSignal.h>
#include <maths/time_series/CTimeSeriesSegmentation.h>
#include <maths/time_series/ImportExport.h>
Expand Down Expand Up @@ -132,6 +133,9 @@ class MATHS_TIME_SERIES_EXPORT CNewSeasonalComponentSummary {
//! Get a description of the component.
std::string print() const;

//! Create a seasonal component from the initial values.
CSeasonalComponent createSeasonalComponent(double decayRate, double bucketLength) const;

private:
std::string m_AnnotationText;
TSeasonalComponent m_Period;
Expand Down
13 changes: 13 additions & 0 deletions include/model/CModelFactory.h
Expand Up @@ -14,6 +14,7 @@

#include <core/CoreTypes.h>

#include <maths/common/CModel.h>
#include <maths/common/COrderings.h>
#include <maths/common/MathsTypes.h>

Expand Down Expand Up @@ -50,6 +51,7 @@ class CAnomalyDetectorModel;
class CDataGatherer;
class CInfluenceCalculator;
class CInterimBucketCorrector;
class CResourceMonitor;
class CSearchKey;

//! \brief A factory class interface for the CAnomalyDetectorModel hierarchy.
Expand Down Expand Up @@ -456,6 +458,17 @@ class MODEL_EXPORT CModelFactory {
//! A cache of influence calculators for collections of features.
mutable TStrFeatureVecPrInfluenceCalculatorCPtrMap m_InfluenceCalculatorCache;
};

class CModelAllocator : public maths::common::CModelAllocator {
public:
explicit CModelAllocator(CResourceMonitor& resourceMonitor)
: m_ResourceMonitor{&resourceMonitor} {}

bool areAllocationsAllowed() const override;

private:
CResourceMonitor* m_ResourceMonitor;
};
valeriy42 marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down