bool CUnivariateTimeSeriesModel::uncorrelatedProbability()

in lib/maths/time_series/CTimeSeriesModel.cc [953:1078]


bool CUnivariateTimeSeriesModel::uncorrelatedProbability(
    const common::CModelProbabilityParams& params,
    const TTime2Vec1Vec& time_,
    const TDouble2Vec1Vec& value,
    common::SModelProbabilityResult& result) const {

    maths_t::EProbabilityCalculation calculation{params.calculation(0)};
    maths_t::TDoubleWeightsAry1Vec weights{unpack(params.weights()[0])};

    TDouble4Vec probabilities;
    common::SModelProbabilityResult::TFeatureProbability4Vec featureProbabilities;

    double pl;
    double pu;
    maths_t::ETail tail;
    core_t::TTime time{time_[0][0]};
    TDouble1Vec sample{m_TrendModel->detrend(
        time, value[0][0], params.seasonalConfidenceInterval(), m_IsNonNegative)};
    if (m_ResidualModel->probabilityOfLessLikelySamples(calculation, sample,
                                                        weights, pl, pu, tail)) {
        LOG_TRACE(<< "P(" << sample << " | weight = " << weights
                  << ", time = " << time << ") = " << (pl + pu) / 2.0);
        double pSingleBucket{(pl + pu) / 2.0};
        probabilities.push_back(pSingleBucket);
        featureProbabilities.emplace_back(
            common::SModelProbabilityResult::E_SingleBucketProbability, pSingleBucket);
        if (pSingleBucket < common::LARGEST_SIGNIFICANT_PROBABILITY) {
            result.s_AnomalyScoreExplanation.s_SingleBucketImpact =
                static_cast<int>(std::round(-std::log10(pSingleBucket)));
        }

        if (maths_t::seasonalVarianceScale(weights[0]) > 1.0) {
            result.s_AnomalyScoreExplanation.s_HighVariancePenalty = true;
        }
        if (maths_t::countVarianceScale(weights[0]) > 1.0) {
            result.s_AnomalyScoreExplanation.s_IncompleteBucketPenalty = true;
        }

    } else {
        LOG_ERROR(<< "Failed to compute P(" << sample
                  << " | weight = " << weights << ", time = " << time << ")");
        return false;
    }

    double correlation{0.0};
    if (m_MultibucketFeatureModel != nullptr && params.useMultibucketFeatures()) {
        double pMultiBucket{1.0};
        TDouble1Vec feature;
        std::tie(feature, std::ignore) = m_MultibucketFeature->value();
        if (feature.empty() == false) {
            for (auto calculation_ : expand(calculation)) {
                maths_t::ETail dummy;
                if (m_MultibucketFeatureModel->probabilityOfLessLikelySamples(
                        calculation_, feature,
                        maths_t::CUnitWeights::SINGLE_UNIT, pl, pu, dummy)) {
                    LOG_TRACE(<< "P(" << feature << ") = " << (pl + pu) / 2.0);
                } else {
                    LOG_ERROR(<< "Failed to compute P(" << feature << ")");
                    return false;
                }
                pMultiBucket = std::min(pMultiBucket, (pl + pu) / 2.0);
            }
            correlation = m_MultibucketFeature->correlationWithBucketValue();
        }

        if (pMultiBucket < common::LARGEST_SIGNIFICANT_PROBABILITY) {
            result.s_AnomalyScoreExplanation.s_MultiBucketImpact =
                static_cast<int>(std::round(-std::log10(pMultiBucket)));
        }

        probabilities.push_back(pMultiBucket);
        featureProbabilities.emplace_back(
            common::SModelProbabilityResult::E_MultiBucketProbability, pMultiBucket);
    }

    double pOverall{aggregateFeatureProbabilities(probabilities, correlation)};

    if (m_AnomalyModel != nullptr && params.useAnomalyModel()) {
        TDouble2Vec seasonalWeight;
        this->seasonalWeight(0.0, time, seasonalWeight);
        double residual{
            (sample[0] - m_ResidualModel->nearestMarginalLikelihoodMean(sample[0])) /
            std::max(std::sqrt(seasonalWeight[0]), 1.0)};
        double pSingleBucket{probabilities[0]};

        m_AnomalyModel->sample(params, time, residual, pSingleBucket, pOverall);

        double pAnomaly;
        std::tie(pOverall, pAnomaly) = m_AnomalyModel->probability(pSingleBucket, pOverall);
        if (pAnomaly < common::LARGEST_SIGNIFICANT_PROBABILITY) {
            result.s_AnomalyScoreExplanation.s_AnomalyType =
                (m_AnomalyModel->sumPredictionError() > (0.0))
                    ? common::SAnomalyScoreExplanation::E_SPIKE
                    : common::SAnomalyScoreExplanation::E_DIP;

            result.s_AnomalyScoreExplanation.s_AnomalyLength = m_AnomalyModel->length(time);

            result.s_AnomalyScoreExplanation.s_AnomalyCharacteristicsImpact =
                static_cast<int>(std::round(-std::log10(pAnomaly)));
        }

        probabilities.push_back(pAnomaly);
        featureProbabilities.emplace_back(
            common::SModelProbabilityResult::E_AnomalyModelProbability, pAnomaly);
    }

    if (pOverall < common::LARGEST_SIGNIFICANT_PROBABILITY) {
        LOG_TRACE(<< "Computing confidence bounds");
        TDouble2Vec3Vec interval(this->confidenceInterval(
            time, CModel::DEFAULT_BOUNDS_PERCENTILE, params.weights()[0]));
        result.s_AnomalyScoreExplanation.s_LowerConfidenceBound = interval[0][0];
        result.s_AnomalyScoreExplanation.s_TypicalValue = interval[1][0];
        result.s_AnomalyScoreExplanation.s_UpperConfidenceBound = interval[2][0];
    }

    result.s_AnomalyScoreExplanation.s_MultimodalDistribution =
        m_ResidualModel->isSelectedModelMultimodal();
    LOG_TRACE(<< "Multimodel distribution: "
              << result.s_AnomalyScoreExplanation.s_MultimodalDistribution);

    result.s_Probability = pOverall;
    result.s_FeatureProbabilities = std::move(featureProbabilities);
    result.s_Tail = {tail};

    return true;
}