diff options
Diffstat (limited to 'eval/src/tests')
5 files changed, 183 insertions, 47 deletions
diff --git a/eval/src/tests/eval/gen_spec/gen_spec_test.cpp b/eval/src/tests/eval/gen_spec/gen_spec_test.cpp index 855b298f295..a52ece90e6a 100644 --- a/eval/src/tests/eval/gen_spec/gen_spec_test.cpp +++ b/eval/src/tests/eval/gen_spec/gen_spec_test.cpp @@ -31,13 +31,13 @@ TEST(DimSpecTest, mapped_dimension) { TEST(DimSpecTest, simple_dictionary_creation) { auto dict = DimSpec::make_dict(5, 1, ""); - std::vector<vespalib::string> expect = {"0", "1", "2", "3", "4"}; + std::vector<vespalib::string> expect = {"1", "2", "3", "4", "5"}; EXPECT_EQ(dict, expect); } TEST(DimSpecTest, advanced_dictionary_creation) { auto dict = DimSpec::make_dict(5, 3, "str_"); - std::vector<vespalib::string> expect = {"str_0", "str_3", "str_6", "str_9", "str_12"}; + std::vector<vespalib::string> expect = {"str_3", "str_6", "str_9", "str_12", "str_15"}; EXPECT_EQ(dict, expect); } @@ -176,50 +176,50 @@ TEST(GenSpecTest, generating_custom_vector) { //----------------------------------------------------------------------------- TensorSpec basic_map = TensorSpec("tensor(a{})") - .add({{"a", "0"}}, 1.0) - .add({{"a", "1"}}, 2.0) - .add({{"a", "2"}}, 3.0); + .add({{"a", "1"}}, 1.0) + .add({{"a", "2"}}, 2.0) + .add({{"a", "3"}}, 3.0); TensorSpec custom_map = TensorSpec("tensor(a{})") - .add({{"a", "s0"}}, 1.0) - .add({{"a", "s5"}}, 2.0) - .add({{"a", "s10"}}, 3.0); + .add({{"a", "s5"}}, 1.0) + .add({{"a", "s10"}}, 2.0) + .add({{"a", "s15"}}, 3.0); TEST(GenSpecTest, generating_basic_map) { EXPECT_EQ(GenSpec().map("a", 3).gen(), basic_map); EXPECT_EQ(GenSpec().map("a", 3, 1).gen(), basic_map); EXPECT_EQ(GenSpec().map("a", 3, 1, "").gen(), basic_map); - EXPECT_EQ(GenSpec().map("a", {"0", "1", "2"}).gen(), basic_map); + EXPECT_EQ(GenSpec().map("a", {"1", "2", "3"}).gen(), basic_map); } TEST(GenSpecTest, generating_custom_map) { EXPECT_EQ(GenSpec().map("a", 3, 5, "s").gen(), custom_map); - EXPECT_EQ(GenSpec().map("a", {"s0", "s5", "s10"}).gen(), custom_map); + EXPECT_EQ(GenSpec().map("a", {"s5", "s10", "s15"}).gen(), custom_map); } //----------------------------------------------------------------------------- TensorSpec basic_mixed = TensorSpec("tensor(a{},b[1],c{},d[3])") - .add({{"a", "0"},{"b", 0},{"c", "0"},{"d", 0}}, 1.0) - .add({{"a", "0"},{"b", 0},{"c", "0"},{"d", 1}}, 2.0) - .add({{"a", "0"},{"b", 0},{"c", "0"},{"d", 2}}, 3.0) - .add({{"a", "1"},{"b", 0},{"c", "0"},{"d", 0}}, 4.0) - .add({{"a", "1"},{"b", 0},{"c", "0"},{"d", 1}}, 5.0) - .add({{"a", "1"},{"b", 0},{"c", "0"},{"d", 2}}, 6.0) - .add({{"a", "2"},{"b", 0},{"c", "0"},{"d", 0}}, 7.0) - .add({{"a", "2"},{"b", 0},{"c", "0"},{"d", 1}}, 8.0) - .add({{"a", "2"},{"b", 0},{"c", "0"},{"d", 2}}, 9.0); + .add({{"a", "1"},{"b", 0},{"c", "1"},{"d", 0}}, 1.0) + .add({{"a", "1"},{"b", 0},{"c", "1"},{"d", 1}}, 2.0) + .add({{"a", "1"},{"b", 0},{"c", "1"},{"d", 2}}, 3.0) + .add({{"a", "2"},{"b", 0},{"c", "1"},{"d", 0}}, 4.0) + .add({{"a", "2"},{"b", 0},{"c", "1"},{"d", 1}}, 5.0) + .add({{"a", "2"},{"b", 0},{"c", "1"},{"d", 2}}, 6.0) + .add({{"a", "3"},{"b", 0},{"c", "1"},{"d", 0}}, 7.0) + .add({{"a", "3"},{"b", 0},{"c", "1"},{"d", 1}}, 8.0) + .add({{"a", "3"},{"b", 0},{"c", "1"},{"d", 2}}, 9.0); TensorSpec inverted_mixed = TensorSpec("tensor(a{},b[1],c{},d[3])") - .add({{"a", "0"},{"b", 0},{"c", "0"},{"d", 0}}, 1.0) - .add({{"a", "1"},{"b", 0},{"c", "0"},{"d", 0}}, 2.0) - .add({{"a", "2"},{"b", 0},{"c", "0"},{"d", 0}}, 3.0) - .add({{"a", "0"},{"b", 0},{"c", "0"},{"d", 1}}, 4.0) - .add({{"a", "1"},{"b", 0},{"c", "0"},{"d", 1}}, 5.0) - .add({{"a", "2"},{"b", 0},{"c", "0"},{"d", 1}}, 6.0) - .add({{"a", "0"},{"b", 0},{"c", "0"},{"d", 2}}, 7.0) - .add({{"a", "1"},{"b", 0},{"c", "0"},{"d", 2}}, 8.0) - .add({{"a", "2"},{"b", 0},{"c", "0"},{"d", 2}}, 9.0); + .add({{"a", "1"},{"b", 0},{"c", "1"},{"d", 0}}, 1.0) + .add({{"a", "2"},{"b", 0},{"c", "1"},{"d", 0}}, 2.0) + .add({{"a", "3"},{"b", 0},{"c", "1"},{"d", 0}}, 3.0) + .add({{"a", "1"},{"b", 0},{"c", "1"},{"d", 1}}, 4.0) + .add({{"a", "2"},{"b", 0},{"c", "1"},{"d", 1}}, 5.0) + .add({{"a", "3"},{"b", 0},{"c", "1"},{"d", 1}}, 6.0) + .add({{"a", "1"},{"b", 0},{"c", "1"},{"d", 2}}, 7.0) + .add({{"a", "2"},{"b", 0},{"c", "1"},{"d", 2}}, 8.0) + .add({{"a", "3"},{"b", 0},{"c", "1"},{"d", 2}}, 9.0); TEST(GenSpecTest, generating_basic_mixed) { EXPECT_EQ(GenSpec().map("a", 3).idx("b", 1).map("c", 1).idx("d", 3).gen(), basic_mixed); diff --git a/eval/src/tests/eval/tensor_lambda/tensor_lambda_test.cpp b/eval/src/tests/eval/tensor_lambda/tensor_lambda_test.cpp index 657a6ca8d0e..a455e5522b3 100644 --- a/eval/src/tests/eval/tensor_lambda/tensor_lambda_test.cpp +++ b/eval/src/tests/eval/tensor_lambda/tensor_lambda_test.cpp @@ -31,7 +31,7 @@ EvalFixture::ParamRepo make_params() { .add("x3_float", GenSpec().idx("x", 3).cells(CellType::FLOAT)) .add("x3_bfloat16", GenSpec().idx("x", 3).cells(CellType::BFLOAT16)) .add("x3_int8", GenSpec().idx("x", 3).cells(CellType::INT8)) - .add("x3m", GenSpec().map("x", 3)) + .add("x3m", GenSpec().map("x", {"0", "1", "2"})) .add("x3y5", GenSpec().idx("x", 3).idx("y", 5)) .add("x3y5_float", GenSpec().idx("x", 3).idx("y", 5).cells(CellType::FLOAT)) .add("x3y5_bfloat16", GenSpec().idx("x", 3).idx("y", 5).cells(CellType::BFLOAT16)) diff --git a/eval/src/tests/eval/value_type/value_type_test.cpp b/eval/src/tests/eval/value_type/value_type_test.cpp index 3d4872c2ab3..245c9c30242 100644 --- a/eval/src/tests/eval/value_type/value_type_test.cpp +++ b/eval/src/tests/eval/value_type/value_type_test.cpp @@ -353,33 +353,34 @@ TEST("require that dimension index can be obtained") { void verify_predicates(const ValueType &type, bool expect_error, bool expect_double, bool expect_tensor, - bool expect_sparse, bool expect_dense) + bool expect_sparse, bool expect_dense, bool expect_mixed) { EXPECT_EQUAL(type.is_error(), expect_error); EXPECT_EQUAL(type.is_double(), expect_double); EXPECT_EQUAL(type.has_dimensions(), expect_tensor); EXPECT_EQUAL(type.is_sparse(), expect_sparse); EXPECT_EQUAL(type.is_dense(), expect_dense); + EXPECT_EQUAL(type.is_mixed(), expect_mixed); } TEST("require that type-related predicate functions work as expected") { - TEST_DO(verify_predicates(type("error"), true, false, false, false, false)); - TEST_DO(verify_predicates(type("double"), false, true, false, false, false)); - TEST_DO(verify_predicates(type("tensor()"), false, true, false, false, false)); - TEST_DO(verify_predicates(type("tensor(x{})"), false, false, true, true, false)); - TEST_DO(verify_predicates(type("tensor(x{},y{})"), false, false, true, true, false)); - TEST_DO(verify_predicates(type("tensor(x[5])"), false, false, true, false, true)); - TEST_DO(verify_predicates(type("tensor(x[5],y[10])"), false, false, true, false, true)); - TEST_DO(verify_predicates(type("tensor(x[5],y{})"), false, false, true, false, false)); - TEST_DO(verify_predicates(type("tensor<float>(x{})"), false, false, true, true, false)); - TEST_DO(verify_predicates(type("tensor<float>(x[5])"), false, false, true, false, true)); - TEST_DO(verify_predicates(type("tensor<float>(x[5],y{})"), false, false, true, false, false)); - TEST_DO(verify_predicates(type("tensor<bfloat16>(x{})"), false, false, true, true, false)); - TEST_DO(verify_predicates(type("tensor<bfloat16>(x[5])"), false, false, true, false, true)); - TEST_DO(verify_predicates(type("tensor<bfloat16>(x[5],y{})"), false, false, true, false, false)); - TEST_DO(verify_predicates(type("tensor<int8>(x{})"), false, false, true, true, false)); - TEST_DO(verify_predicates(type("tensor<int8>(x[5])"), false, false, true, false, true)); - TEST_DO(verify_predicates(type("tensor<int8>(x[5],y{})"), false, false, true, false, false)); + TEST_DO(verify_predicates(type("error"), true, false, false, false, false, false)); + TEST_DO(verify_predicates(type("double"), false, true, false, false, false, false)); + TEST_DO(verify_predicates(type("tensor()"), false, true, false, false, false, false)); + TEST_DO(verify_predicates(type("tensor(x{})"), false, false, true, true, false, false)); + TEST_DO(verify_predicates(type("tensor(x{},y{})"), false, false, true, true, false, false)); + TEST_DO(verify_predicates(type("tensor(x[5])"), false, false, true, false, true, false)); + TEST_DO(verify_predicates(type("tensor(x[5],y[10])"), false, false, true, false, true, false)); + TEST_DO(verify_predicates(type("tensor(x[5],y{})"), false, false, true, false, false, true)); + TEST_DO(verify_predicates(type("tensor<float>(x{})"), false, false, true, true, false, false)); + TEST_DO(verify_predicates(type("tensor<float>(x[5])"), false, false, true, false, true, false)); + TEST_DO(verify_predicates(type("tensor<float>(x[5],y{})"), false, false, true, false, false, true)); + TEST_DO(verify_predicates(type("tensor<bfloat16>(x{})"), false, false, true, true, false, false)); + TEST_DO(verify_predicates(type("tensor<bfloat16>(x[5])"), false, false, true, false, true, false)); + TEST_DO(verify_predicates(type("tensor<bfloat16>(x[5],y{})"), false, false, true, false, false, true)); + TEST_DO(verify_predicates(type("tensor<int8>(x{})"), false, false, true, true, false, false)); + TEST_DO(verify_predicates(type("tensor<int8>(x[5])"), false, false, true, false, true, false)); + TEST_DO(verify_predicates(type("tensor<int8>(x[5],y{})"), false, false, true, false, false, true)); } TEST("require that mapped and indexed dimensions can be counted") { diff --git a/eval/src/tests/instruction/mapped_lookup/CMakeLists.txt b/eval/src/tests/instruction/mapped_lookup/CMakeLists.txt new file mode 100644 index 00000000000..6bc598235ea --- /dev/null +++ b/eval/src/tests/instruction/mapped_lookup/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(eval_mapped_lookup_test_app TEST + SOURCES + mapped_lookup_test.cpp + DEPENDS + vespaeval + GTest::GTest +) +vespa_add_test(NAME eval_mapped_lookup_test_app COMMAND eval_mapped_lookup_test_app) diff --git a/eval/src/tests/instruction/mapped_lookup/mapped_lookup_test.cpp b/eval/src/tests/instruction/mapped_lookup/mapped_lookup_test.cpp new file mode 100644 index 00000000000..1ffc6b04f4b --- /dev/null +++ b/eval/src/tests/instruction/mapped_lookup/mapped_lookup_test.cpp @@ -0,0 +1,126 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/eval/eval/fast_value.h> +#include <vespa/eval/eval/simple_value.h> +#include <vespa/eval/instruction/mapped_lookup.h> +#include <vespa/eval/eval/test/eval_fixture.h> +#include <vespa/eval/eval/test/gen_spec.h> +#include <vespa/vespalib/gtest/gtest.h> + +using namespace vespalib::eval; +using namespace vespalib::eval::test; + +//----------------------------------------------------------------------------- + +struct FunInfo { + using LookFor = MappedLookup; + bool expect_mutable; + FunInfo(bool expect_mutable_in) + : expect_mutable(expect_mutable_in) {} + void verify(const LookFor &fun) const { + EXPECT_EQ(fun.result_is_mutable(), expect_mutable); + } +}; + +void verify_optimized_cell_types(const vespalib::string &expr) { + auto same_stable_types = CellTypeSpace(CellTypeUtils::list_stable_types(), 2).same(); + auto same_unstable_types = CellTypeSpace(CellTypeUtils::list_unstable_types(), 2).same(); + auto different_types = CellTypeSpace(CellTypeUtils::list_types(), 2).different(); + EvalFixture::verify<FunInfo>(expr, {FunInfo(false)}, same_stable_types); + EvalFixture::verify<FunInfo>(expr, {}, same_unstable_types); + EvalFixture::verify<FunInfo>(expr, {}, different_types); +} + +void verify_optimized(const vespalib::string &expr, bool expect_mutable = false) { + CellTypeSpace just_float({CellType::FLOAT}, 2); + EvalFixture::verify<FunInfo>(expr, {FunInfo(expect_mutable)}, just_float); +} + +void verify_not_optimized(const vespalib::string &expr) { + CellTypeSpace just_float({CellType::FLOAT}, 2); + EvalFixture::verify<FunInfo>(expr, {}, just_float); +} + +//----------------------------------------------------------------------------- + +TEST(MappedLookup, expression_can_be_optimized) { + verify_optimized_cell_types("reduce(x1_1*x5_1y5,sum,x)"); +} + +TEST(MappedLookup, key_and_map_can_be_swapped) { + verify_optimized("reduce(x5_1y5*x1_1,sum,x)"); +} + +TEST(MappedLookup, trivial_indexed_dimensions_are_ignored) { + verify_optimized("reduce(c1d1x1_1*a1b1x5_1y5,sum,x,c,d,a,b)"); + verify_optimized("reduce(c1d1x1_1*a1b1x5_1y5,sum,x,c,a)"); + verify_optimized("reduce(c1d1x1_1*a1b1x5_1y5,sum,x)"); +} + +TEST(MappedLookup, mutable_map_gives_mutable_result) { + verify_optimized("reduce(@x1_1*x5_1y5,sum,x)", false); + verify_optimized("reduce(x1_1*@x5_1y5,sum,x)", true); + verify_optimized("reduce(@x5_1y5*x1_1,sum,x)", true); + verify_optimized("reduce(x5_1y5*@x1_1,sum,x)", false); + verify_optimized("reduce(@x5_1y5*@x1_1,sum,x)", true); +} + +TEST(MappedLookup, similar_expressions_are_not_optimized) { + verify_not_optimized("reduce(x1_1*x5_1,sum,x)"); + verify_not_optimized("reduce(x1_1*x5_1y5,sum,y)"); + verify_not_optimized("reduce(x1_1*x5_1y5,sum)"); + verify_not_optimized("reduce(x1_1*x5_1y5z8,sum,x,y)"); + verify_not_optimized("reduce(x1_1*x5_1y5,prod,x)"); + verify_not_optimized("reduce(x1_1y3_3*x5_1y3_2z5,sum,x)"); + verify_not_optimized("reduce(x1_1y3_3*x5_1y3_2z5,sum,x,y)"); + verify_not_optimized("reduce(x1_1y5*x5_1z5,sum,x)"); +} + +enum class KeyType { EMPTY, UNIT, SCALING, MULTI }; +GenSpec make_key(KeyType type) { + switch (type) { + case KeyType::EMPTY: return GenSpec().cells_float().map("x", {}); + case KeyType::UNIT: return GenSpec().cells_float().map("x", {"1"}).seq({1.0}); + case KeyType::SCALING: return GenSpec().cells_float().map("x", {"1"}).seq({5.0}); + case KeyType::MULTI: return GenSpec().cells_float().map("x", {"1", "2", "3"}).seq({1.0}); + } + abort(); +} + +enum class MapType { EMPTY, SMALL, MEDIUM, LARGE1, LARGE2, LARGE3 }; +GenSpec make_map(MapType type) { + switch (type) { + case MapType::EMPTY: return GenSpec().cells_float().idx("y", 5).map("x", {}); + case MapType::SMALL: return GenSpec().cells_float().idx("y", 5).map("x", {"1"}).seq(N(10)); + case MapType::MEDIUM: return GenSpec().cells_float().idx("y", 5).map("x", {"1", "2"}).seq(N(10)); + case MapType::LARGE1: return GenSpec().cells_float().idx("y", 5).map("x", 5, 100).seq(N(10)); + case MapType::LARGE2: return GenSpec().cells_float().idx("y", 5).map("x", 5, 2).seq(N(10)); + case MapType::LARGE3: return GenSpec().cells_float().idx("y", 5).map("x", 5, 1).seq(N(10)); + } + abort(); +} + +std::vector<MapType> map_types_for(KeyType key_type) { + if (key_type == KeyType::MULTI) { + return {MapType::EMPTY, MapType::SMALL, MapType::MEDIUM, MapType::LARGE1, MapType::LARGE2, MapType::LARGE3}; + } else { + return {MapType::EMPTY, MapType::SMALL, MapType::MEDIUM}; + } +} + +TEST(MappedLookup, test_case_interactions) { + for (bool mutable_map: {false, true}) { + vespalib::string expr = mutable_map ? "reduce(a*@b,sum,x)" : "reduce(a*b,sum,x)"; + for (KeyType key_type: {KeyType::EMPTY, KeyType::UNIT, KeyType::SCALING, KeyType::MULTI}) { + auto key = make_key(key_type); + for (MapType map_type: map_types_for(key_type)) { + auto map = make_map(map_type); + EvalFixture::verify<FunInfo>(expr, {FunInfo(mutable_map)}, {key,map}); + } + } + } +} + +//----------------------------------------------------------------------------- + +GTEST_MAIN_RUN_ALL_TESTS() |