Commit 1d960fb8 authored by Min Qin's avatar Min Qin Committed by Commit Bot

Implement time decay for tile score

This CL implements the time decay for tile score. The equation is
score = original_score * exp(-0.099 * time_passed).

BUG=1096224

Change-Id: Id623232a7510f2ca63f854e75846bc6119fdba03
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2417497
Commit-Queue: Min Qin <qinmin@chromium.org>
Reviewed-by: default avatarShakti Sahu <shaktisahu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#808632}
parent 934d7b1d
......@@ -48,6 +48,8 @@ constexpr char kBackoffInitDelayInMsKey[] = "backoff_policy_init_delay_in_ms";
// Finch parameter key for Backoff policy maximum delay in ms.
constexpr char kBackoffMaxDelayInMsKey[] = "backoff_policy_max_delay_in_ms";
constexpr char kTileScoreDecayLambdaKey[] = "tile_score_decay_lambda";
// Default expire duration.
constexpr int kDefaultExpireDurationInSeconds = 48 * 60 * 60; // 2 days.
......@@ -67,6 +69,9 @@ constexpr int kDefaultBackoffInitDelayInMs = 30 * 1000; // 30 seconds.
// Default maximum delay in backoff policy, also used for suspend duration.
constexpr int kDefaultBackoffMaxDelayInMs = 24 * 3600 * 1000; // 1 day.
// Default lambda value used for calculating tile score decay over time.
constexpr double kDefaultTileScoreDecayLambda = -0.099;
namespace {
// For testing. Json string for single tier experiment tag.
......@@ -171,4 +176,11 @@ int TileConfig::GetBackoffPolicyArgsMaxDelayInMs() {
kDefaultBackoffMaxDelayInMs);
}
// static
double TileConfig::GetTileScoreDecayLambda() {
return base::GetFieldTrialParamByFeatureAsDouble(
features::kQueryTiles, kTileScoreDecayLambdaKey,
kDefaultTileScoreDecayLambda);
}
} // namespace query_tiles
......@@ -47,6 +47,9 @@ extern const char kBackoffInitDelayInMsKey[];
// Finch parameter key for Backoff policy maximum delay in ms.
extern const char kBackoffMaxDelayInMsKey[];
// Finch parameter key for lambda in tile score decay calculation.
extern const char kTileScoreDecayLambdaKey[];
class TileConfig {
public:
// Gets the URL for the Query Tiles server.
......@@ -84,6 +87,9 @@ class TileConfig {
// Get the max delay (unit:ms) argument for backoff policy.
static int GetBackoffPolicyArgsMaxDelayInMs();
// Get the lambda value used for calculating the tile score decay over time.
static double GetTileScoreDecayLambda();
};
} // namespace query_tiles
......
......@@ -7,9 +7,13 @@
#include <sstream>
#include <utility>
#include "tile_utils.h"
namespace query_tiles {
namespace {
// Score to be received by a tile when it is clicked.
constexpr double kTileClickScore = 1.0;
void DeepCopyGroup(const TileGroup& input, TileGroup* output) {
DCHECK(output);
......@@ -39,6 +43,16 @@ bool TileGroup::operator!=(const TileGroup& other) const {
return !(*this == other);
}
void TileGroup::OnTileClicked(const std::string& tile_id) {
base::Time now_time = base::Time::Now();
auto iter = tile_stats.find(tile_id);
double score =
(iter == tile_stats.end())
? kTileClickScore
: kTileClickScore + CalculateTileScore(iter->second, now_time);
tile_stats[tile_id] = TileStats(now_time, score);
}
TileGroup::TileGroup(const TileGroup& other) {
DeepCopyGroup(other, this);
}
......
......@@ -29,6 +29,9 @@ struct TileGroup {
bool operator==(const TileGroup& other) const;
bool operator!=(const TileGroup& other) const;
// Called when a tile was clicked, need to recalculate |tile_stats|.
void OnTileClicked(const std::string& tile_id);
// Unique id for the group.
std::string id;
......
......@@ -69,6 +69,18 @@ TEST(TileGroupTest, MoveOperator) {
EXPECT_TRUE(test::AreTileGroupsIdentical(expected, rhs));
}
TEST(TileGroupTest, OnTileClicked) {
base::Time now_time = base::Time::Now();
TileGroup group;
group.tile_stats["guid-1-1"] = TileStats(now_time, 0);
group.tile_stats["guid-1-2"] =
TileStats(now_time + base::TimeDelta::FromHours(1), 0.5);
group.OnTileClicked("guid-1-1");
EXPECT_EQ(group.tile_stats["guid-1-1"].score, 1);
group.OnTileClicked("guid-1-2");
EXPECT_EQ(group.tile_stats["guid-1-2"].score, 1.5);
}
} // namespace
} // namespace query_tiles
......@@ -5,24 +5,22 @@
#include <algorithm>
#include <limits>
#include "components/query_tiles/internal/tile_config.h"
#include "components/query_tiles/internal/tile_utils.h"
namespace query_tiles {
namespace {
struct TileComparator {
explicit TileComparator(const std::map<std::string, TileStats>& tile_stats)
: tile_stats(tile_stats) {}
explicit TileComparator(const std::map<std::string, double>& tile_score_map)
: tile_score_map(tile_score_map) {}
inline bool operator()(const std::unique_ptr<Tile>& a,
const std::unique_ptr<Tile>& b) {
auto iter_a = tile_stats.find(a->id);
auto iter_b = tile_stats.find(b->id);
return (iter_a != tile_stats.end() ? iter_a->second.score : 0) >
(iter_b != tile_stats.end() ? iter_b->second.score : 0);
return tile_score_map[a->id] > tile_score_map[b->id];
}
std::map<std::string, TileStats> tile_stats;
std::map<std::string, double> tile_score_map;
};
} // namespace
......@@ -47,38 +45,59 @@ void SortTiles(std::vector<std::unique_ptr<Tile>>* tiles,
// score will be (0.5, 0.5, 0.7). Simularly, (0.5, new_tile1, 0.7, new_tile2)
// will result in (0.5, 0.5, 0.7, 0).
double last_score = std::numeric_limits<double>::max();
size_t new_tile_index = 0;
base::Time now_time = base::Time::Now();
TileStats last_tile_stats(now_time, last_score);
size_t new_tile_index = 0;
std::map<std::string, double> score_map;
// Find any tiles that don't have scores, and add new entries for them.
for (size_t i = 0; i < tiles->size(); ++i) {
auto iter = tile_stats->find((*tiles)[i]->id);
// Find a new tile. Skip it for now, will add the entry when we found the
// Found a new tile. Skip it for now, will add the entry when we found the
// first
if (iter == tile_stats->end())
continue;
// If the previous tiles are new tiles, fill them with a value that is
// minimum of their neighbors.
double new_score = CalculateTileScore(iter->second, now_time);
// If the previous tiles are new tiles, fill them with the same tile stats
// from the neighbor that has the minimum score. Using the same tile stats
// will allow tiles to have the same rate of decay over time.
if (i > new_tile_index) {
double score = std::min(last_score, iter->second.score);
TileStats new_stats(now_time, score);
for (size_t j = new_tile_index; j < i; ++j)
double min_score = std::min(new_score, last_score);
TileStats new_stats =
new_score > last_score ? last_tile_stats : iter->second;
for (size_t j = new_tile_index; j < i; ++j) {
tile_stats->emplace((*tiles)[j]->id, new_stats);
score_map.emplace((*tiles)[j]->id, min_score);
}
}
// Move |new_tile_index| to the next one that might not have
// a score.
new_tile_index = i + 1;
last_score = iter->second.score;
last_score = new_score;
last_tile_stats = iter->second;
score_map.emplace((*tiles)[i]->id, last_score);
}
// Fill the new tiles at the end with 0 score.
if (new_tile_index < tiles->size()) {
TileStats new_stats(now_time, 0);
for (size_t j = new_tile_index; j < tiles->size(); ++j)
for (size_t j = new_tile_index; j < tiles->size(); ++j) {
tile_stats->emplace((*tiles)[j]->id, new_stats);
score_map.emplace((*tiles)[j]->id, 0);
}
}
// Sort the tiles in descending order.
std::sort(tiles->begin(), tiles->end(), TileComparator(*tile_stats));
std::sort(tiles->begin(), tiles->end(), TileComparator(score_map));
for (auto& tile : *tiles)
SortTiles(&tile->sub_tiles, tile_stats);
}
double CalculateTileScore(const TileStats& tile_stats,
base::Time current_time) {
if (tile_stats.last_clicked_time >= current_time)
return tile_stats.score;
return tile_stats.score *
exp(TileConfig::GetTileScoreDecayLambda() *
(current_time - tile_stats.last_clicked_time).InDaysFloored());
}
} // namespace query_tiles
......@@ -19,6 +19,10 @@ namespace query_tiles {
void SortTiles(std::vector<std::unique_ptr<Tile>>* tiles,
std::map<std::string, TileStats>* tile_stats);
// Calculates the current tile score based on |current_time|. Tile score will
// decay over time.
double CalculateTileScore(const TileStats& tile_stats, base::Time current_time);
} // namespace query_tiles
#endif // COMPONENTS_QUERY_TILES_INTERNAL_TILE_UTILS_H_
......@@ -96,6 +96,18 @@ TEST(TileUtilsTest, SortWithNewTilesInTheMiddle) {
EXPECT_EQ(group.tiles[1]->id, "guid-1-1");
EXPECT_EQ(group.tiles[2]->id, "guid-1-2");
EXPECT_EQ(tile_stats["guid-1-2"].score, 0.5);
EXPECT_EQ(tile_stats["guid-1-2"].last_clicked_time, group.last_updated_ts);
}
TEST(TileUtilsTest, CalculateTileScore) {
base::Time now_time = base::Time::Now();
EXPECT_EQ(CalculateTileScore(TileStats(now_time, 0.7), now_time), 0.7);
EXPECT_EQ(CalculateTileScore(TileStats(now_time, 1.0),
now_time + base::TimeDelta::FromHours(18)),
1.0);
EXPECT_EQ(CalculateTileScore(TileStats(now_time, 1.0),
now_time + base::TimeDelta::FromDays(1)),
exp(-0.099));
}
} // namespace
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment