diff options
author | Arne Juul <arnej@verizonmedia.com> | 2020-11-26 10:17:58 +0000 |
---|---|---|
committer | Arne Juul <arnej@verizonmedia.com> | 2020-11-26 13:35:25 +0000 |
commit | c78c2dcf8354dcbbc1664cc81a73e68474705fe5 (patch) | |
tree | 6be27a0a805a5c64225591afa8c1a17ed04052eb | |
parent | de5a6ac9951c083b8d5cb5557e7a0799ebbb79f7 (diff) |
test ReferenceOperations implementations
-rw-r--r-- | eval/CMakeLists.txt | 1 | ||||
-rw-r--r-- | eval/src/tests/eval/reference_operations/CMakeLists.txt | 10 | ||||
-rw-r--r-- | eval/src/tests/eval/reference_operations/reference_operations_test.cpp | 439 |
3 files changed, 450 insertions, 0 deletions
diff --git a/eval/CMakeLists.txt b/eval/CMakeLists.txt index ee8509fcf19..3d7cbf03071 100644 --- a/eval/CMakeLists.txt +++ b/eval/CMakeLists.txt @@ -28,6 +28,7 @@ vespa_define_module( src/tests/eval/node_tools src/tests/eval/node_types src/tests/eval/param_usage + src/tests/eval/reference_operations src/tests/eval/simple_tensor src/tests/eval/simple_value src/tests/eval/tensor_function diff --git a/eval/src/tests/eval/reference_operations/CMakeLists.txt b/eval/src/tests/eval/reference_operations/CMakeLists.txt new file mode 100644 index 00000000000..46838f6fc82 --- /dev/null +++ b/eval/src/tests/eval/reference_operations/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +vespa_add_executable(eval_reference_operations_test_app TEST + SOURCES + reference_operations_test.cpp + DEPENDS + vespaeval + GTest::GTest +) +vespa_add_test(NAME eval_reference_operations_test_app COMMAND eval_reference_operations_test_app) diff --git a/eval/src/tests/eval/reference_operations/reference_operations_test.cpp b/eval/src/tests/eval/reference_operations/reference_operations_test.cpp new file mode 100644 index 00000000000..ad777e382bc --- /dev/null +++ b/eval/src/tests/eval/reference_operations/reference_operations_test.cpp @@ -0,0 +1,439 @@ +// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/eval/eval/test/reference_operations.h> +#include <vespa/eval/eval/test/tensor_model.hpp> +#include <vespa/vespalib/gtest/gtest.h> +#include <vespa/vespalib/util/stringfmt.h> +#include <iostream> + +using vespalib::make_string_short::fmt; + +using namespace vespalib; +using namespace vespalib::eval; +using namespace vespalib::eval::test; + +TensorSpec dense_2d_some_cells(bool float_cells, bool square) { + return TensorSpec(fmt("tensor%s(a[3],d[5])", float_cells ? "<float>" : "")) + .add({{"a", 1}, {"d", 2}}, square ? 9.0 : 3.0) + .add({{"a", 2}, {"d", 4}}, square ? 16.0 : 4.0) + .add({{"a", 1}, {"d", 0}}, square ? 25.0 : 5.0); +} + +TensorSpec sparse_2d_some_cells(bool float_cells, bool square) { + return TensorSpec(fmt("tensor%s(c{},e{})", float_cells ? "<float>" : "")) + .add({{"c", "foo"}, {"e", "foo"}}, square ? 1.0 : 1.0) + .add({{"c", "foo"}, {"e", "bar"}}, square ? 4.0 : 2.0) + .add({{"c", "bar"}, {"e", "bar"}}, square ? 9.0 : 3.0) + .add({{"c", "qux"}, {"e", "foo"}}, square ? 16.0 : 4.0) + .add({{"c", "qux"}, {"e", "qux"}}, square ? 25.0 : 5.0); +} + +TensorSpec mixed_5d_some_cells(bool float_cells, bool square) { + return TensorSpec(fmt("tensor%s(a[3],b[1],c{},d[5],e{})", float_cells ? "<float>" : "")) + .add({{"a", 1}, {"b", 0}, {"c", "foo"}, {"d", 2}, {"e", "bar"}}, square ? 4.0 : 2.0) + .add({{"a", 2}, {"b", 0}, {"c", "bar"}, {"d", 3}, {"e", "bar"}}, square ? 9.0 : 3.0) + .add({{"a", 0}, {"b", 0}, {"c", "foo"}, {"d", 4}, {"e", "foo"}}, square ? 16.0 : 4.0) + .add({{"a", 1}, {"b", 0}, {"c", "bar"}, {"d", 0}, {"e", "qux"}}, square ? 25.0 : 5.0) + .add({{"a", 2}, {"b", 0}, {"c", "qux"}, {"d", 1}, {"e", "foo"}}, square ? 36.0 : 6.0); +} + +TensorSpec dense_1d_all_two(bool float_cells) { + return TensorSpec(fmt("tensor%s(a[3])", float_cells ? "<float>" : "")) + .add({{"a", 0}}, 2.0) + .add({{"a", 1}}, 2.0) + .add({{"a", 2}}, 2.0); +} + +TensorSpec sparse_1d_all_two(bool float_cells) { + return TensorSpec(fmt("tensor%s(c{})", float_cells ? "<float>" : "")) + .add({{"c", "foo"}}, 2.0) + .add({{"c", "bar"}}, 2.0) + .add({{"c", "qux"}}, 2.0); +} + +//----------------------------------------------------------------------------- + + +TEST(ReferenceOperationsTest, concat_gives_expected_results) { + auto a = TensorSpec("double").add({}, 7.0); + auto b = TensorSpec("double").add({}, 4.0); + auto output = ReferenceOperations::concat(a, b, "x"); + auto expect = TensorSpec("tensor(x[2])") + .add({{"x", 0}}, 7.0) + .add({{"x", 1}}, 4.0); + EXPECT_EQ(output, expect); + a = TensorSpec("tensor<float>(a[3])") + .add({{"a", 0}}, 1.0) + .add({{"a", 1}}, 2.0) + .add({{"a", 2}}, 3.0); + output = ReferenceOperations::concat(a, b, "a"); + expect = TensorSpec("tensor<float>(a[4])") + .add({{"a", 0}}, 1.0) + .add({{"a", 1}}, 2.0) + .add({{"a", 2}}, 3.0) + .add({{"a", 3}}, 4.0); + EXPECT_EQ(output, expect); + output = ReferenceOperations::concat(b, a, "a"); + expect = TensorSpec("tensor<float>(a[4])") + .add({{"a", 0}}, 4.0) + .add({{"a", 1}}, 1.0) + .add({{"a", 2}}, 2.0) + .add({{"a", 3}}, 3.0); + EXPECT_EQ(output, expect); + bool use_float = false; + a = mixed_5d_some_cells(use_float, false); + b = mixed_5d_some_cells(use_float, true); + output = ReferenceOperations::concat(a, b, "a"); + EXPECT_EQ(output.type(), "tensor(a[6],b[1],c{},d[5],e{})"); + output = ReferenceOperations::concat(a, b, "b"); + EXPECT_EQ(output.type(), "tensor(a[3],b[2],c{},d[5],e{})"); + output = ReferenceOperations::concat(a, b, "x"); + EXPECT_EQ(output.type(), "tensor(a[3],b[1],c{},d[5],e{},x[2])"); + output = ReferenceOperations::concat(a, b, "c"); + EXPECT_EQ(output.type(), "error"); +} + +TEST(ReferenceOperationsTest, create_gives_expected_results) { + auto a = TensorSpec("double").add({}, 1.5); + auto b = TensorSpec("tensor(z[2])").add({{"z",0}}, 2.0).add({{"z",1}}, 3.0); + auto c = TensorSpec("tensor()").add({{}}, 4.0); + ReferenceOperations::CreateSpec spec; + spec.emplace(TensorSpec::Address{{"x",1},{"y","foo"}}, 0); + spec.emplace(TensorSpec::Address{{"x",0},{"y","bar"}}, 1); + spec.emplace(TensorSpec::Address{{"x",1},{"y","bar"}}, 2); + auto output = ReferenceOperations::create("tensor(x[2],y{})", spec, {a,b,c}); + auto expect = TensorSpec("tensor(x[2],y{})") + .add({{"x",1},{"y","foo"}}, 1.5) + .add({{"x",0},{"y","bar"}}, 5.0) + .add({{"x",1},{"y","bar"}}, 4.0); + EXPECT_EQ(output, expect); +} + +TEST(ReferenceOperationsTest, join_gives_expected_results) { + auto a = TensorSpec("tensor()").add({}, 7.0); + auto b = TensorSpec("tensor()").add({}, 4.0); + auto output = ReferenceOperations::join(a, b, operation::Sub::f); + EXPECT_EQ(output, TensorSpec("double").add({}, 3.0)); + for (bool use_float : {false, true}) { + const auto expect_sq = mixed_5d_some_cells(use_float, true); + a = mixed_5d_some_cells(use_float, false); + b = TensorSpec("double").add({}, 2.0); + output = ReferenceOperations::join(a, b, operation::Pow::f); + EXPECT_EQ(output, expect_sq); + output = ReferenceOperations::join(a, a, operation::Mul::f); + EXPECT_EQ(output, expect_sq); + auto c = ReferenceOperations::join(output, a, operation::Div::f); + EXPECT_EQ(c, a); + b = dense_1d_all_two(use_float); + output = ReferenceOperations::join(a, b, operation::Pow::f); + EXPECT_EQ(output, expect_sq); + b = sparse_1d_all_two(use_float); + output = ReferenceOperations::join(a, b, operation::Pow::f); + EXPECT_EQ(output, expect_sq); + } +} + +TEST(ReferenceOperationsTest, map_gives_expected_results) { + auto input = TensorSpec("tensor()").add({}, 0.0); + auto output = ReferenceOperations::map(input, operation::Exp::f); + EXPECT_EQ(output, TensorSpec("double").add({}, 1.0)); + auto out2 = ReferenceOperations::map(output, operation::Neg::f); + EXPECT_EQ(out2, TensorSpec("double").add({}, -1.0)); + + for (bool use_float : {false, true}) { + input = dense_2d_some_cells(use_float, false); + output = ReferenceOperations::map(input, operation::Square::f); + EXPECT_EQ(output, dense_2d_some_cells(use_float, true)); + + input = sparse_2d_some_cells(use_float, false); + output = ReferenceOperations::map(input, operation::Square::f); + EXPECT_EQ(output, sparse_2d_some_cells(use_float, true)); + + input = mixed_5d_some_cells(use_float, false); + output = ReferenceOperations::map(input, operation::Square::f); + EXPECT_EQ(output, mixed_5d_some_cells(use_float, true)); + } +} + + +TEST(ReferenceOperationsTest, merge_gives_expected_results) { + auto a = mixed_5d_some_cells(false, false); + auto b = TensorSpec("tensor(a[3],b[1],c{},d[5],e{})") + .add({{"a", 0}, {"b", 0}, {"c", "foo"}, {"d", 4}, {"e", "foo"}}, 0.0) + .add({{"a", 1}, {"b", 0}, {"c", "bar"}, {"d", 0}, {"e", "qux"}}, 42.0) + .add({{"a", 0}, {"b", 0}, {"c", "new"}, {"d", 0}, {"e", "new"}}, 1.0); + auto output = ReferenceOperations::merge(a, b, operation::Max::f); + auto expect = TensorSpec("tensor(a[3],b[1],c{},d[5],e{})") + .add({{"a", 1}, {"b", 0}, {"c", "foo"}, {"d", 2}, {"e", "bar"}}, 2.0) + .add({{"a", 2}, {"b", 0}, {"c", "bar"}, {"d", 3}, {"e", "bar"}}, 3.0) + .add({{"a", 0}, {"b", 0}, {"c", "foo"}, {"d", 4}, {"e", "foo"}}, 4.0) + .add({{"a", 1}, {"b", 0}, {"c", "bar"}, {"d", 0}, {"e", "qux"}}, 42.0) + .add({{"a", 2}, {"b", 0}, {"c", "qux"}, {"d", 1}, {"e", "foo"}}, 6.0) + .add({{"a", 0}, {"b", 0}, {"c", "new"}, {"d", 0}, {"e", "new"}}, 1.0); + EXPECT_EQ(output, expect); +} + +//----------------------------------------------------------------------------- + +TEST(ReferenceOperationsTest, peek_verbatim_labels) { + auto input = sparse_2d_some_cells(true, true); + ReferenceOperations::PeekSpec spec; + spec.emplace("c", "qux"); + // peek 1 mapped dimension, verbatim label + auto output = ReferenceOperations::peek(input, spec, {}); + auto expect = TensorSpec("tensor<float>(e{})") + .add({{"e","foo"}}, 16.0) + .add({{"e","qux"}}, 25.0); + EXPECT_EQ(output, expect); + spec.emplace("e", "foo"); + // peek all mapped dimensions, verbatim labels + output = ReferenceOperations::peek(input, spec, {}); + expect = TensorSpec("double").add({}, 16.0); + EXPECT_EQ(output, expect); + + spec.clear(); + spec.emplace("c", "nomatch"); + // peek 1 mapped dimension, non-matching verbatim label + output = ReferenceOperations::peek(input, spec, {}); + expect = TensorSpec("tensor<float>(e{})"); + EXPECT_EQ(output, expect); + spec.emplace("e", "nomatch"); + // peek all mapped dimensions, non-matching verbatim labels + output = ReferenceOperations::peek(input, spec, {}); + expect = TensorSpec("double"); + EXPECT_EQ(output, expect); + + input = dense_2d_some_cells(true, false); + spec.clear(); + spec.emplace("a", TensorSpec::Label(1)); + // peek 1 indexed dimension, verbatim label + output = ReferenceOperations::peek(input, spec, {}); + expect = TensorSpec("tensor<float>(d[5])") + .add({{"d", 2}}, 3.0) + .add({{"d", 0}}, 5.0); + EXPECT_EQ(output, expect); + spec.emplace("d", TensorSpec::Label(2)); + // peek all indexed dimensions, verbatim labels + output = ReferenceOperations::peek(input, spec, {}); + expect = TensorSpec("double").add({}, 3.0); + EXPECT_EQ(output, expect); +} + +TEST(ReferenceOperationsTest, peek_labels_from_children) { + auto pos_ch = TensorSpec("double").add({}, 1.0); + auto zero_ch = TensorSpec("double").add({}, 0.0); + auto neg_ch = TensorSpec("double").add({}, -2.0); + auto too_big_ch = TensorSpec("double").add({}, 42.0); + std::vector<TensorSpec> children = {too_big_ch, too_big_ch, zero_ch, pos_ch, neg_ch, too_big_ch}; + + auto input = dense_2d_some_cells(false, false); + ReferenceOperations::PeekSpec spec; + spec.emplace("a", size_t(3)); + // peek 1 indexed dimension, child (evaluating to 1.0) + auto output = ReferenceOperations::peek(input, spec, children); + auto expect = TensorSpec("tensor(d[5])") + .add({{"d", 2}}, 3.0) + .add({{"d", 0}}, 5.0); + EXPECT_EQ(output, expect); + spec.emplace("d", size_t(2)); + // peek 2 indexed dimensions (both children) + output = ReferenceOperations::peek(input, spec, children); + expect = TensorSpec("double").add({}, 5.0); + EXPECT_EQ(output, expect); + spec.clear(); + spec.emplace("a", size_t(0)); + // peek 1 indexed dimension, child (evaluating to 42.0) + output = ReferenceOperations::peek(input, spec, children); + expect = TensorSpec("tensor(d[5])"); + EXPECT_EQ(output, expect); + spec.clear(); + spec.emplace("a", size_t(4)); + // peek 1 indexed dimension, child (evaluating to -2.0) + output = ReferenceOperations::peek(input, spec, children); + expect = TensorSpec("tensor(d[5])"); + EXPECT_EQ(output, expect); + + input = TensorSpec("tensor(c{},e{})") + .add({{"c", "0"}, {"e", "0"}}, 2.0) + .add({{"c", "1"}, {"e", "1"}}, 3.0) + .add({{"c", "1"}, {"e", "0"}}, 4.0) + .add({{"c", "-2"}, {"e", "1"}}, 5.0) + .add({{"c", "-2"}, {"e", "-2"}}, 6.0); + spec.clear(); + spec.emplace("c", size_t(3)); + // peek 1 mapped dimension, child (evaluating to 1.0) + output = ReferenceOperations::peek(input, spec, children); + expect = TensorSpec("tensor(e{})") + .add({{"e", "1"}}, 3.0) + .add({{"e", "0"}}, 4.0); + EXPECT_EQ(output, expect); + spec.emplace("e", size_t(2)); + // peek 2 mapped dimensions (both children) + output = ReferenceOperations::peek(input, spec, children); + expect = TensorSpec("double").add({}, 4.0); + EXPECT_EQ(output, expect); + + spec.clear(); + spec.emplace("c", size_t(4)); + // peek 1 mapped dimension, child (evaluating to -2.0) + output = ReferenceOperations::peek(input, spec, children); + expect = TensorSpec("tensor(e{})") + .add({{"e", "1"}}, 5.0) + .add({{"e", "-2"}}, 6.0); + EXPECT_EQ(output, expect); + + spec.clear(); + spec.emplace("c", size_t(0)); + // peek 1 indexed dimension, child (evaluating to 42.0) + output = ReferenceOperations::peek(input, spec, children); + expect = TensorSpec("tensor(e{})"); + EXPECT_EQ(output, expect); +} + +TEST(ReferenceOperationsTest, peek_mixed) { + auto pos_ch = TensorSpec("double").add({}, 1.0); + auto zero_ch = TensorSpec("double").add({}, 0.0); + auto neg_ch = TensorSpec("double").add({}, -2.0); + auto too_big_ch = TensorSpec("double").add({}, 42.0); + std::vector<TensorSpec> children = {too_big_ch, too_big_ch, zero_ch, pos_ch, neg_ch, too_big_ch}; + auto input = TensorSpec("tensor<float>(a[3],b[1],c{},d[5],e{})") + .add({{"a", 0}, {"b", 0}, {"c", "-2"}, {"d", 1}, {"e", "foo"}}, 1.0) + .add({{"a", 0}, {"b", 0}, {"c", "1"}, {"d", 4}, {"e", "foo"}}, 2.0) + .add({{"a", 1}, {"b", 0}, {"c", "-1"}, {"d", 4}, {"e", "foo"}}, 3.0) + .add({{"a", 1}, {"b", 0}, {"c", "-2"}, {"d", 0}, {"e", "qux"}}, 4.0) + .add({{"a", 1}, {"b", 0}, {"c", "-2"}, {"d", 1}, {"e", "bar"}}, 5.0) + .add({{"a", 1}, {"b", 0}, {"c", "-2"}, {"d", 1}, {"e", "foo"}}, 6.0) // + .add({{"a", 1}, {"b", 0}, {"c", "-2"}, {"d", 2}, {"e", "bar"}}, 7.0) + .add({{"a", 1}, {"b", 0}, {"c", "-2"}, {"d", 2}, {"e", "foo"}}, 8.0) // + .add({{"a", 1}, {"b", 0}, {"c", "-2"}, {"d", 2}, {"e", "qux"}}, 9.0) + .add({{"a", 1}, {"b", 0}, {"c", "-2"}, {"d", 3}, {"e", "foo"}}, 10.0) // + .add({{"a", 1}, {"b", 0}, {"c", "-2"}, {"d", 0}, {"e", "foo"}}, 11.0) // + .add({{"a", 1}, {"b", 0}, {"c", "-2"}, {"d", 3}, {"e", "nop"}}, 12.0) + .add({{"a", 1}, {"b", 0}, {"c", "-2"}, {"d", 4}, {"e", "bar"}}, 13.0) + .add({{"a", 1}, {"b", 0}, {"c", "-2"}, {"d", 4}, {"e", "foo"}}, 14.0) // + .add({{"a", 1}, {"b", 0}, {"c", "0"}, {"d", 1}, {"e", "foo"}}, 15.0) + .add({{"a", 1}, {"b", 0}, {"c", "1"}, {"d", 2}, {"e", "foo"}}, 16.0) + .add({{"a", 1}, {"b", 0}, {"c", "2"}, {"d", 3}, {"e", "foo"}}, 17.0) + .add({{"a", 2}, {"b", 0}, {"c", "-2"}, {"d", 2}, {"e", "foo"}}, 18.0) + .add({{"a", 2}, {"b", 0}, {"c", "0"}, {"d", 3}, {"e", "bar"}}, 19.0) + .add({{"a", 2}, {"b", 0}, {"c", "1"}, {"d", 1}, {"e", "foo"}}, 20.0); + ReferenceOperations::PeekSpec spec; + spec.emplace("a", size_t(3)); + spec.emplace("b", size_t(2)); + spec.emplace("c", size_t(4)); + spec.emplace("e", "foo"); + auto output = ReferenceOperations::peek(input, spec, children); + auto expect = TensorSpec("tensor<float>(d[5])") + .add({{"d", 1}}, 6.0) + .add({{"d", 2}}, 8.0) + .add({{"d", 3}}, 10.0) + .add({{"d", 0}}, 11.0) + .add({{"d", 4}}, 14.0); + EXPECT_EQ(output, expect); +} + +//----------------------------------------------------------------------------- + +TEST(ReferenceOperationsTest, reduce_gives_expected_results) { + auto input = TensorSpec("tensor<float>(a[3],b[1],c{},d[5],e{})") + .add({{"a", 0}, {"b", 0}, {"c", "bar"}, {"d", 1}, {"e", "foo"}}, 5.0) + .add({{"a", 0}, {"b", 0}, {"c", "bar"}, {"d", 4}, {"e", "foo"}}, 3.0) + .add({{"a", 0}, {"b", 0}, {"c", "foo"}, {"d", 1}, {"e", "foo"}}, 4.0) + .add({{"a", 0}, {"b", 0}, {"c", "foo"}, {"d", 2}, {"e", "foo"}}, 6.0) + .add({{"a", 0}, {"b", 0}, {"c", "foo"}, {"d", 4}, {"e", "foo"}}, 2.0) + .add({{"a", 1}, {"b", 0}, {"c", "bar"}, {"d", 0}, {"e", "qux"}}, 7.0) + .add({{"a", 1}, {"b", 0}, {"c", "bar"}, {"d", 2}, {"e", "qux"}}, 9.0) + .add({{"a", 1}, {"b", 0}, {"c", "foo"}, {"d", 1}, {"e", "qux"}}, 8.0) + .add({{"a", 1}, {"b", 0}, {"c", "foo"}, {"d", 2}, {"e", "bar"}}, 10.0) + .add({{"a", 2}, {"b", 0}, {"c", "bar"}, {"d", 2}, {"e", "bar"}}, 13.0) + .add({{"a", 2}, {"b", 0}, {"c", "bar"}, {"d", 3}, {"e", "bar"}}, 12.0) + .add({{"a", 2}, {"b", 0}, {"c", "foo"}, {"d", 3}, {"e", "foo"}}, 11.0) + .add({{"a", 2}, {"b", 0}, {"c", "qux"}, {"d", 1}, {"e", "foo"}}, 14.0); + + auto output = ReferenceOperations::reduce(input, {"a"}, Aggr::SUM); + auto expect = TensorSpec("tensor<float>(b[1],c{},d[5],e{})") + .add({{"b", 0}, {"c", "bar"}, {"d", 0}, {"e", "qux"}}, 7.0) + .add({{"b", 0}, {"c", "bar"}, {"d", 1}, {"e", "foo"}}, 5.0) + .add({{"b", 0}, {"c", "bar"}, {"d", 2}, {"e", "bar"}}, 13.0) + .add({{"b", 0}, {"c", "bar"}, {"d", 2}, {"e", "qux"}}, 9.0) + .add({{"b", 0}, {"c", "bar"}, {"d", 3}, {"e", "bar"}}, 12.0) + .add({{"b", 0}, {"c", "bar"}, {"d", 4}, {"e", "foo"}}, 3.0) + .add({{"b", 0}, {"c", "foo"}, {"d", 1}, {"e", "foo"}}, 4.0) + .add({{"b", 0}, {"c", "foo"}, {"d", 1}, {"e", "qux"}}, 8.0) + .add({{"b", 0}, {"c", "foo"}, {"d", 2}, {"e", "bar"}}, 10.0) + .add({{"b", 0}, {"c", "foo"}, {"d", 2}, {"e", "foo"}}, 6.0) + .add({{"b", 0}, {"c", "foo"}, {"d", 3}, {"e", "foo"}}, 11.0) + .add({{"b", 0}, {"c", "foo"}, {"d", 4}, {"e", "foo"}}, 2.0) + .add({{"b", 0}, {"c", "qux"}, {"d", 1}, {"e", "foo"}}, 14.0); + EXPECT_EQ(output, expect); + + output = ReferenceOperations::reduce(input, {"a", "b", "d"}, Aggr::SUM); + expect = TensorSpec("tensor<float>(c{},e{})") + .add({{"c", "bar"}, {"e", "bar"}}, 25.0) + .add({{"c", "bar"}, {"e", "foo"}}, 8.0) + .add({{"c", "bar"}, {"e", "qux"}}, 16.0) + .add({{"c", "foo"}, {"e", "bar"}}, 10.0) + .add({{"c", "foo"}, {"e", "foo"}}, 23.0) + .add({{"c", "foo"}, {"e", "qux"}}, 8.0) + .add({{"c", "qux"}, {"e", "foo"}}, 14.0); + EXPECT_EQ(output, expect); + + output = ReferenceOperations::reduce(input, {"c"}, Aggr::SUM); + expect = TensorSpec("tensor<float>(a[3],b[1],d[5],e{})") + .add({{"a", 0}, {"b", 0}, {"d", 1}, {"e", "foo"}}, 9.0) + .add({{"a", 0}, {"b", 0}, {"d", 2}, {"e", "foo"}}, 6.0) + .add({{"a", 0}, {"b", 0}, {"d", 4}, {"e", "foo"}}, 5.0) + .add({{"a", 1}, {"b", 0}, {"d", 0}, {"e", "qux"}}, 7.0) + .add({{"a", 1}, {"b", 0}, {"d", 1}, {"e", "qux"}}, 8.0) + .add({{"a", 1}, {"b", 0}, {"d", 2}, {"e", "bar"}}, 10.0) + .add({{"a", 1}, {"b", 0}, {"d", 2}, {"e", "qux"}}, 9.0) + .add({{"a", 2}, {"b", 0}, {"d", 1}, {"e", "foo"}}, 14.0) + .add({{"a", 2}, {"b", 0}, {"d", 2}, {"e", "bar"}}, 13.0) + .add({{"a", 2}, {"b", 0}, {"d", 3}, {"e", "bar"}}, 12.0) + .add({{"a", 2}, {"b", 0}, {"d", 3}, {"e", "foo"}}, 11.0); + EXPECT_EQ(output, expect); + + output = ReferenceOperations::reduce(input, {"a", "c"}, Aggr::SUM); + expect = TensorSpec("tensor<float>(b[1],d[5],e{})") + .add({{"b", 0}, {"d", 0}, {"e", "qux"}}, 7.0) + .add({{"b", 0}, {"d", 1}, {"e", "foo"}}, 23.0) + .add({{"b", 0}, {"d", 1}, {"e", "qux"}}, 8.0) + .add({{"b", 0}, {"d", 2}, {"e", "bar"}}, 23.0) + .add({{"b", 0}, {"d", 2}, {"e", "foo"}}, 6.0) + .add({{"b", 0}, {"d", 2}, {"e", "qux"}}, 9.0) + .add({{"b", 0}, {"d", 3}, {"e", "bar"}}, 12.0) + .add({{"b", 0}, {"d", 3}, {"e", "foo"}}, 11.0) + .add({{"b", 0}, {"d", 4}, {"e", "foo"}}, 5.0); + EXPECT_EQ(output, expect); + + output = ReferenceOperations::reduce(input, {"a", "c", "d"}, Aggr::SUM); + expect = TensorSpec("tensor<float>(b[1],e{})") + .add({{"b", 0}, {"e", "bar"}}, 35.0) + .add({{"b", 0}, {"e", "foo"}}, 45.0) + .add({{"b", 0}, {"e", "qux"}}, 24.0); + EXPECT_EQ(output, expect); + + output = ReferenceOperations::reduce(input, {"a", "b", "c", "d", "e"}, Aggr::SUM); + expect = TensorSpec("double").add({}, 104); + EXPECT_EQ(output, expect); + output = ReferenceOperations::reduce(input, {}, Aggr::SUM); + EXPECT_EQ(output, expect); +} + + +TEST(ReferenceOperationsTest, rename_gives_expected_results) { + auto input = mixed_5d_some_cells(false, false); + auto output = ReferenceOperations::rename(input, + {"a","b","c","e"}, + {"e","x","b","a"}); + auto expect = TensorSpec("tensor(a{},b{},d[5],e[3],x[1])") + .add({{"e", 1}, {"x", 0}, {"b", "foo"}, {"d", 2}, {"a", "bar"}}, 2.0) + .add({{"e", 2}, {"x", 0}, {"b", "bar"}, {"d", 3}, {"a", "bar"}}, 3.0) + .add({{"e", 0}, {"x", 0}, {"b", "foo"}, {"d", 4}, {"a", "foo"}}, 4.0) + .add({{"e", 1}, {"x", 0}, {"b", "bar"}, {"d", 0}, {"a", "qux"}}, 5.0) + .add({{"e", 2}, {"x", 0}, {"b", "qux"}, {"d", 1}, {"a", "foo"}}, 6.0); + EXPECT_EQ(output, expect); +} + +GTEST_MAIN_RUN_ALL_TESTS() + |