aboutsummaryrefslogtreecommitdiffstats
path: root/vespalib/src/tests/tensor/dense_tensor_operations/dense_tensor_operations_test.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'vespalib/src/tests/tensor/dense_tensor_operations/dense_tensor_operations_test.cpp')
-rw-r--r--vespalib/src/tests/tensor/dense_tensor_operations/dense_tensor_operations_test.cpp473
1 files changed, 473 insertions, 0 deletions
diff --git a/vespalib/src/tests/tensor/dense_tensor_operations/dense_tensor_operations_test.cpp b/vespalib/src/tests/tensor/dense_tensor_operations/dense_tensor_operations_test.cpp
new file mode 100644
index 00000000000..8264fa74e17
--- /dev/null
+++ b/vespalib/src/tests/tensor/dense_tensor_operations/dense_tensor_operations_test.cpp
@@ -0,0 +1,473 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/vespalib/testkit/test_kit.h>
+#include <vespa/vespalib/tensor/dense/dense_tensor.h>
+#include <vespa/vespalib/tensor/dense/dense_tensor_builder.h>
+#include <vespa/vespalib/tensor/types.h>
+#include <vespa/vespalib/tensor/tensor_function.h>
+#include <vespa/vespalib/tensor/tensor_visitor.h>
+#include <vespa/vespalib/tensor/tensor_type.h>
+#include <iostream>
+
+using namespace vespalib::tensor;
+
+using DenseTensorCells = std::map<std::map<vespalib::string, size_t>, double>;
+
+namespace vespalib {
+namespace tensor {
+
+static bool operator==(const Tensor &lhs, const Tensor &rhs)
+{
+ return lhs.equals(rhs);
+}
+
+}
+}
+
+//-----------------------------------------------------------------------------
+
+class MyInput : public TensorFunction::Input
+{
+private:
+ std::vector<Tensor::CREF> tensors;
+ std::vector<CellFunction::CREF> cell_functions;
+ const Tensor &get_tensor(size_t id) const override {
+ ASSERT_GREATER(tensors.size(), id);
+ return tensors[id];
+ }
+ virtual const CellFunction &get_cell_function(size_t id) const override {
+ ASSERT_GREATER(cell_functions.size(), id);
+ return cell_functions[id];
+ }
+public:
+ size_t add(const Tensor &tensor) {
+ size_t id = tensors.size();
+ tensors.push_back(tensor);
+ return id;
+ }
+ size_t add(const CellFunction &cell_function) {
+ size_t id = cell_functions.size();
+ cell_functions.push_back(cell_function);
+ return id;
+ }
+};
+
+const Tensor &eval_tensor(function::Node &function_ir, const TensorFunction::Input &input) {
+ ASSERT_TRUE(function_ir.type().is_tensor());
+ TensorFunction &function = function_ir; // compile step
+ const Tensor &result = function.eval(input).as_tensor;
+ EXPECT_EQUAL(result.getType(), function_ir.type());
+ return result;
+}
+
+const Tensor &eval_tensor_unchecked(function::Node &function_ir, const TensorFunction::Input &input) {
+ ASSERT_TRUE(function_ir.type().is_tensor());
+ TensorFunction &function = function_ir; // compile step
+ return function.eval(input).as_tensor;
+}
+
+const Tensor &eval_tensor_unchecked_allow_invalid(function::Node &function_ir, const TensorFunction::Input &input) {
+ TensorFunction &function = function_ir; // compile step
+ return function.eval(input).as_tensor;
+}
+
+double eval_number(function::Node &function_ir, const TensorFunction::Input &input) {
+ ASSERT_TRUE(function_ir.type().is_number());
+ TensorFunction &function = function_ir; // compile step
+ return function.eval(input).as_double;
+}
+
+//-----------------------------------------------------------------------------
+
+template <typename BuilderType>
+struct Fixture
+{
+ BuilderType _builder;
+ Fixture() : _builder() {}
+
+ Tensor::UP createTensor(const DenseTensorCells &cells) {
+ std::map<std::string, size_t> dimensionSizes;
+ for (const auto &cell : cells) {
+ for (const auto &addressElem : cell.first) {
+ dimensionSizes[addressElem.first] = std::max(dimensionSizes[addressElem.first],
+ (addressElem.second + 1));
+ }
+ }
+ std::map<std::string, typename BuilderType::Dimension> dimensionEnums;
+ for (const auto &dimensionElem : dimensionSizes) {
+ dimensionEnums[dimensionElem.first] =
+ _builder.defineDimension(dimensionElem.first, dimensionElem.second);
+ }
+ for (const auto &cell : cells) {
+ for (const auto &addressElem : cell.first) {
+ const auto &dimension = addressElem.first;
+ size_t label = addressElem.second;
+ _builder.addLabel(dimensionEnums[dimension], label);
+ }
+ _builder.addCell(cell.second);
+ }
+ return _builder.build();
+ }
+ void assertAddImpl(const Tensor &exp, const Tensor &lhs, const Tensor &rhs) {
+ MyInput input;
+ function::Node_UP ir = function::add(function::input(lhs.getType(), input.add(lhs)),
+ function::input(rhs.getType(), input.add(rhs)));
+ EXPECT_EQUAL(exp, eval_tensor(*ir, input));
+ }
+ void assertAdd(const DenseTensorCells &exp,
+ const DenseTensorCells &lhs, const DenseTensorCells &rhs) {
+ assertAddImpl(*createTensor(exp), *createTensor(lhs), *createTensor(rhs));
+ }
+ void assertSubtractImpl(const Tensor &exp, const Tensor &lhs, const Tensor &rhs) {
+ MyInput input;
+ function::Node_UP ir = function::subtract(function::input(lhs.getType(), input.add(lhs)),
+ function::input(rhs.getType(), input.add(rhs)));
+ EXPECT_EQUAL(exp, eval_tensor(*ir, input));
+ }
+ void assertSubtract(const DenseTensorCells &exp,
+ const DenseTensorCells &lhs,
+ const DenseTensorCells &rhs) {
+ assertSubtractImpl(*createTensor(exp), *createTensor(lhs), *createTensor(rhs));
+ }
+ void assertMinImpl(const Tensor &exp, const Tensor &lhs, const Tensor &rhs) {
+ MyInput input;
+ function::Node_UP ir = function::min(function::input(lhs.getType(), input.add(lhs)),
+ function::input(rhs.getType(), input.add(rhs)));
+ EXPECT_EQUAL(exp, eval_tensor(*ir, input));
+ }
+ void assertMin(const DenseTensorCells &exp, const DenseTensorCells &lhs,
+ const DenseTensorCells &rhs) {
+ assertMinImpl(*createTensor(exp), *createTensor(lhs), *createTensor(rhs));
+ }
+ void assertMaxImpl(const Tensor &exp, const Tensor &lhs, const Tensor &rhs) {
+ MyInput input;
+ function::Node_UP ir = function::max(function::input(lhs.getType(), input.add(lhs)),
+ function::input(rhs.getType(), input.add(rhs)));
+ EXPECT_EQUAL(exp, eval_tensor(*ir, input));
+ }
+ void assertMax(const DenseTensorCells &exp, const DenseTensorCells &lhs,
+ const DenseTensorCells &rhs) {
+ assertMaxImpl(*createTensor(exp), *createTensor(lhs), *createTensor(rhs));
+ }
+ void assertSumImpl(double exp, const Tensor &tensor) {
+ MyInput input;
+ function::Node_UP ir = function::sum(function::input(tensor.getType(), input.add(tensor)));
+ EXPECT_EQUAL(exp, eval_number(*ir, input));
+ }
+ void assertSum(double exp, const DenseTensorCells &cells) {
+ assertSumImpl(exp, *createTensor(cells));
+ }
+ void assertMatchImpl(const Tensor &exp, const Tensor &lhs, const Tensor &rhs) {
+ MyInput input;
+ function::Node_UP ir = function::match(function::input(lhs.getType(), input.add(lhs)),
+ function::input(rhs.getType(), input.add(rhs)));
+ EXPECT_EQUAL(exp, eval_tensor(*ir, input));
+ }
+ void assertMatch(const DenseTensorCells &exp, const DenseTensorCells &lhs,
+ const DenseTensorCells &rhs) {
+ assertMatchImpl(*createTensor(exp), *createTensor(lhs), *createTensor(rhs));
+ }
+ void assertApplyImpl(const Tensor &exp, const Tensor &tensor, const CellFunction &func) {
+ MyInput input;
+ function::Node_UP ir = function::apply(function::input(tensor.getType(), input.add(tensor)), input.add(func));
+ EXPECT_EQUAL(exp, eval_tensor(*ir, input));
+ }
+ void assertApply(const DenseTensorCells &exp, const DenseTensorCells &arg,
+ const CellFunction &func) {
+ assertApplyImpl(*createTensor(exp), *createTensor(arg), func);
+ }
+ void assertDimensionSumImpl(const Tensor &exp, const Tensor &tensor, const vespalib::string &dimension) {
+ MyInput input;
+ function::Node_UP ir = function::dimension_sum(function::input(tensor.getType(), input.add(tensor)), dimension);
+ if (!ir->type().is_valid()) {
+ // According to the ir, it is not allowed to sum over a
+ // non-existing dimension. The current implementation
+ // allows this, resulting in a tensor with no cells and
+ // with all dimensions not sliced.
+ EXPECT_EQUAL(exp, eval_tensor_unchecked_allow_invalid(*ir, input)); // UNCHECKED_ALLOW_INVALID
+ } else {
+ EXPECT_EQUAL(exp, eval_tensor(*ir, input));
+ }
+ }
+ void assertDimensionSum(const DenseTensorCells &exp,
+ const DenseTensorCells &arg,
+ const vespalib::string &dimension) {
+ assertDimensionSumImpl(*createTensor(exp), *createTensor(arg), dimension);
+ }
+ void assertMultiplyImpl(const Tensor &exp, const Tensor &lhs, const Tensor &rhs) {
+ MyInput input;
+ function::Node_UP ir = function::multiply(function::input(lhs.getType(), input.add(lhs)),
+ function::input(rhs.getType(), input.add(rhs)));
+ EXPECT_EQUAL(exp, eval_tensor(*ir, input));
+ }
+ void assertMultiply(const DenseTensorCells &exp,
+ const DenseTensorCells &lhs, const DenseTensorCells &rhs) {
+ assertMultiplyImpl(*createTensor(exp), *createTensor(lhs), *createTensor(rhs));
+ }
+};
+
+using DenseFixture = Fixture<DenseTensorBuilder>;
+
+
+template <typename FixtureType>
+void
+testTensorAdd(FixtureType &f)
+{
+ f.assertAdd({},{},{});
+ f.assertAdd({ {{{"x",0}}, 8} },
+ { {{{"x",0}}, 3} },
+ { {{{"x",0}}, 5} });
+ f.assertAdd({ {{{"x",0}}, -2} },
+ { {{{"x",0}}, 3} },
+ { {{{"x",0}}, -5} });
+ f.assertAdd({ {{{"x",0}}, 10}, {{{"x",1}}, 16} },
+ { {{{"x",0}}, 3}, {{{"x",1}}, 5} },
+ { {{{"x",0}}, 7}, {{{"x",1}}, 11} });
+ f.assertAdd({ {{{"x",0},{"y",0}}, 8} },
+ { {{{"x",0},{"y",0}}, 3} },
+ { {{{"x",0},{"y",0}}, 5} });
+}
+
+template <typename FixtureType>
+void
+testTensorSubtract(FixtureType &f)
+{
+ f.assertSubtract({},{},{});
+ f.assertSubtract({ {{{"x",0}}, -2} },
+ { {{{"x",0}}, 3} },
+ { {{{"x",0}}, 5} });
+ f.assertSubtract({ {{{"x",0}}, 8} },
+ { {{{"x",0}}, 3} },
+ { {{{"x",0}}, -5} });
+ f.assertSubtract({ {{{"x",0}}, -4}, {{{"x",1}}, -6} },
+ { {{{"x",0}}, 3}, {{{"x",1}}, 5} },
+ { {{{"x",0}}, 7}, {{{"x",1}}, 11} });
+ f.assertSubtract({ {{{"x",0},{"y",0}}, -2} },
+ { {{{"x",0},{"y",0}}, 3} },
+ { {{{"x",0},{"y",0}}, 5} });
+}
+
+template <typename FixtureType>
+void
+testTensorMin(FixtureType &f)
+{
+ f.assertMin({},{},{});
+ f.assertMin({ {{{"x",0}}, 3} },
+ { {{{"x",0}}, 3} },
+ { {{{"x",0}}, 5} });
+ f.assertMin({ {{{"x",0}}, -5} },
+ { {{{"x",0}}, 3} },
+ { {{{"x",0}}, -5} });
+ f.assertMin({ {{{"x",0}}, 3}, {{{"x",1}}, 5} },
+ { {{{"x",0}}, 3}, {{{"x",1}}, 5} },
+ { {{{"x",0}}, 7}, {{{"x",1}}, 11} });
+ f.assertMin({ {{{"x",0},{"y",0}}, 3} },
+ { {{{"x",0},{"y",0}}, 3} },
+ { {{{"x",0},{"y",0}}, 5} });
+}
+
+template <typename FixtureType>
+void
+testTensorMax(FixtureType &f)
+{
+ f.assertMax({},{},{});
+ f.assertMax({ {{{"x",0}}, 5} },
+ { {{{"x",0}}, 3} },
+ { {{{"x",0}}, 5} });
+ f.assertMax({ {{{"x",0}}, 3} },
+ { {{{"x",0}}, 3} },
+ { {{{"x",0}}, -5} });
+ f.assertMax({ {{{"x",0}}, 7}, {{{"x",1}}, 11} },
+ { {{{"x",0}}, 3}, {{{"x",1}}, 5} },
+ { {{{"x",0}}, 7}, {{{"x",1}}, 11} });
+ f.assertMax({ {{{"x",0},{"y",0}}, 5} },
+ { {{{"x",0},{"y",0}}, 3} },
+ { {{{"x",0},{"y",0}}, 5} });
+}
+
+template <typename FixtureType>
+void
+testTensorSum(FixtureType &f)
+{
+ f.assertSum(0.0, {});
+ f.assertSum(0.0, { {{{"x",0}}, 0} });
+ f.assertSum(3.0, { {{{"x",0}}, 3} });
+ f.assertSum(8.0, { {{{"x",0}}, 3}, {{{"x",1}}, 5} });
+ f.assertSum(-2.0, { {{{"x",0}}, 3}, {{{"x",1}}, -5} });
+}
+
+template <typename FixtureType>
+void
+testTensorMatch(FixtureType &f)
+{
+ f.assertMatch({}, {}, {});
+ f.assertMatch({ {{{"x",0}}, 15} },
+ { {{{"x",0}}, 3} },
+ { {{{"x",0}}, 5} });
+ f.assertMatch({ {{{"x",0}}, 0} },
+ { {{{"x",0}}, 3} },
+ { {{{"x",0}}, 0} });
+ f.assertMatch({ {{{"x",0}}, -15} },
+ { {{{"x",0}}, 3} },
+ { {{{"x",0}}, -5} });
+ f.assertMatch({ {{{"x",0}, {"y",0}}, 39},
+ {{{"x",1}, {"y",0}}, 85},
+ {{{"x",0}, {"y",1}}, 133},
+ {{{"x",1}, {"y",1}}, 253} },
+ { {{{"x",0}, {"y",0}}, 3},
+ {{{"x",1}, {"y",0}}, 5},
+ {{{"x",0}, {"y",1}}, 7},
+ {{{"x",1}, {"y",1}}, 11} },
+ { {{{"x",0}, {"y",0}}, 13},
+ {{{"x",1}, {"y",0}}, 17},
+ {{{"x",0}, {"y",1}}, 19},
+ {{{"x",1}, {"y",1}}, 23} });
+}
+
+template <typename FixtureType>
+void
+testTensorMultiply(FixtureType &f)
+{
+ f.assertMultiply({}, {}, {});
+ f.assertMultiply({ {{{"x",0}}, 15} },
+ { {{{"x",0}}, 3} },
+ { {{{"x",0}}, 5} });
+ f.assertMultiply({ {{{"x",0}}, 21},
+ {{{"x",1}}, 55} },
+ { {{{"x",0}}, 3},
+ {{{"x",1}}, 5} },
+ { {{{"x",0}}, 7},
+ {{{"x",1}}, 11} });
+ f.assertMultiply({ {{{"x",0},{"y",0}}, 15} },
+ { {{{"x",0}}, 3} },
+ { {{{"y",0}}, 5} });
+ f.assertMultiply({ {{{"x",0},{"y",0}}, 21},
+ {{{"x",0},{"y",1}}, 33},
+ {{{"x",1},{"y",0}}, 35},
+ {{{"x",1},{"y",1}}, 55} },
+ { {{{"x",0}}, 3},
+ {{{"x",1}}, 5} },
+ { {{{"y",0}}, 7},
+ {{{"y",1}}, 11} });
+ f.assertMultiply({ {{{"x",0},{"y",0},{"z",0}}, 7},
+ {{{"x",0},{"y",0},{"z",1}}, 11},
+ {{{"x",0},{"y",1},{"z",0}}, 26},
+ {{{"x",0},{"y",1},{"z",1}}, 34},
+ {{{"x",1},{"y",0},{"z",0}}, 21},
+ {{{"x",1},{"y",0},{"z",1}}, 33},
+ {{{"x",1},{"y",1},{"z",0}}, 65},
+ {{{"x",1},{"y",1},{"z",1}}, 85} },
+ { {{{"x",0},{"y",0}}, 1},
+ {{{"x",0},{"y",1}}, 2},
+ {{{"x",1},{"y",0}}, 3},
+ {{{"x",1},{"y",1}}, 5} },
+ { {{{"y",0},{"z",0}}, 7},
+ {{{"y",0},{"z",1}}, 11},
+ {{{"y",1},{"z",0}}, 13},
+ {{{"y",1},{"z",1}}, 17} });
+}
+
+template <typename FixtureType>
+void
+testTensorMultiplePreservationOfDimensions(FixtureType &f)
+{
+ (void) f;
+}
+
+struct MyFunction : public CellFunction
+{
+ virtual double apply(double value) const override {
+ return value + 5;
+ }
+};
+
+template <typename FixtureType>
+void
+testTensorApply(FixtureType &f)
+{
+ f.assertApply({ {{{"x",0}}, 6}, {{{"x",1}}, 2} },
+ { {{{"x",0}}, 1}, {{{"x",1}}, -3} },
+ MyFunction());
+}
+
+template <typename FixtureType>
+void
+testTensorSumDimension(FixtureType &f)
+{
+ f.assertDimensionSum({ {{{"y",0}}, 4}, {{{"y",1}}, 12} },
+ { {{{"x",0},{"y",0}}, 1},
+ {{{"x",1},{"y",0}}, 3},
+ {{{"x",0},{"y",1}}, 5},
+ {{{"x",1},{"y",1}}, 7} }, "x");
+
+ f.assertDimensionSum({ {{{"x",0}}, 6}, {{{"x",1}}, 10} },
+ { {{{"x",0},{"y",0}}, 1},
+ {{{"x",1},{"y",0}}, 3},
+ {{{"x",0},{"y",1}}, 5},
+ {{{"x",1},{"y",1}}, 7} }, "y");
+ f.assertDimensionSum({ {{{"y",0}, {"z",0}}, 4},
+ {{{"y",1}, {"z",0}}, 12},
+ {{{"y",0}, {"z",1}}, 24},
+ {{{"y",1}, {"z",1}}, 36} },
+ { {{{"x",0},{"y",0}, {"z",0}}, 1},
+ {{{"x",1},{"y",0}, {"z",0}}, 3},
+ {{{"x",0},{"y",1}, {"z",0}}, 5},
+ {{{"x",1},{"y",1}, {"z",0}}, 7},
+ {{{"x",0},{"y",0}, {"z",1}}, 11},
+ {{{"x",1},{"y",0}, {"z",1}}, 13},
+ {{{"x",0},{"y",1}, {"z",1}}, 17},
+ {{{"x",1},{"y",1}, {"z",1}}, 19} }, "x");
+ f.assertDimensionSum({ {{{"x",0}, {"z",0}}, 6},
+ {{{"x",1}, {"z",0}}, 10},
+ {{{"x",0}, {"z",1}}, 28},
+ {{{"x",1}, {"z",1}}, 32} },
+ { {{{"x",0},{"y",0}, {"z",0}}, 1},
+ {{{"x",1},{"y",0}, {"z",0}}, 3},
+ {{{"x",0},{"y",1}, {"z",0}}, 5},
+ {{{"x",1},{"y",1}, {"z",0}}, 7},
+ {{{"x",0},{"y",0}, {"z",1}}, 11},
+ {{{"x",1},{"y",0}, {"z",1}}, 13},
+ {{{"x",0},{"y",1}, {"z",1}}, 17},
+ {{{"x",1},{"y",1}, {"z",1}}, 19} }, "y");
+ f.assertDimensionSum({ {{{"x",0}, {"y",0}}, 12},
+ {{{"x",1}, {"y",0}}, 16},
+ {{{"x",0}, {"y",1}}, 22},
+ {{{"x",1}, {"y",1}}, 26} },
+ { {{{"x",0},{"y",0}, {"z",0}}, 1},
+ {{{"x",1},{"y",0}, {"z",0}}, 3},
+ {{{"x",0},{"y",1}, {"z",0}}, 5},
+ {{{"x",1},{"y",1}, {"z",0}}, 7},
+ {{{"x",0},{"y",0}, {"z",1}}, 11},
+ {{{"x",1},{"y",0}, {"z",1}}, 13},
+ {{{"x",0},{"y",1}, {"z",1}}, 17},
+ {{{"x",1},{"y",1}, {"z",1}}, 19} }, "z");
+ f.assertDimensionSum({ {{{"x",0}}, 3} },
+ { {{{"x",0}}, 3} },
+ "y");
+ f.assertDimensionSum({ {{}, 3} },
+ { {{{"x",0}}, 3} },
+ "x");
+}
+
+template <typename FixtureType>
+void
+testAllTensorOperations(FixtureType &f)
+{
+ TEST_DO(testTensorAdd(f));
+ TEST_DO(testTensorSubtract(f));
+ TEST_DO(testTensorMin(f));
+ TEST_DO(testTensorMax(f));
+ TEST_DO(testTensorSum(f));
+ TEST_DO(testTensorMatch(f));
+ TEST_DO(testTensorMultiply(f));
+ TEST_DO(testTensorMultiplePreservationOfDimensions(f));
+ TEST_DO(testTensorApply(f));
+ TEST_DO(testTensorSumDimension(f));
+}
+
+TEST_F("test tensor operations for DenseTensor", DenseFixture)
+{
+ testAllTensorOperations(f);
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }