summaryrefslogtreecommitdiffstats
path: root/src/bm/index.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/bm/index.cpp')
-rw-r--r--src/bm/index.cpp288
1 files changed, 288 insertions, 0 deletions
diff --git a/src/bm/index.cpp b/src/bm/index.cpp
new file mode 100644
index 0000000..4e1cbd1
--- /dev/null
+++ b/src/bm/index.cpp
@@ -0,0 +1,288 @@
+/****************************************************************************
+**
+** 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 "bmmisc.h"
+#include <QBitArray>
+#include <QDebug>
+
+
+// ### 2 B DOCUMENTED!
+// Note: The dynamic memory in \a candidateRHInfos is deleted by this class.
+Index::Index(
+ const QList<ResultHistoryInfo *> &candidateRHInfos, const int baseTimestamp,
+ const QList<int> &evalTimestamps, int medianWinSize, int *rejectedNonPositive)
+ : baseTimestamp(baseTimestamp), evalTimestamps(evalTimestamps)
+ , medianWinSize(medianWinSize), valid(false), invalidReason_("uninitialized")
+{
+ init(candidateRHInfos, 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<ResultHistoryInfo *> &candidateRHInfos, int *rejectedNonPositive)
+{
+ if (baseTimestamp < 0) {
+ valid = false;
+ invalidReason_ = "negative base timestamp";
+ return;
+ }
+
+ for (int i = 1; i < evalTimestamps.size(); ++i) {
+ if (evalTimestamps.at(i - 1) > evalTimestamps.at(i)) {
+ valid = false;
+ invalidReason_ = "non-increasing eval timestamp order";
+ return;
+ }
+ }
+
+ if (evalTimestamps.first() < 0) {
+ valid = false;
+ invalidReason_ = "negative eval timestamp";
+ return;
+ }
+
+ populateRHInfos(candidateRHInfos, rejectedNonPositive);
+ if (rhInfos.isEmpty()) {
+ valid = false;
+ invalidReason_ = "no valid result histories";
+ return;
+ }
+
+ valid = true;
+ invalidReason_ = "";
+}
+
+// Populates rhInfos from \a candidateRHInfos by rejecting certain result histories.
+void Index::populateRHInfos(
+ const QList<ResultHistoryInfo *> &candidateRHInfos, int *rejectedNonPositive)
+{
+ int rejectedNonPositive_ = 0;
+
+ for (int i = 0; i < candidateRHInfos.size(); ++i) {
+
+ ResultHistoryInfo *rhInfo = candidateRHInfos.at(i);
+ Q_ASSERT(rhInfo->size() > 0);
+
+ // Reject candidate if the time series contains at least one non-positive value ...
+ int j = 0;
+ for (; j < rhInfo->size(); ++j) {
+ if (rhInfo->value(j) <= 0.0)
+ break;
+ }
+ if (j < rhInfo->size()) {
+ delete rhInfo;
+ ++rejectedNonPositive_;
+ continue;
+ }
+
+ // Accept candidate ...
+ rhInfos.append(rhInfo);
+ }
+
+ if (rejectedNonPositive)
+ *rejectedNonPositive = rejectedNonPositive_;
+}
+
+
+//+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
+
+bool IndexAlgorithm1::computeValues(
+ QList<qreal> *values, int *baseValuePos, QList<int> *contrCounts, QString *error,
+ QList<QList<RankedInfo> > *topContr, int topContrLimit) 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<int> basePos(rhInfos.size()); // Base value pos of each result history.
+ QVector<qreal> diffPrev(rhInfos.size()); // Previous base diff value of each result hist.
+ QVector<qreal> targetValPrev(rhInfos.size()); // Previous target value of each result history.
+ QVector<int> targetPosPrev(rhInfos.size()); // 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<QPair<RankedInfo, qreal> > rankedBest; // ... highest BDC
+ QList<QPair<RankedInfo, qreal> > 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->getSmoothPos(evalTimestamps.at(i), medianWinSize, &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);
+
+ const qreal diffChange = diff - diffPrev.at(j);
+
+ const QString descr =
+ QString("base val: %1; val1: %2; val2: %3; abs log2 diff change: %4")
+ .arg(baseVal)
+ .arg(targetValPrev.at(j))
+ .arg(targetVal)
+ .arg(qAbs(diffChange));
+ RankedInfo rankedInfo(
+ rhInfo->bmcontextId(), basePos.at(j), targetPosPrev.at(j), targetPos,
+ descr);
+
+ BMMisc::insertRankedId<RankedInfo>(
+ &rankedBest, topContrLimit, rankedInfo, diffChange);
+ BMMisc::insertRankedId<RankedInfo>(
+ &rankedWorst, topContrLimit, rankedInfo, -diffChange);
+ diffPrev.replace(j, diff);
+
+ } else {
+ // Record base value position ...
+ basePos.replace(j, targetPos);
+ hasBase.setBit(j);
+ }
+
+ targetValPrev.replace(j, targetVal);
+ targetPosPrev.replace(j, targetPos);
+ }
+ }
+
+ 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<RankedInfo> 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<QPair<RankedInfo, qreal> > * 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);
+ }
+ }
+
+ return true;
+}