summaryrefslogtreecommitdiffstats
path: root/eval
diff options
context:
space:
mode:
authorArne Juul <arnej@verizonmedia.com>2020-10-05 08:27:12 +0000
committerArne Juul <arnej@verizonmedia.com>2020-10-05 13:57:20 +0000
commit8cc6d2d46c0196975e5340db5aaacccc89d44ab2 (patch)
treedbd94b061c1071dd59603c7ee949a78dd6a91ec1 /eval
parentd7a7d903dc80ad63f77314a73ee718dad6b68e7d (diff)
add reference implementation of concat, with test
Diffstat (limited to 'eval')
-rw-r--r--eval/CMakeLists.txt1
-rw-r--r--eval/src/tests/instruction/generic_concat/CMakeLists.txt9
-rw-r--r--eval/src/tests/instruction/generic_concat/generic_concat_test.cpp123
-rw-r--r--eval/src/vespa/eval/eval/tensor_spec.cpp7
-rw-r--r--eval/src/vespa/eval/eval/tensor_spec.h1
5 files changed, 141 insertions, 0 deletions
diff --git a/eval/CMakeLists.txt b/eval/CMakeLists.txt
index 485d68b4ae3..18c3676c366 100644
--- a/eval/CMakeLists.txt
+++ b/eval/CMakeLists.txt
@@ -34,6 +34,7 @@ vespa_define_module(
src/tests/eval/value_codec
src/tests/eval/value_type
src/tests/gp/ponder_nov2017
+ src/tests/instruction/generic_concat
src/tests/instruction/generic_join
src/tests/instruction/generic_merge
src/tests/instruction/generic_reduce
diff --git a/eval/src/tests/instruction/generic_concat/CMakeLists.txt b/eval/src/tests/instruction/generic_concat/CMakeLists.txt
new file mode 100644
index 00000000000..ddc6d4f5944
--- /dev/null
+++ b/eval/src/tests/instruction/generic_concat/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_concat_test_app TEST
+ SOURCES
+ generic_concat_test.cpp
+ DEPENDS
+ vespaeval
+ GTest::GTest
+)
+vespa_add_test(NAME eval_generic_concat_test_app COMMAND eval_generic_concat_test_app)
diff --git a/eval/src/tests/instruction/generic_concat/generic_concat_test.cpp b/eval/src/tests/instruction/generic_concat/generic_concat_test.cpp
new file mode 100644
index 00000000000..b4822e2115e
--- /dev/null
+++ b/eval/src/tests/instruction/generic_concat/generic_concat_test.cpp
@@ -0,0 +1,123 @@
+// 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/eval/simple_tensor.h>
+#include <vespa/eval/eval/simple_tensor_engine.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> concat_layouts = {
+ {}, {},
+ {}, {x(5)},
+ {x(5)}, {},
+ {x(2)}, {x(3)},
+ {x(2)}, {y(3)},
+ {y(2)}, {z(3)},
+ {x(5)}, {x(2),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)}),
+ {y({"a","b","c"})}, {y({"a","b","c"})},
+ {y({"a","b","c"})}, {y({"a","b"})},
+ {y({"a","b","c"})}, {y({"b","c","d"})},
+ float_cells({y({"a","b","c"})}), {y({"b","c","d"})},
+ {y({"a","b","c"})}, float_cells({y({"b","c","d"})}),
+ float_cells({y({"a","b","c"})}), float_cells({z({"foo","bar","baz"})}),
+ {y({"a","b","c"})}, {y({"a","b","c"}),z({"foo","bar","baz"})},
+ {y({"a","b"}),z({"foo","bar","baz"})}, {y({"a","b","c"}),z({"foo","bar"})},
+ {x(2),y({"a","b","c"})}, {x(3),y({"b","c","d"})},
+ {x(2),y({"a","b"})}, {x(3),z({"c","d"})}
+};
+
+TensorSpec perform_simpletensor_concat(const TensorSpec &a, const TensorSpec &b, const std::string &dimension) {
+ auto lhs = SimpleTensor::create(a);
+ auto rhs = SimpleTensor::create(b);
+ auto out = SimpleTensor::concat(*lhs, *rhs, dimension);
+ return SimpleTensorEngine::ref().to_spec(*out);
+}
+
+bool concat_address(const TensorSpec::Address &me, const TensorSpec::Address &other,
+ const std::string &concat_dim, size_t my_offset,
+ TensorSpec::Address &my_out, TensorSpec::Address &other_out)
+{
+ my_out.insert_or_assign(concat_dim, my_offset);
+ for (const auto &my_dim: me) {
+ const auto & name = my_dim.first;
+ const auto & label = my_dim.second;
+ if (name == concat_dim) {
+ my_out.insert_or_assign(name, label.index + my_offset);
+ } else {
+ auto pos = other.find(name);
+ if ((pos == other.end()) || (pos->second == label)) {
+ my_out.insert_or_assign(name, label);
+ other_out.insert_or_assign(name, label);
+ } else {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool concat_addresses(const TensorSpec::Address &a, const TensorSpec::Address &b,
+ const std::string &concat_dim, size_t b_offset,
+ TensorSpec::Address &a_out, TensorSpec::Address &b_out)
+{
+ return concat_address(a, b, concat_dim, 0, a_out, b_out) &&
+ concat_address(b, a, concat_dim, b_offset, b_out, a_out);
+}
+
+TensorSpec reference_concat(const TensorSpec &a, const TensorSpec &b, const std::string &concat_dim) {
+ ValueType a_type = ValueType::from_spec(a.type());
+ ValueType b_type = ValueType::from_spec(b.type());
+ size_t cc_dim_a_size = 1;
+ for (const auto & dim : a_type.dimensions()) {
+ if (dim.name == concat_dim) {
+ EXPECT_TRUE(dim.is_indexed());
+ cc_dim_a_size = dim.size;
+ }
+ }
+ ValueType res_type = ValueType::concat(a_type, b_type, concat_dim);
+ 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_a;
+ TensorSpec::Address addr_b;
+ if (concat_addresses(cell_a.first, cell_b.first, concat_dim, cc_dim_a_size, addr_a, addr_b)) {
+ result.set(addr_a, cell_a.second);
+ result.set(addr_b, cell_b.second);
+ }
+ }
+ }
+ return result;
+}
+
+TEST(GenericConcatTest, generic_reference_concat_works) {
+ ASSERT_TRUE((concat_layouts.size() % 2) == 0);
+ for (size_t i = 0; i < concat_layouts.size(); i += 2) {
+ const TensorSpec lhs = spec(concat_layouts[i], N());
+ const TensorSpec rhs = spec(concat_layouts[i + 1], Div16(N()));
+ SCOPED_TRACE(fmt("\n===\nin LHS: %s\nin RHS: %s\n===\n", lhs.to_string().c_str(), rhs.to_string().c_str()));
+ auto expect = reference_concat(lhs, rhs, "x");
+ auto actual = perform_simpletensor_concat(lhs, rhs, "x");
+ // auto actual = perform_generic_concat(lhs, rhs, "x");
+ EXPECT_EQ(actual, expect);
+ }
+}
+
+GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/eval/src/vespa/eval/eval/tensor_spec.cpp b/eval/src/vespa/eval/eval/tensor_spec.cpp
index 712ee282c71..d3aafc7632a 100644
--- a/eval/src/vespa/eval/eval/tensor_spec.cpp
+++ b/eval/src/vespa/eval/eval/tensor_spec.cpp
@@ -45,6 +45,13 @@ TensorSpec & TensorSpec::operator = (const TensorSpec &) = default;
TensorSpec::~TensorSpec() { }
+TensorSpec &
+TensorSpec::set(Address address, double value) {
+ auto res = _cells.emplace(std::move(address), value);
+ if (!res.second) { assert(res.first->second.value == value); }
+ return *this;
+}
+
vespalib::string
TensorSpec::to_string() const
{
diff --git a/eval/src/vespa/eval/eval/tensor_spec.h b/eval/src/vespa/eval/eval/tensor_spec.h
index 974ad4a1f4c..8a4343b3faa 100644
--- a/eval/src/vespa/eval/eval/tensor_spec.h
+++ b/eval/src/vespa/eval/eval/tensor_spec.h
@@ -68,6 +68,7 @@ public:
TensorSpec(const TensorSpec &);
TensorSpec & operator = (const TensorSpec &);
~TensorSpec();
+ TensorSpec &set(Address address, double value);
TensorSpec &add(Address address, double value) {
auto res = _cells.emplace(std::move(address), value);
if (!res.second) {