// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include #include #include #include constexpr size_t loop_cnt = 64; constexpr size_t max_work = 1; // 500'000'000; constexpr bool dump_unexpected = false; constexpr bool verbose = false; using namespace search::queryeval; // at what in-flow (non-strict) rate is it equally cheap to be (forced) strict and non-strict double strict_crossover(const FlowStats &stats) { return (stats.strict_cost - 0.2 * stats.estimate) / (stats.cost - 0.2); } // how much cost do we save by having an iterator strict vs non-strict with the given in-flow double strict_gain(const FlowStats &stats, InFlow in_flow) { if (in_flow.strict()) { return stats.cost - stats.strict_cost; } else { return (in_flow.rate() * stats.cost) - flow::forced_strict_cost(stats, in_flow.rate()); } } template double ordered_cost_of(const std::vector &data, InFlow in_flow, bool allow_force_strict) { return flow::ordered_cost_of(flow::DirectAdapter(), data, FLOW(in_flow), allow_force_strict); } template double dual_ordered_cost_of(const std::vector &data, InFlow in_flow, bool allow_force_strict) { double result = flow::ordered_cost_of(flow::DirectAdapter(), data, FLOW(in_flow), allow_force_strict); AnyFlow any_flow = AnyFlow::create(in_flow); double total_cost = 0.0; for (const auto &item: data) { double child_cost = flow::min_child_cost(InFlow(any_flow.strict(), any_flow.flow()), item, allow_force_strict); any_flow.update_cost(total_cost, child_cost); any_flow.add(item.estimate); } EXPECT_DOUBLE_EQ(total_cost, result); return result; } std::vector gen_data(size_t size) { static std::mt19937 gen; static std::uniform_real_distribution estimate(0.0, 1.0); static std::uniform_real_distribution cost(1.0, 10.0); std::vector result; result.reserve(size); for (size_t i = 0; i < size; ++i) { double est = estimate(gen); std::uniform_real_distribution strict_cost(est, 5.0); result.emplace_back(est, cost(gen), strict_cost(gen)); } if (size == 0) { gen.seed(gen.default_seed); } return result; } void re_seed() { gen_data(0); } size_t count_perms(size_t n) { return (n <= 1) ? 1 : count_perms(n-1) * n; }; template void each_perm(std::vector &data, size_t k, F fun) { if (k <= 1) { fun(const_cast &>(data)); } else { each_perm(data, k-1, fun); for (size_t i = 0; i < k-1; ++i) { if (k & 1) { std::swap(data[0], data[k-1]); } else { std::swap(data[i], data[k-1]); } each_perm(data, k-1, fun); } } } template void each_perm(std::vector &data, F fun) { each_perm(data, data.size(), fun); } TEST(FlowTest, strict_crossover_and_gain) { auto list = gen_data(64); for (const auto &item: list) { double limit = strict_crossover(item); double gain = strict_gain(item, limit); EXPECT_NEAR(gain, 0.0, 1e-9); } } TEST(FlowTest, perm_test) { std::set> seen; std::vector data = {1,2,3,4,5}; auto hook = [&](const std::vector &perm) { EXPECT_EQ(perm.size(), 5); seen.insert(perm); }; each_perm(data, hook); EXPECT_EQ(seen.size(), 120); } template