summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArne H Juul <arnej27959@users.noreply.github.com>2019-06-26 14:10:08 +0200
committerGitHub <noreply@github.com>2019-06-26 14:10:08 +0200
commit0f7b172e464f4a89d22b12fe0e6ff9082e420b8c (patch)
treee8aaac992d907ea33612285f3fe4db0f3998330a
parent4b3b9f5d2dc616d69e0dbba999595ffc558251a9 (diff)
parent2e6a0ea56d399ba6966a39b0e11ef0b276c3e5af (diff)
Merge pull request #9889 from vespa-engine/arnej/add-dense-dimension-combiner
Arnej/add dense dimension combiner
-rw-r--r--eval/CMakeLists.txt2
-rw-r--r--eval/src/tests/tensor/dense_dimension_combiner/CMakeLists.txt9
-rw-r--r--eval/src/tests/tensor/dense_dimension_combiner/dense_dimension_combiner_test.cpp185
-rw-r--r--eval/src/tests/tensor/dense_tensor_address_combiner/CMakeLists.txt8
-rw-r--r--eval/src/tests/tensor/dense_tensor_address_combiner/dense_tensor_address_combiner_test.cpp32
-rw-r--r--eval/src/vespa/eval/tensor/dense/CMakeLists.txt4
-rw-r--r--eval/src/vespa/eval/tensor/dense/dense_dimension_combiner.cpp91
-rw-r--r--eval/src/vespa/eval/tensor/dense/dense_dimension_combiner.h114
-rw-r--r--eval/src/vespa/eval/tensor/dense/dense_tensor_address_combiner.cpp63
-rw-r--r--eval/src/vespa/eval/tensor/dense/dense_tensor_address_combiner.h123
-rw-r--r--eval/src/vespa/eval/tensor/dense/dense_tensor_apply.hpp69
11 files changed, 420 insertions, 280 deletions
diff --git a/eval/CMakeLists.txt b/eval/CMakeLists.txt
index 26e046ec52a..e0159febdfe 100644
--- a/eval/CMakeLists.txt
+++ b/eval/CMakeLists.txt
@@ -25,6 +25,7 @@ vespa_define_module(
src/tests/eval/value_cache
src/tests/eval/value_type
src/tests/gp/ponder_nov2017
+ src/tests/tensor/dense_dimension_combiner
src/tests/tensor/dense_add_dimension_optimizer
src/tests/tensor/dense_dot_product_function
src/tests/tensor/dense_fast_rename_optimizer
@@ -32,7 +33,6 @@ vespa_define_module(
src/tests/tensor/dense_inplace_map_function
src/tests/tensor/dense_remove_dimension_optimizer
src/tests/tensor/dense_replace_type_function
- src/tests/tensor/dense_tensor_address_combiner
src/tests/tensor/dense_xw_product_function
src/tests/tensor/direct_dense_tensor_builder
src/tests/tensor/direct_sparse_tensor_builder
diff --git a/eval/src/tests/tensor/dense_dimension_combiner/CMakeLists.txt b/eval/src/tests/tensor/dense_dimension_combiner/CMakeLists.txt
new file mode 100644
index 00000000000..eaee8ebb4e4
--- /dev/null
+++ b/eval/src/tests/tensor/dense_dimension_combiner/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+vespa_add_executable(eval_dense_dimension_combiner_test_app TEST
+ SOURCES
+ dense_dimension_combiner_test.cpp
+ DEPENDS
+ vespaeval
+)
+vespa_add_test(NAME eval_dense_dimension_combiner_test_app COMMAND eval_dense_dimension_combiner_test_app)
diff --git a/eval/src/tests/tensor/dense_dimension_combiner/dense_dimension_combiner_test.cpp b/eval/src/tests/tensor/dense_dimension_combiner/dense_dimension_combiner_test.cpp
new file mode 100644
index 00000000000..b8949e3a7e6
--- /dev/null
+++ b/eval/src/tests/tensor/dense_dimension_combiner/dense_dimension_combiner_test.cpp
@@ -0,0 +1,185 @@
+// Copyright 2019 Oath 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/eval/tensor/dense/dense_dimension_combiner.h>
+
+using namespace vespalib;
+using namespace vespalib::eval;
+using namespace vespalib::tensor;
+
+void verifyLeft(DenseDimensionCombiner &d, size_t last) {
+ d.commonReset();
+ d.leftReset();
+ EXPECT_TRUE(d.leftInRange());
+ EXPECT_EQUAL(d.leftIdx(), 0u);
+ size_t expect = 0;
+ while (d.leftInRange()) {
+ d.stepLeft();
+ EXPECT_GREATER(d.leftIdx(), expect);
+ expect = d.leftIdx();
+ }
+ EXPECT_FALSE(d.leftInRange());
+ EXPECT_EQUAL(expect, last);
+ d.leftReset();
+ EXPECT_TRUE(d.leftInRange());
+ EXPECT_EQUAL(d.leftIdx(), 0u);
+}
+
+void verifyRight(DenseDimensionCombiner &d, size_t last) {
+ d.commonReset();
+ d.rightReset();
+ EXPECT_TRUE(d.rightInRange());
+ EXPECT_EQUAL(d.rightIdx(), 0u);
+ size_t expect = 0;
+ while (d.rightInRange()) {
+ d.stepRight();
+ EXPECT_GREATER(d.rightIdx(), expect);
+ expect = d.rightIdx();
+ }
+ EXPECT_FALSE(d.rightInRange());
+ EXPECT_EQUAL(expect, last);
+ d.rightReset();
+ EXPECT_TRUE(d.rightInRange());
+ EXPECT_EQUAL(d.rightIdx(), 0u);
+}
+
+
+TEST("require that one left, one common, one right dimension works") {
+ ValueType t12_lc = ValueType::tensor_type({{"d1_l", 3},{"d2_c", 4}});
+ ValueType t23_cr = ValueType::tensor_type({{"d2_c", 4},{"d3_r", 5}});
+
+ DenseDimensionCombiner d(t12_lc, t23_cr);
+
+ EXPECT_TRUE(d.leftInRange());
+ EXPECT_TRUE(d.rightInRange());
+ EXPECT_TRUE(d.commonInRange());
+ EXPECT_EQUAL(d.leftIdx(), 0u);
+ EXPECT_EQUAL(d.rightIdx(), 0u);
+ EXPECT_EQUAL(d.outputIdx(), 0u);
+
+ d.stepCommon();
+ EXPECT_TRUE(d.leftInRange());
+ EXPECT_TRUE(d.rightInRange());
+ EXPECT_TRUE(d.commonInRange());
+ EXPECT_EQUAL(d.leftIdx(), 1u);
+ EXPECT_EQUAL(d.rightIdx(), 5u);
+ EXPECT_EQUAL(d.outputIdx(), 5u);
+
+ d.stepRight();
+ EXPECT_TRUE(d.leftInRange());
+ EXPECT_TRUE(d.rightInRange());
+ EXPECT_TRUE(d.commonInRange());
+ EXPECT_EQUAL(d.leftIdx(), 1u);
+ EXPECT_EQUAL(d.rightIdx(), 6u);
+ EXPECT_EQUAL(d.outputIdx(), 6u);
+
+ d.stepLeft();
+ EXPECT_TRUE(d.leftInRange());
+ EXPECT_TRUE(d.rightInRange());
+ EXPECT_TRUE(d.commonInRange());
+ EXPECT_EQUAL(d.leftIdx(), 5u);
+ EXPECT_EQUAL(d.rightIdx(), 6u);
+ EXPECT_EQUAL(d.outputIdx(), 26u);
+
+ d.stepLeft();
+ EXPECT_TRUE(d.leftInRange());
+ EXPECT_TRUE(d.rightInRange());
+ EXPECT_TRUE(d.commonInRange());
+ EXPECT_EQUAL(d.leftIdx(), 9u);
+ EXPECT_EQUAL(d.rightIdx(), 6u);
+ EXPECT_EQUAL(d.outputIdx(), 46u);
+
+ d.stepLeft();
+ EXPECT_FALSE(d.leftInRange());
+ EXPECT_TRUE(d.rightInRange());
+ EXPECT_TRUE(d.commonInRange());
+ EXPECT_EQUAL(d.leftIdx(), 13u);
+ EXPECT_EQUAL(d.rightIdx(), 6u);
+ EXPECT_EQUAL(d.outputIdx(), 6u);
+
+ d.leftReset();
+ EXPECT_TRUE(d.leftInRange());
+ EXPECT_TRUE(d.rightInRange());
+ EXPECT_TRUE(d.commonInRange());
+ EXPECT_EQUAL(d.leftIdx(), 1u);
+ EXPECT_EQUAL(d.rightIdx(), 6u);
+ EXPECT_EQUAL(d.outputIdx(), 6u);
+
+ d.stepCommon();
+ EXPECT_TRUE(d.leftInRange());
+ EXPECT_TRUE(d.rightInRange());
+ EXPECT_TRUE(d.commonInRange());
+ EXPECT_EQUAL(d.leftIdx(), 2u);
+ EXPECT_EQUAL(d.rightIdx(), 11u);
+ EXPECT_EQUAL(d.outputIdx(), 11u);
+
+ d.stepRight();
+ EXPECT_TRUE(d.leftInRange());
+ EXPECT_TRUE(d.rightInRange());
+ EXPECT_TRUE(d.commonInRange());
+ EXPECT_EQUAL(d.leftIdx(), 2u);
+ EXPECT_EQUAL(d.rightIdx(), 12u);
+ EXPECT_EQUAL(d.outputIdx(), 12u);
+
+ TEST_DO(verifyLeft(d, 12));
+ TEST_DO(verifyRight(d, 20));
+}
+
+TEST("require that two left, no common, two right dimensions works") {
+ ValueType t12_ll = ValueType::tensor_type({{"d1_l", 3},{"d2_l", 4}});
+ ValueType t34_rr = ValueType::tensor_type({{"d3_r", 5},{"d4_r", 2}});
+
+ DenseDimensionCombiner d(t12_ll, t34_rr);
+
+ EXPECT_TRUE(d.leftInRange());
+ EXPECT_TRUE(d.rightInRange());
+ EXPECT_TRUE(d.commonInRange());
+ EXPECT_EQUAL(d.leftIdx(), 0u);
+ EXPECT_EQUAL(d.rightIdx(), 0u);
+ EXPECT_EQUAL(d.outputIdx(), 0u);
+
+ d.stepCommon();
+ EXPECT_TRUE(d.leftInRange());
+ EXPECT_TRUE(d.rightInRange());
+ EXPECT_FALSE(d.commonInRange());
+ EXPECT_EQUAL(d.leftIdx(), 0u);
+ EXPECT_EQUAL(d.rightIdx(), 0u);
+ EXPECT_EQUAL(d.outputIdx(), 120u);
+
+ d.commonReset();
+ d.stepRight();
+ EXPECT_TRUE(d.leftInRange());
+ EXPECT_TRUE(d.rightInRange());
+ EXPECT_TRUE(d.commonInRange());
+ EXPECT_EQUAL(d.leftIdx(), 0u);
+ EXPECT_EQUAL(d.rightIdx(), 1u);
+ EXPECT_EQUAL(d.outputIdx(), 1u);
+
+ d.stepLeft();
+ EXPECT_TRUE(d.leftInRange());
+ EXPECT_TRUE(d.rightInRange());
+ EXPECT_TRUE(d.commonInRange());
+ EXPECT_EQUAL(d.leftIdx(), 1u);
+ EXPECT_EQUAL(d.rightIdx(), 1u);
+ EXPECT_EQUAL(d.outputIdx(), 11u);
+
+ d.stepLeft();
+ d.stepLeft();
+ d.stepLeft();
+ d.stepLeft();
+ d.stepLeft();
+ d.stepLeft();
+ d.stepLeft();
+
+ EXPECT_TRUE(d.leftInRange());
+ EXPECT_TRUE(d.rightInRange());
+ EXPECT_TRUE(d.commonInRange());
+ EXPECT_EQUAL(d.leftIdx(), 8u);
+ EXPECT_EQUAL(d.rightIdx(), 1u);
+ EXPECT_EQUAL(d.outputIdx(), 81u);
+
+ TEST_DO(verifyLeft(d, 12));
+ TEST_DO(verifyRight(d, 10));
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/eval/src/tests/tensor/dense_tensor_address_combiner/CMakeLists.txt b/eval/src/tests/tensor/dense_tensor_address_combiner/CMakeLists.txt
deleted file mode 100644
index a006d70935d..00000000000
--- a/eval/src/tests/tensor/dense_tensor_address_combiner/CMakeLists.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-vespa_add_executable(eval_dense_tensor_address_combiner_test_app TEST
- SOURCES
- dense_tensor_address_combiner_test.cpp
- DEPENDS
- vespaeval
-)
-vespa_add_test(NAME eval_dense_tensor_address_combiner_test_app COMMAND eval_dense_tensor_address_combiner_test_app)
diff --git a/eval/src/tests/tensor/dense_tensor_address_combiner/dense_tensor_address_combiner_test.cpp b/eval/src/tests/tensor/dense_tensor_address_combiner/dense_tensor_address_combiner_test.cpp
deleted file mode 100644
index 91a6087ea3a..00000000000
--- a/eval/src/tests/tensor/dense_tensor_address_combiner/dense_tensor_address_combiner_test.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2017 Yahoo Holdings. 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/eval/tensor/dense/dense_tensor_address_combiner.h>
-#include <vespa/vespalib/test/insertion_operators.h>
-
-using namespace vespalib::tensor;
-using vespalib::eval::ValueType;
-
-ValueType
-combine(const std::vector<ValueType::Dimension> &lhs,
- const std::vector<ValueType::Dimension> &rhs)
-{
- return DenseTensorAddressCombiner::combineDimensions(
- ValueType::tensor_type(lhs),
- ValueType::tensor_type(rhs));
-}
-
-TEST("require that dimensions can be combined")
-{
- EXPECT_EQUAL(ValueType::tensor_type({{"a", 3}, {"b", 5}}), combine({{"a", 3}}, {{"b", 5}}));
- EXPECT_EQUAL(ValueType::tensor_type({{"a", 3}, {"b", 5}}), combine({{"a", 3}, {"b", 5}}, {{"b", 5}}));
- EXPECT_EQUAL(ValueType::tensor_type({{"a", 3}, {"b", 5}}), combine({{"a", 3}, {"b", 5}}, {{"b", 5}}));
- EXPECT_EQUAL(ValueType::tensor_type({{"a", 3}, {"b", 11}, {"c", 5}, {"d", 7}, {"e", 17}}),
- combine({{"a", 3}, {"c", 5}, {"d", 7}},
- {{"b", 11}, {"c", 5}, {"e", 17}}));
- EXPECT_EQUAL(ValueType::tensor_type({{"a", 3}, {"b", 11}, {"c", 5}, {"d", 7}, {"e", 17}}),
- combine({{"b", 11}, {"c", 5}, {"e", 17}},
- {{"a", 3}, {"c", 5}, {"d", 7}}));
-}
-
-TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/eval/src/vespa/eval/tensor/dense/CMakeLists.txt b/eval/src/vespa/eval/tensor/dense/CMakeLists.txt
index c2638466de6..ce20d6ba6d9 100644
--- a/eval/src/vespa/eval/tensor/dense/CMakeLists.txt
+++ b/eval/src/vespa/eval/tensor/dense/CMakeLists.txt
@@ -2,16 +2,16 @@
vespa_add_library(eval_tensor_dense OBJECT
SOURCES
dense_add_dimension_optimizer.cpp
+ dense_dimension_combiner.cpp
dense_dot_product_function.cpp
dense_fast_rename_optimizer.cpp
dense_inplace_join_function.cpp
dense_inplace_map_function.cpp
dense_remove_dimension_optimizer.cpp
dense_replace_type_function.cpp
- dense_tensor.cpp
- dense_tensor_address_combiner.cpp
dense_tensor_address_mapper.cpp
dense_tensor_cells_iterator.cpp
+ dense_tensor.cpp
dense_tensor_modify.cpp
dense_tensor_reduce.cpp
dense_tensor_view.cpp
diff --git a/eval/src/vespa/eval/tensor/dense/dense_dimension_combiner.cpp b/eval/src/vespa/eval/tensor/dense/dense_dimension_combiner.cpp
new file mode 100644
index 00000000000..22c8ff12ad1
--- /dev/null
+++ b/eval/src/vespa/eval/tensor/dense/dense_dimension_combiner.cpp
@@ -0,0 +1,91 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "dense_dimension_combiner.h"
+#include <cassert>
+
+namespace vespalib::tensor {
+
+DenseDimensionCombiner::~DenseDimensionCombiner() = default;
+
+DenseDimensionCombiner::DenseDimensionCombiner(const eval::ValueType &lhs,
+ const eval::ValueType &rhs)
+ : _left(), _right(),
+ _commonDims(),
+ _outputIndex(0),
+ _outputSize(1u),
+ result_type(eval::ValueType::join(lhs, rhs))
+{
+ assert(lhs.is_dense());
+ assert(rhs.is_dense());
+ assert(result_type.is_dense());
+
+ const auto &lDims = lhs.dimensions();
+ const auto &rDims = rhs.dimensions();
+ const auto &oDims = result_type.dimensions();
+
+ size_t i = lDims.size();
+ size_t j = rDims.size();
+ size_t k = oDims.size();
+
+ uint32_t lMul = 1;
+ uint32_t rMul = 1;
+ uint32_t oMul = 1;
+
+ while (k-- > 0) {
+ if ((i > 0) && (lDims[i-1].name == oDims[k].name)) {
+ --i;
+ // left dim match
+ if ((j > 0) && (rDims[j-1].name == oDims[k].name)) {
+ // both dim match
+ --j;
+ CommonDim cd;
+ cd.idx = 0;
+ cd.leftMultiplier = lMul;
+ cd.rightMultiplier = rMul;
+ cd.outputMultiplier = oMul;
+ assert(lDims[i].size == oDims[k].size);
+ assert(rDims[j].size == oDims[k].size);
+ cd.size = oDims[k].size;
+ lMul *= cd.size;
+ rMul *= cd.size;
+ oMul *= cd.size;
+ _left.totalSize *= cd.size;
+ _right.totalSize *= cd.size;
+ _outputSize *= cd.size;
+ _commonDims.push_back(cd);
+ } else {
+ SideDim ld;
+ ld.idx = 0;
+ ld.sideMultiplier = lMul;
+ ld.outputMultiplier = oMul;
+ assert(lDims[i].size == oDims[k].size);
+ ld.size = oDims[k].size;
+ lMul *= ld.size;
+ oMul *= ld.size;
+ _outputSize *= ld.size;
+ _left.totalSize *= ld.size;
+ _left.dims.push_back(ld);
+ }
+ } else {
+ // right dim match
+ assert(j > 0);
+ assert(rDims[j-1].name == oDims[k].name);
+ --j;
+ SideDim rd;
+ rd.idx = 0;
+ rd.sideMultiplier = rMul;
+ rd.outputMultiplier = oMul;
+ assert(rDims[j].size == oDims[k].size);
+ rd.size = oDims[k].size;
+ rMul *= rd.size;
+ oMul *= rd.size;
+ _outputSize *= rd.size;
+ _right.totalSize *= rd.size;
+ _right.dims.push_back(rd);
+ }
+ }
+}
+
+
+} // namespace
+
diff --git a/eval/src/vespa/eval/tensor/dense/dense_dimension_combiner.h b/eval/src/vespa/eval/tensor/dense/dense_dimension_combiner.h
new file mode 100644
index 00000000000..dd3f74bad9b
--- /dev/null
+++ b/eval/src/vespa/eval/tensor/dense/dense_dimension_combiner.h
@@ -0,0 +1,114 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <vespa/eval/tensor/tensor.h>
+#include <vespa/eval/tensor/types.h>
+#include <vespa/eval/eval/value_type.h>
+
+namespace vespalib::tensor {
+
+class DenseDimensionCombiner {
+
+ struct SideDim {
+ uint32_t idx;
+ uint32_t size;
+ uint32_t sideMultiplier;
+ uint32_t outputMultiplier;
+ };
+ struct CommonDim {
+ uint32_t idx;
+ uint32_t size;
+ uint32_t leftMultiplier;
+ uint32_t rightMultiplier;
+ uint32_t outputMultiplier;
+ };
+
+ struct SideDims {
+ std::vector<SideDim> dims;
+ uint32_t index;
+ uint32_t totalSize;
+
+ SideDims() : dims(), index(0), totalSize(1u) {}
+
+ void reset(uint32_t &outIndex) {
+ for (SideDim& d : dims) {
+ index -= d.idx * d.sideMultiplier;
+ outIndex -= d.idx * d.outputMultiplier;
+ d.idx = 0;
+ }
+ if (index >= totalSize) {
+ index -= totalSize;
+ }
+ }
+ void step(uint32_t &outIndex) {
+ for (SideDim& d : dims) {
+ d.idx++;
+ index += d.sideMultiplier;
+ outIndex += d.outputMultiplier;
+ if (d.idx < d.size) return;
+ index -= d.idx * d.sideMultiplier;
+ outIndex -= d.idx * d.outputMultiplier;
+ d.idx = 0;
+ }
+ index += totalSize;
+ }
+ };
+ SideDims _left;
+ SideDims _right;
+ std::vector<CommonDim> _commonDims;
+ uint32_t _outputIndex;
+ uint32_t _outputSize;
+
+public:
+ size_t leftIdx() const { return _left.index; }
+ size_t rightIdx() const { return _right.index; }
+ size_t outputIdx() const { return _outputIndex; }
+
+ bool leftInRange() const { return _left.index < _left.totalSize; }
+ bool rightInRange() const { return _right.index < _right.totalSize; }
+ bool commonInRange() const { return _outputIndex < _outputSize; }
+
+ void leftReset() { _left.reset(_outputIndex); }
+ void stepLeft() { _left.step(_outputIndex); }
+
+ void rightReset() { _right.reset(_outputIndex); }
+ void stepRight() { _right.step(_outputIndex); }
+
+ void commonReset() {
+ for (CommonDim& cd : _commonDims) {
+ _left.index -= cd.idx * cd.leftMultiplier;
+ _right.index -= cd.idx * cd.rightMultiplier;
+ _outputIndex -= cd.idx * cd.outputMultiplier;
+ cd.idx = 0;
+ }
+ if (_outputIndex >= _outputSize) {
+ _outputIndex -= _outputSize;
+ }
+ }
+
+ void stepCommon() {
+ size_t lim = _commonDims.size();
+ for (size_t i = 0; i < lim; ++i) {
+ CommonDim &cd = _commonDims[i];
+ cd.idx++;
+ _left.index += cd.leftMultiplier;
+ _right.index += cd.rightMultiplier;
+ _outputIndex += cd.outputMultiplier;
+ if (cd.idx < cd.size) return;
+ _left.index -= cd.idx * cd.leftMultiplier;
+ _right.index -= cd.idx * cd.rightMultiplier;
+ _outputIndex -= cd.idx * cd.outputMultiplier;
+ cd.idx = 0;
+ }
+ _outputIndex += _outputSize;
+ }
+
+ const eval::ValueType result_type;
+
+ DenseDimensionCombiner(const eval::ValueType &lhs, const eval::ValueType &rhs);
+
+ ~DenseDimensionCombiner();
+};
+
+}
diff --git a/eval/src/vespa/eval/tensor/dense/dense_tensor_address_combiner.cpp b/eval/src/vespa/eval/tensor/dense/dense_tensor_address_combiner.cpp
deleted file mode 100644
index b5c5d9b6a04..00000000000
--- a/eval/src/vespa/eval/tensor/dense/dense_tensor_address_combiner.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#include "dense_tensor_address_combiner.h"
-#include <vespa/vespalib/util/exceptions.h>
-#include <cassert>
-
-namespace vespalib::tensor {
-
-DenseTensorAddressCombiner::~DenseTensorAddressCombiner() = default;
-
-DenseTensorAddressCombiner::DenseTensorAddressCombiner(const eval::ValueType &combined, const eval::ValueType &lhs,
- const eval::ValueType &rhs)
- : _rightAddress(rhs),
- _combinedAddress(combined),
- _left(),
- _commonRight(),
- _right()
-{
- auto rhsItr = rhs.dimensions().cbegin();
- auto rhsItrEnd = rhs.dimensions().cend();
- uint32_t numDimensions(0);
- for (const auto &lhsDim : lhs.dimensions()) {
- while ((rhsItr != rhsItrEnd) && (rhsItr->name < lhsDim.name)) {
- _right.emplace_back(numDimensions++, rhsItr-rhs.dimensions().cbegin());
- ++rhsItr;
- }
- if ((rhsItr != rhsItrEnd) && (rhsItr->name == lhsDim.name)) {
- _left.emplace_back(numDimensions, _left.size());
- _commonRight.emplace_back(numDimensions, rhsItr-rhs.dimensions().cbegin());
- ++numDimensions;
- ++rhsItr;
- } else {
- _left.emplace_back(numDimensions++, _left.size());
- }
- }
- while (rhsItr != rhsItrEnd) {
- _right.emplace_back(numDimensions++, rhsItr-rhs.dimensions().cbegin());
- ++rhsItr;
- }
-}
-
-AddressContext::AddressContext(const eval::ValueType &type)
- : _type(type),
- _accumulatedSize(_type.dimensions().size()),
- _address(type.dimensions().size(), 0)
-
-{
- size_t multiplier = 1;
- for (int32_t i(_address.size() - 1); i >= 0; i--) {
- _accumulatedSize[i] = multiplier;
- multiplier *= type.dimensions()[i].size;
- }
-}
-
-AddressContext::~AddressContext() = default;
-
-eval::ValueType
-DenseTensorAddressCombiner::combineDimensions(const eval::ValueType &lhs, const eval::ValueType &rhs)
-{
- return eval::ValueType::join(lhs, rhs);
-}
-
-}
diff --git a/eval/src/vespa/eval/tensor/dense/dense_tensor_address_combiner.h b/eval/src/vespa/eval/tensor/dense/dense_tensor_address_combiner.h
deleted file mode 100644
index 3f6e347490c..00000000000
--- a/eval/src/vespa/eval/tensor/dense/dense_tensor_address_combiner.h
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#pragma once
-
-#include "dense_tensor_cells_iterator.h"
-#include <vespa/eval/tensor/tensor.h>
-#include <vespa/eval/tensor/types.h>
-#include <vespa/eval/eval/value_type.h>
-
-namespace vespalib::tensor {
-
-class AddressContext {
-public:
- using Address = DenseTensorCellsIterator::Address;
- using size_type = eval::ValueType::Dimension::size_type;
- using Mapping = std::vector<std::pair<uint32_t, uint32_t>>;
- AddressContext(const eval::ValueType &type);
- ~AddressContext();
- size_type dimSize(uint32_t dim) const { return _type.dimensions()[dim].size; }
- size_type wholeDimStep(uint32_t dim) const { return _accumulatedSize[dim] * dimSize(dim); }
- size_t index() const {
- size_t cellIdx(0);
- for (uint32_t i(0); i < _address.size(); i++) {
- cellIdx += _address[i]*_accumulatedSize[i];
- }
- return cellIdx;
- }
- void update(const Address & addr, const Mapping & mapping) {
- for (const auto & m : mapping) {
- _address[m.first] = addr[m.second];
- }
- }
- bool updateCommon(const Address & addr, const Mapping & mapping) {
- for (const auto & m : mapping) {
- if (addr[m.first] >= dimSize(m.second)) {
- return false;
- }
- _address[m.second] = addr[m.first];
- }
- return true;
- }
-
- const eval::ValueType &_type;
- std::vector<size_t> _accumulatedSize;
- Address _address;
-
-};
-
-/**
- * Combines two dense tensor addresses to a new tensor address.
- * The resulting dimensions is the union of the input dimensions and
- * common dimensions must have matching labels.
- */
-class DenseTensorAddressCombiner
-{
-public:
- using Mapping = AddressContext::Mapping;
-
-private:
- using Address = DenseTensorCellsIterator::Address;
- using CellsRef = vespalib::ConstArrayRef<double>;
- using size_type = eval::ValueType::Dimension::size_type;
-
- AddressContext _rightAddress;
- AddressContext _combinedAddress;
-
- Mapping _left;
- Mapping _commonRight;
- Mapping _right;
-
-public:
- DenseTensorAddressCombiner(const eval::ValueType &combined, const eval::ValueType &lhs, const eval::ValueType &rhs);
- ~DenseTensorAddressCombiner();
- void updateLeftAndCommon(const Address & addr) { _combinedAddress.update(addr, _left); }
- bool updateCommon() { return _rightAddress.updateCommon(_combinedAddress._address, _commonRight); }
- bool hasAnyRightOnlyDimensions() const { return ! _right.empty(); }
-
- const Address & address() const { return _combinedAddress._address; }
- size_t rightCellIndex() const { return _rightAddress.index(); }
-
- template <typename Func>
- void for_each_right(const CellsRef & rhsCells, Func && func) {
- // The rightAddress oly holds the starting point for iteration and what is need to efficiently maintain
- // an index for addressing th ecells.
- const int32_t lastDimension = _right.size() - 1;
- int32_t curDimension = lastDimension;
- size_t rightCellIdx = _rightAddress.index();
- size_t combinedCellIdx = _combinedAddress.index();
- while (curDimension >= 0) {
- const uint32_t rdim = _right[curDimension].second;
- const uint32_t cdim = _right[curDimension].first;
- size_type & cindex = _combinedAddress._address[cdim];
- if (curDimension == lastDimension) {
- for (cindex = 0; cindex < _rightAddress.dimSize(rdim); cindex++) {
- func(combinedCellIdx, rhsCells[rightCellIdx]);
- rightCellIdx += _rightAddress._accumulatedSize[rdim];
- combinedCellIdx += _combinedAddress._accumulatedSize[cdim];
- }
- cindex = 0;
- rightCellIdx -= _rightAddress.wholeDimStep(rdim);
- combinedCellIdx -= _combinedAddress.wholeDimStep(cdim);
- curDimension--;
- } else {
- if ((cindex + 1) < _rightAddress.dimSize(rdim)) {
- cindex++;
- rightCellIdx += _rightAddress._accumulatedSize[rdim];
- combinedCellIdx += _combinedAddress._accumulatedSize[cdim];
- curDimension++;
- } else {
- rightCellIdx -= _rightAddress.wholeDimStep(rdim);
- combinedCellIdx -= _combinedAddress.wholeDimStep(cdim);
- cindex = 0;
- curDimension--;
- }
- }
- }
- }
-
- static eval::ValueType combineDimensions(const eval::ValueType &lhs, const eval::ValueType &rhs);
-};
-
-
-}
diff --git a/eval/src/vespa/eval/tensor/dense/dense_tensor_apply.hpp b/eval/src/vespa/eval/tensor/dense/dense_tensor_apply.hpp
index fa1e59c87db..e71840f392c 100644
--- a/eval/src/vespa/eval/tensor/dense/dense_tensor_apply.hpp
+++ b/eval/src/vespa/eval/tensor/dense/dense_tensor_apply.hpp
@@ -3,78 +3,45 @@
#pragma once
#include "dense_tensor_apply.h"
-#include "dense_tensor_address_combiner.h"
+#include "dense_dimension_combiner.h"
#include "direct_dense_tensor_builder.h"
namespace vespalib::tensor::dense {
template <typename Function>
std::unique_ptr<Tensor>
-apply(DenseTensorAddressCombiner & combiner, DirectDenseTensorBuilder & builder,
- const DenseTensorView &lhs, const DenseTensorView::CellsRef & rhsCells, Function &&func) __attribute__((noinline));
+apply(DenseDimensionCombiner & combiner, DirectDenseTensorBuilder & builder,
+ const DenseTensorView::CellsRef & lhsCells,
+ const DenseTensorView::CellsRef & rhsCells, Function &&func) __attribute__((noinline));
template <typename Function>
std::unique_ptr<Tensor>
-apply(DenseTensorAddressCombiner & combiner, DirectDenseTensorBuilder & builder,
- const DenseTensorView &lhs, const DenseTensorView::CellsRef & rhsCells, Function &&func)
+apply(DenseDimensionCombiner & combiner, DirectDenseTensorBuilder & builder,
+ const DenseTensorView::CellsRef & lhsCells,
+ const DenseTensorView::CellsRef & rhsCells, Function &&func)
{
- for (DenseTensorCellsIterator lhsItr = lhs.cellsIterator(); lhsItr.valid(); lhsItr.next()) {
- combiner.updateLeftAndCommon(lhsItr.address());
- if (combiner.updateCommon()) {
- combiner.for_each_right(rhsCells, [&func, &builder, &lhsItr](size_t combined, double rhsCell) {
- builder.insertCell(combined, func(lhsItr.cell(), rhsCell));
- });
+ for (combiner.leftReset(); combiner.leftInRange(); combiner.stepLeft()) {
+ for (combiner.rightReset(); combiner.rightInRange(); combiner.stepRight()) {
+ for (combiner.commonReset(); combiner.commonInRange(); combiner.stepCommon()) {
+ size_t outIdx = combiner.outputIdx();
+ size_t l = combiner.leftIdx();
+ size_t r = combiner.rightIdx();
+ builder.insertCell(outIdx, func(lhsCells[l], rhsCells[r]));
+ }
}
}
return builder.build();
}
-
-template <typename Function>
-std::unique_ptr<Tensor>
-apply_no_rightonly_dimensions(DenseTensorAddressCombiner & combiner, DirectDenseTensorBuilder & builder,
- const DenseTensorView &lhs, const DenseTensorView::CellsRef & rhsCells,
- Function &&func) __attribute__((noinline));
-
-template <typename Function>
-std::unique_ptr<Tensor>
-apply_no_rightonly_dimensions(DenseTensorAddressCombiner & combiner, DirectDenseTensorBuilder & builder,
- const DenseTensorView &lhs, const DenseTensorView::CellsRef & rhsCells, Function &&func)
-{
- for (DenseTensorCellsIterator lhsItr = lhs.cellsIterator(); lhsItr.valid(); lhsItr.next()) {
- combiner.updateLeftAndCommon(lhsItr.address());
- if (combiner.updateCommon()) {
- builder.insertCell(combiner.address(), func(lhsItr.cell(), rhsCells[combiner.rightCellIndex()]));
- }
- }
- return builder.build();
-}
-
-template <typename Function>
-std::unique_ptr<Tensor>
-apply(const DenseTensorView &lhs, const DenseTensorView &rhs, Function &&func)
-{
- eval::ValueType resultType = DenseTensorAddressCombiner::combineDimensions(lhs.fast_type(), rhs.fast_type());
- DenseTensorAddressCombiner combiner(resultType, lhs.fast_type(), rhs.fast_type());
- DirectDenseTensorBuilder builder(resultType);
- if (combiner.hasAnyRightOnlyDimensions()) {
- return apply(combiner, builder, lhs, rhs.cellsRef(), std::move(func));
- } else {
- return apply_no_rightonly_dimensions(combiner, builder, lhs, rhs.cellsRef(), std::move(func));
- }
-}
-
template <typename Function>
std::unique_ptr<Tensor>
apply(const DenseTensorView &lhs, const Tensor &rhs, Function &&func)
{
const DenseTensorView *view = dynamic_cast<const DenseTensorView *>(&rhs);
if (view) {
- return apply(lhs, *view, func);
- }
- const DenseTensor *dense = dynamic_cast<const DenseTensor *>(&rhs);
- if (dense) {
- return apply(lhs, *dense, func);
+ DenseDimensionCombiner combiner(lhs.fast_type(), view->fast_type());
+ DirectDenseTensorBuilder builder(combiner.result_type);
+ return apply(combiner, builder, lhs.cellsRef(), view->cellsRef(), std::move(func));
}
return Tensor::UP();
}