summaryrefslogtreecommitdiffstats
path: root/eval/src/tests/instruction
diff options
context:
space:
mode:
Diffstat (limited to 'eval/src/tests/instruction')
-rw-r--r--eval/src/tests/instruction/generic_join/CMakeLists.txt9
-rw-r--r--eval/src/tests/instruction/generic_join/generic_join_test.cpp138
-rw-r--r--eval/src/tests/instruction/generic_rename/CMakeLists.txt9
-rw-r--r--eval/src/tests/instruction/generic_rename/generic_rename_test.cpp145
4 files changed, 301 insertions, 0 deletions
diff --git a/eval/src/tests/instruction/generic_join/CMakeLists.txt b/eval/src/tests/instruction/generic_join/CMakeLists.txt
new file mode 100644
index 00000000000..13fc6550d3c
--- /dev/null
+++ b/eval/src/tests/instruction/generic_join/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(eval_generic_join_test_app TEST
+ SOURCES
+ generic_join_test.cpp
+ DEPENDS
+ vespaeval
+ GTest::GTest
+)
+vespa_add_test(NAME eval_generic_join_test_app COMMAND eval_generic_join_test_app)
diff --git a/eval/src/tests/instruction/generic_join/generic_join_test.cpp b/eval/src/tests/instruction/generic_join/generic_join_test.cpp
new file mode 100644
index 00000000000..4821bf092da
--- /dev/null
+++ b/eval/src/tests/instruction/generic_join/generic_join_test.cpp
@@ -0,0 +1,138 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/eval/eval/simple_value.h>
+#include <vespa/eval/eval/value_codec.h>
+#include <vespa/eval/instruction/generic_join.h>
+#include <vespa/eval/eval/interpreted_function.h>
+#include <vespa/eval/eval/test/tensor_model.hpp>
+#include <vespa/vespalib/util/stringfmt.h>
+#include <vespa/vespalib/gtest/gtest.h>
+
+using namespace vespalib;
+using namespace vespalib::eval;
+using namespace vespalib::eval::instruction;
+using namespace vespalib::eval::test;
+
+using vespalib::make_string_short::fmt;
+
+std::vector<Layout> join_layouts = {
+ {}, {},
+ {x(5)}, {x(5)},
+ {x(5)}, {y(5)},
+ {x(5)}, {x(5),y(5)},
+ {y(3)}, {x(2),z(3)},
+ {x(3),y(5)}, {y(5),z(7)},
+ float_cells({x(3),y(5)}), {y(5),z(7)},
+ {x(3),y(5)}, float_cells({y(5),z(7)}),
+ float_cells({x(3),y(5)}), float_cells({y(5),z(7)}),
+ {x({"a","b","c"})}, {x({"a","b","c"})},
+ {x({"a","b","c"})}, {x({"a","b"})},
+ {x({"a","b","c"})}, {y({"foo","bar","baz"})},
+ {x({"a","b","c"})}, {x({"a","b","c"}),y({"foo","bar","baz"})},
+ {x({"a","b"}),y({"foo","bar","baz"})}, {x({"a","b","c"}),y({"foo","bar"})},
+ {x({"a","b"}),y({"foo","bar","baz"})}, {y({"foo","bar"}),z({"i","j","k","l"})},
+ float_cells({x({"a","b"}),y({"foo","bar","baz"})}), {y({"foo","bar"}),z({"i","j","k","l"})},
+ {x({"a","b"}),y({"foo","bar","baz"})}, float_cells({y({"foo","bar"}),z({"i","j","k","l"})}),
+ float_cells({x({"a","b"}),y({"foo","bar","baz"})}), float_cells({y({"foo","bar"}),z({"i","j","k","l"})}),
+ {x(3),y({"foo", "bar"})}, {y({"foo", "bar"}),z(7)},
+ {x({"a","b","c"}),y(5)}, {y(5),z({"i","j","k","l"})},
+ float_cells({x({"a","b","c"}),y(5)}), {y(5),z({"i","j","k","l"})},
+ {x({"a","b","c"}),y(5)}, float_cells({y(5),z({"i","j","k","l"})}),
+ float_cells({x({"a","b","c"}),y(5)}), float_cells({y(5),z({"i","j","k","l"})})
+};
+
+bool join_address(const TensorSpec::Address &a, const TensorSpec::Address &b, TensorSpec::Address &addr) {
+ for (const auto &dim_a: a) {
+ auto pos_b = b.find(dim_a.first);
+ if ((pos_b != b.end()) && !(pos_b->second == dim_a.second)) {
+ return false;
+ }
+ addr.insert_or_assign(dim_a.first, dim_a.second);
+ }
+ return true;
+}
+
+TensorSpec reference_join(const TensorSpec &a, const TensorSpec &b, join_fun_t function) {
+ ValueType res_type = ValueType::join(ValueType::from_spec(a.type()), ValueType::from_spec(b.type()));
+ EXPECT_FALSE(res_type.is_error());
+ TensorSpec result(res_type.to_spec());
+ for (const auto &cell_a: a.cells()) {
+ for (const auto &cell_b: b.cells()) {
+ TensorSpec::Address addr;
+ if (join_address(cell_a.first, cell_b.first, addr) &&
+ join_address(cell_b.first, cell_a.first, addr))
+ {
+ result.add(addr, function(cell_a.second, cell_b.second));
+ }
+ }
+ }
+ return result;
+}
+
+TensorSpec perform_generic_join(const TensorSpec &a, const TensorSpec &b, join_fun_t function) {
+ Stash stash;
+ const auto &factory = SimpleValueBuilderFactory::get();
+ auto lhs = value_from_spec(a, factory);
+ auto rhs = value_from_spec(b, factory);
+ auto my_op = GenericJoin::make_instruction(lhs->type(), rhs->type(), function, factory, stash);
+ InterpretedFunction::EvalSingle single(my_op);
+ return spec_from_value(single.eval(std::vector<Value::CREF>({*lhs,*rhs})));
+}
+
+TEST(GenericJoinTest, dense_join_plan_can_be_created) {
+ auto lhs = ValueType::from_spec("tensor(a{},b[6],c[5],e[3],f[2],g{})");
+ auto rhs = ValueType::from_spec("tensor(a{},b[6],c[5],d[4],h{})");
+ auto plan = DenseJoinPlan(lhs, rhs);
+ std::vector<size_t> expect_loop = {30,4,6};
+ std::vector<size_t> expect_lhs_stride = {6,0,1};
+ std::vector<size_t> expect_rhs_stride = {4,1,0};
+ EXPECT_EQ(plan.lhs_size, 180);
+ EXPECT_EQ(plan.rhs_size, 120);
+ EXPECT_EQ(plan.out_size, 720);
+ EXPECT_EQ(plan.loop_cnt, expect_loop);
+ EXPECT_EQ(plan.lhs_stride, expect_lhs_stride);
+ EXPECT_EQ(plan.rhs_stride, expect_rhs_stride);
+}
+
+TEST(GenericJoinTest, sparse_join_plan_can_be_created) {
+ auto lhs = ValueType::from_spec("tensor(a{},b[6],c[5],e[3],f[2],g{})");
+ auto rhs = ValueType::from_spec("tensor(b[6],c[5],d[4],g{},h{})");
+ auto plan = SparseJoinPlan(lhs, rhs);
+ using SRC = SparseJoinPlan::Source;
+ std::vector<SRC> expect_sources = {SRC::LHS,SRC::BOTH,SRC::RHS};
+ std::vector<size_t> expect_lhs_overlap = {1};
+ std::vector<size_t> expect_rhs_overlap = {0};
+ EXPECT_EQ(plan.sources, expect_sources);
+ EXPECT_EQ(plan.lhs_overlap, expect_lhs_overlap);
+ EXPECT_EQ(plan.rhs_overlap, expect_rhs_overlap);
+}
+
+TEST(GenericJoinTest, dense_join_plan_can_be_executed) {
+ auto plan = DenseJoinPlan(ValueType::from_spec("tensor(a[2])"),
+ ValueType::from_spec("tensor(b[3])"));
+ std::vector<int> a({1, 2});
+ std::vector<int> b({3, 4, 5});
+ std::vector<int> c(6, 0);
+ std::vector<int> expect = {3,4,5,6,8,10};
+ ASSERT_EQ(plan.out_size, 6);
+ int *dst = &c[0];
+ auto cell_join = [&](size_t a_idx, size_t b_idx) { *dst++ = (a[a_idx] * b[b_idx]); };
+ plan.execute(0, 0, cell_join);
+ EXPECT_EQ(c, expect);
+}
+
+TEST(GenericJoinTest, generic_join_works_for_simple_values) {
+ ASSERT_TRUE((join_layouts.size() % 2) == 0);
+ for (size_t i = 0; i < join_layouts.size(); i += 2) {
+ TensorSpec lhs = spec(join_layouts[i], Div16(N()));
+ TensorSpec rhs = spec(join_layouts[i + 1], Div16(N()));
+ for (auto fun: {operation::Add::f, operation::Sub::f, operation::Mul::f, operation::Div::f}) {
+ SCOPED_TRACE(fmt("\n===\nLHS: %s\nRHS: %s\n===\n", lhs.to_string().c_str(), rhs.to_string().c_str()));
+ auto expect = reference_join(lhs, rhs, fun);
+ auto actual = perform_generic_join(lhs, rhs, fun);
+ EXPECT_EQ(actual, expect);
+ }
+ }
+}
+
+GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/eval/src/tests/instruction/generic_rename/CMakeLists.txt b/eval/src/tests/instruction/generic_rename/CMakeLists.txt
new file mode 100644
index 00000000000..98af0fe0212
--- /dev/null
+++ b/eval/src/tests/instruction/generic_rename/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(eval_generic_rename_test_app TEST
+ SOURCES
+ generic_rename_test.cpp
+ DEPENDS
+ vespaeval
+ GTest::GTest
+)
+vespa_add_test(NAME eval_generic_rename_test_app COMMAND eval_generic_rename_test_app)
diff --git a/eval/src/tests/instruction/generic_rename/generic_rename_test.cpp b/eval/src/tests/instruction/generic_rename/generic_rename_test.cpp
new file mode 100644
index 00000000000..f61899e4dda
--- /dev/null
+++ b/eval/src/tests/instruction/generic_rename/generic_rename_test.cpp
@@ -0,0 +1,145 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/eval/eval/simple_value.h>
+#include <vespa/eval/eval/value_codec.h>
+#include <vespa/eval/instruction/generic_rename.h>
+#include <vespa/eval/eval/interpreted_function.h>
+#include <vespa/eval/eval/test/tensor_model.hpp>
+#include <vespa/vespalib/util/stringfmt.h>
+#include <vespa/vespalib/gtest/gtest.h>
+
+using namespace vespalib;
+using namespace vespalib::eval;
+using namespace vespalib::eval::instruction;
+using namespace vespalib::eval::test;
+
+using vespalib::make_string_short::fmt;
+
+std::vector<Layout> rename_layouts = {
+ {x(3)},
+ {x(3),y(5)},
+ {x(3),y(5),z(7)},
+ float_cells({x(3),y(5),z(7)}),
+ {x({"a","b","c"})},
+ {x({"a","b","c"}),y({"foo","bar"})},
+ {x({"a","b","c"}),y({"foo","bar"}),z({"i","j","k","l"})},
+ float_cells({x({"a","b","c"}),y({"foo","bar"}),z({"i","j","k","l"})}),
+ {x(3),y({"foo", "bar"}),z(7)},
+ {x({"a","b","c"}),y(5),z({"i","j","k","l"})},
+ float_cells({x({"a","b","c"}),y(5),z({"i","j","k","l"})})
+};
+
+struct FromTo {
+ std::vector<vespalib::string> from;
+ std::vector<vespalib::string> to;
+};
+
+std::vector<FromTo> rename_from_to = {
+ { {"x"}, {"x_renamed"} },
+ { {"x"}, {"z_was_x"} },
+ { {"x", "y"}, {"y", "x"} },
+ { {"x", "z"}, {"z", "x"} },
+ { {"x", "y", "z"}, {"a", "b", "c"} },
+ { {"z"}, {"a"} },
+ { {"y"}, {"z_was_y"} },
+ { {"y"}, {"b"} }
+};
+
+
+TEST(GenericRenameTest, dense_rename_plan_can_be_created_and_executed) {
+ auto lhs = ValueType::from_spec("tensor(a[2],c[3],d{},e[5],g[7],h{})");
+ std::vector<vespalib::string> from({"a", "c", "e"});
+ std::vector<vespalib::string> to({"f", "a", "b"});
+ ValueType renamed = lhs.rename(from, to);
+ auto plan = DenseRenamePlan(lhs, renamed, from, to);
+ std::vector<size_t> expect_loop = {15,2,7};
+ std::vector<size_t> expect_stride = {7,105,1};
+ EXPECT_EQ(plan.subspace_size, 210);
+ EXPECT_EQ(plan.loop_cnt, expect_loop);
+ EXPECT_EQ(plan.stride, expect_stride);
+ std::vector<int> out;
+ int want[3][5][2][7];
+ size_t counter = 0;
+ for (size_t a = 0; a < 2; ++a) {
+ for (size_t c = 0; c < 3; ++c) {
+ for (size_t e = 0; e < 5; ++e) {
+ for (size_t g = 0; g < 7; ++g) {
+ want[c][e][a][g] = counter++;
+ }
+ }
+ }
+ }
+ std::vector<int> expect(210);
+ memcpy(&expect[0], &want[0], 210*sizeof(int));
+ auto move_cell = [&](size_t offset) { out.push_back(offset); };
+ plan.execute(0, move_cell);
+ EXPECT_EQ(out, expect);
+}
+
+TEST(GenericRenameTest, sparse_rename_plan_can_be_created) {
+ auto lhs = ValueType::from_spec("tensor(a{},c{},d[3],e{},g{},h[5])");
+ std::vector<vespalib::string> from({"a", "c", "e"});
+ std::vector<vespalib::string> to({"f", "a", "b"});
+ ValueType renamed = lhs.rename(from, to);
+ auto plan = SparseRenamePlan(lhs, renamed, from, to);
+ EXPECT_EQ(plan.mapped_dims, 4);
+ std::vector<size_t> expect = {2,0,1,3};
+ EXPECT_EQ(plan.output_dimensions, expect);
+}
+
+vespalib::string rename_dimension(const vespalib::string &name, const FromTo &ft) {
+ assert(ft.from.size() == ft.to.size());
+ for (size_t i = 0; i < ft.from.size(); ++i) {
+ if (name == ft.from[i]) {
+ return ft.to[i];
+ }
+ }
+ return name;
+}
+
+TensorSpec reference_rename(const TensorSpec &a, const FromTo &ft) {
+ ValueType res_type = ValueType::from_spec(a.type()).rename(ft.from, ft.to);
+ EXPECT_FALSE(res_type.is_error());
+ TensorSpec result(res_type.to_spec());
+ for (const auto &cell: a.cells()) {
+ TensorSpec::Address addr;
+ for (const auto &dim: cell.first) {
+ addr.insert_or_assign(rename_dimension(dim.first, ft), dim.second);
+ }
+ result.add(addr, cell.second);
+ }
+ return result;
+}
+
+TensorSpec perform_generic_rename(const TensorSpec &a, const ValueType &res_type,
+ const FromTo &ft, const ValueBuilderFactory &factory)
+{
+ Stash stash;
+ auto lhs = value_from_spec(a, factory);
+ auto my_op = GenericRename::make_instruction(lhs->type(), res_type, ft.from, ft.to, factory, stash);
+ InterpretedFunction::EvalSingle single(my_op);
+ return spec_from_value(single.eval(std::vector<Value::CREF>({*lhs})));
+}
+
+void test_generic_rename(const ValueBuilderFactory &factory) {
+ for (const auto & layout : rename_layouts) {
+ TensorSpec lhs = spec(layout, N());
+ ValueType lhs_type = ValueType::from_spec(lhs.type());
+ // printf("lhs_type: %s\n", lhs_type.to_spec().c_str());
+ for (const auto & from_to : rename_from_to) {
+ ValueType renamed_type = lhs_type.rename(from_to.from, from_to.to);
+ if (renamed_type.is_error()) continue;
+ // printf("type %s -> %s\n", lhs_type.to_spec().c_str(), renamed_type.to_spec().c_str());
+ SCOPED_TRACE(fmt("\n===\nLHS: %s\n===\n", lhs.to_string().c_str()));
+ auto expect = reference_rename(lhs, from_to);
+ auto actual = perform_generic_rename(lhs, renamed_type, from_to, factory);
+ EXPECT_EQ(actual, expect);
+ }
+ }
+}
+
+TEST(GenericRenameTest, generic_rename_works_for_simple_values) {
+ test_generic_rename(SimpleValueBuilderFactory::get());
+}
+
+GTEST_MAIN_RUN_ALL_TESTS()