/**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Qt Software Information (qt-info@nokia.com) ** ** This file is part of the BM project on Qt Labs. ** ** This file may be used under the terms of the GNU General Public ** License version 2.0 or 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of ** this file. Please review the following information to ensure GNU ** General Public Licensing requirements will be met: ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and ** http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at qt-sales@nokia.com. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ****************************************************************************/ #include "index.h" #include "resulthistoryinfo.h" #include "dataqualitystats.h" #include "bmmisc.h" #include #include // ### 2 B DOCUMENTED! // Note: The dynamic memory in \a candidateRHInfos is deleted by this class. Index::Index( const QList &candidateRHInfos, const int baseTimestamp, QList *evalTimestamps, int *rejectedNonPositive) : baseTimestamp(baseTimestamp), valid(false), invalidReason_("uninitialized") { init(candidateRHInfos, evalTimestamps, rejectedNonPositive); } Index::~Index() { // Free dynamic memory ... for (int i = 0; i < rhInfos.size(); ++i) delete rhInfos.at(i); } // ### 2 B DOCUMENTED! void Index::init( const QList &candidateRHInfos, QList *evalTimestamps, int *rejectedNonPositive) { int rejectedNonPositive_ = 0; QSet etset; for (int i = 0; i < candidateRHInfos.size(); ++i) { ResultHistoryInfo *rhInfo = candidateRHInfos.at(i); Q_ASSERT(rhInfo->size() > 0); int j = 0; for (; j < rhInfo->size(); ++j) { // Reject candidate if the time series contains at least one non-positive value ... if (rhInfo->value(j) <= 0.0) break; // Expand union of eval timestamps ... etset.insert(rhInfo->timestamp(j)); } if (j < rhInfo->size()) { delete rhInfo; ++rejectedNonPositive_; continue; } // Accept candidate ... rhInfos.append(rhInfo); } if (rejectedNonPositive) *rejectedNonPositive = rejectedNonPositive_; if (rhInfos.isEmpty()) { valid = false; invalidReason_ = "no valid result histories"; return; } *evalTimestamps = etset.toList(); qSort(*evalTimestamps); evalTimestamps_ = *evalTimestamps; if (baseTimestamp < 0) baseTimestamp = evalTimestamps_.last(); valid = true; invalidReason_ = ""; } //+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= bool IndexAlgorithm1::computeValues( QList *values, int *baseValuePos, QList *contrCounts, QString *error, QList > *topContr, int topContrLimit, DataQualityStats *dqStats) const { if (!isValid()) { *error = invalidReason(); return false; } Q_ASSERT(rhInfos.size() > 0); Q_ASSERT(values); values->clear(); Q_ASSERT(contrCounts); contrCounts->clear(); QBitArray contr(rhInfos.size()); // Set of contributors (i.e. whether each result // history is contributing at the current timestamp). QBitArray hasBase(rhInfos.size()); // Whether a base value has been established for // each result history. QVector basePos(rhInfos.size()); // Base value pos of each result history. QVector diffPrev(rhInfos.size()); // Previous base diff value of each result hist. QBitArray hasTargetPosPrev(rhInfos.size()); // Whether a previous (computable) target pos has // been encountered for each result history. QVector targetValPrev(rhInfos.size()); // Previous target value of each result history. QVector targetPosPrev(rhInfos.size(), -1); // Previous target pos of each result history. qreal hc = 0.0; // History constant to adjust for changes to the set of contributors. bool contr_prev_empty = true; *baseValuePos = -1; // Loop over evaluation timestamps ... for (int i = 0; i < evalTimestamps_.size(); ++i) { if (*baseValuePos == -1) { // Determine if this evaluation timestamp represents the base timestamp ... if (baseTimestamp < evalTimestamps_.first()) { *baseValuePos = 0; } else if (((i < (evalTimestamps_.size() - 1)) && (baseTimestamp >= evalTimestamps_.at(i)) && (baseTimestamp < evalTimestamps_.at(i + 1))) || (i == (evalTimestamps_.size() - 1))) *baseValuePos = i; } const QBitArray contr_prev = contr; contr_prev_empty = (contr_prev.count(true) == 0); contr.fill(false); qreal dsum_all = 0.0; // Base diff sum of all contributors. qreal dsum_existing = 0.0; // Base diff sum of all contributors that contributed at // the previous evaluation timestamp as well. // Lists of most significant contributors ranked on BDC, where BDC is // the base diff change between the current and the previous eval timestamp: QList > rankedBest; // ... highest BDC QList > rankedWorst; // ... lowest BDC // Loop over potential contributors ... for (int j = 0; j < rhInfos.size(); ++j) { const ResultHistoryInfo *rhInfo = rhInfos.at(j); // Attempt to sample the result history at this timestamp ... int targetPos = -1; if (rhInfo->findSmoothPos(evalTimestamps_.at(i), &targetPos)) { const qreal targetVal = rhInfo->value(targetPos); if (hasBase.testBit(j)) { // Compute base diff and accumulate contribution ... const qreal baseVal = rhInfo->value(basePos.at(j)); const qreal diff = BMMisc::log2diff(targetVal, baseVal, rhInfo->metric()); dsum_all += diff; if (contr_prev.testBit(j)) dsum_existing += diff; contr.setBit(j); if (hasTargetPosPrev.testBit(j)) { const qreal diffChange = diff - diffPrev.at(j); const QString descr = QString("diff change: %1; base val: %2; val1: %3; val2: %4") .arg(diffChange) .arg(baseVal) .arg(targetValPrev.at(j)) .arg(targetVal); RankedInfo rankedInfo( rhInfo->bmcontextId(), basePos.at(j), targetPosPrev.at(j), targetPos, descr); BMMisc::insertRankedId( &rankedBest, topContrLimit, rankedInfo, diffChange); BMMisc::insertRankedId( &rankedWorst, topContrLimit, rankedInfo, -diffChange); } else { hasTargetPosPrev.setBit(j); } targetValPrev.replace(j, targetVal); targetPosPrev.replace(j, targetPos); diffPrev.replace(j, diff); } else { // Record base value position ... basePos.replace(j, targetPos); hasBase.setBit(j); } } } contrCounts->append(contr.count(true)); // record contribution count qreal indexValue = -1.0; if (contr.count(true) > 0) { // A base diff was computable for at least one contributor at this eval timestamp, // so a valid index value may be derived ... const qreal dmean_all = dsum_all / contr.count(true); if (contr_prev.count(true) > 0) { // A base diff was computable for at least one contributor at the previous // eval timestamp as well, so the set of contributors may potentially have // been changed. // Compute the arithmetic mean of the existing contributors only (i.e. those // who contributed both at this eval timestamp and the previous one) ... const qreal dmean_existing = dsum_existing / contr_prev.count(true); // The index value should only be influenced by the existing contributors only ... indexValue = dmean_existing - hc; if (contr != contr_prev) { // The set of contributors changed, so adjust the history constant to ensure // that the index value for the next eval timestamp is not influenced by the // new contributors until their base diffs start to change ... hc += (dmean_all - dmean_existing); } } else { indexValue = dmean_all; // special initialization case } } values->append(indexValue); // record the raw index value (valid or not) if (topContr) { // Establish (if possible) a list of the most significant contributors ... QList tc; if ((i > 0) && (contrCounts->at(i) > 0) && (contrCounts->at(i - 1) > 0)) { // The index value was computable for both this eval timestamp and the one // preceding it, so rank wrt. high or low base diff change depending on whether // the index value went up or down ... QList > * ranked = (values->at(i) > values->at(i - 1)) ? &rankedBest // up : &rankedWorst; // down for (int j = 0; j < ranked->size(); ++j) tc.append(ranked->at(j).first); } topContr->append(tc); } } if (dqStats) dqStats->compute(rhInfos); return true; }