diff options
author | Arne Juul <arnej@yahoo-inc.com> | 2019-06-27 08:48:33 +0000 |
---|---|---|
committer | Arne Juul <arnej@yahoo-inc.com> | 2019-07-04 11:49:32 +0000 |
commit | 5a993bc1a9272281e41ca220ee117d0f8d3081d8 (patch) | |
tree | f466bc6ec9e203a12973cfcb66cef49171f0fb75 | |
parent | eb95248e0f4e89e59e8af64efdcfc0388529b832 (diff) |
add TypedCells and related functionality
* templated DenseTensor
* templated DenseTensorModify
* add templated TypedDenseTensorBuilder
* remove DirectDenseTensorBuilder
* remove unused TensorMapper
* add dispatch structs
* add unit test for generic dense join
* add special handling of reducing all dimensions
49 files changed, 719 insertions, 843 deletions
diff --git a/eval/CMakeLists.txt b/eval/CMakeLists.txt index e0159febdfe..d91e35f2d63 100644 --- a/eval/CMakeLists.txt +++ b/eval/CMakeLists.txt @@ -29,6 +29,7 @@ vespa_define_module( src/tests/tensor/dense_add_dimension_optimizer src/tests/tensor/dense_dot_product_function src/tests/tensor/dense_fast_rename_optimizer + src/tests/tensor/dense_generic_join src/tests/tensor/dense_inplace_join_function src/tests/tensor/dense_inplace_map_function src/tests/tensor/dense_remove_dimension_optimizer @@ -39,7 +40,6 @@ vespa_define_module( src/tests/tensor/tensor_add_operation src/tests/tensor/tensor_address src/tests/tensor/tensor_conformance - src/tests/tensor/tensor_mapper src/tests/tensor/tensor_modify_operation src/tests/tensor/tensor_remove_operation src/tests/tensor/tensor_serialization diff --git a/eval/src/tests/tensor/dense_generic_join/CMakeLists.txt b/eval/src/tests/tensor/dense_generic_join/CMakeLists.txt new file mode 100644 index 00000000000..1fbb35cb2b8 --- /dev/null +++ b/eval/src/tests/tensor/dense_generic_join/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(eval_dense_generic_join_test_app TEST + SOURCES + dense_generic_join_test.cpp + DEPENDS + vespaeval +) +vespa_add_test(NAME eval_dense_generic_join_test_app COMMAND eval_dense_generic_join_test_app) diff --git a/eval/src/tests/tensor/dense_generic_join/dense_generic_join_test.cpp b/eval/src/tests/tensor/dense_generic_join/dense_generic_join_test.cpp new file mode 100644 index 00000000000..395437e13dd --- /dev/null +++ b/eval/src/tests/tensor/dense_generic_join/dense_generic_join_test.cpp @@ -0,0 +1,127 @@ +// Copyright 2018 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/eval/tensor_function.h> +#include <vespa/eval/eval/simple_tensor.h> +#include <vespa/eval/eval/simple_tensor_engine.h> +#include <vespa/eval/tensor/default_tensor_engine.h> +#include <vespa/eval/tensor/dense/typed_dense_tensor_builder.h> +#include <vespa/eval/tensor/dense/dense_tensor.h> +#include <vespa/eval/eval/test/tensor_model.hpp> +#include <vespa/eval/eval/test/eval_fixture.h> + +#include <vespa/vespalib/util/stringfmt.h> +#include <vespa/vespalib/util/stash.h> + +using namespace vespalib; +using namespace vespalib::eval; +using namespace vespalib::eval::test; +using namespace vespalib::tensor; +using namespace vespalib::eval::tensor_function; + +const TensorEngine &prod_engine = DefaultTensorEngine::ref(); + +double seq_value = 0.0; + +struct GlobalSequence : public Sequence { + GlobalSequence() {} + double operator[](size_t) const override { + seq_value += 1.0; + return seq_value; + } + ~GlobalSequence() {} +}; +GlobalSequence seq; + +EvalFixture::ParamRepo make_params() { + return EvalFixture::ParamRepo() + .add("con_x5_A", spec({x(5) }, seq)) + .add("con_x5y3_B", spec({x(5),y(3) }, seq)) + .add("con_x5z4_C", spec({x(5), z(4)}, seq)) + .add("con_x5y3z4_D", spec({x(5),y(3),z(4)}, seq)) + .add("con_y3_E", spec({ y(3) }, seq)) + .add("con_y3z4_F", spec({ y(3),z(4)}, seq)) + .add("con_z4_G", spec({ z(4)}, seq)) + .add("con_x5f_H", spec({x(5) }, seq), "tensor<float>(x[5])") + .add("con_x5y3_I", spec({x(5),y(3) }, seq), "tensor<float>(x[5],y[3])") + .add("con_x5z4_J", spec({x(5), z(4)}, seq), "tensor<float>(x[5],z[4])") + .add("con_x5y3z4_K", spec({x(5),y(3),z(4)}, seq), "tensor<float>(x[5],y[3],z[4])") + .add("con_y3_L", spec({ y(3) }, seq), "tensor<float>(y[3])") + .add("con_y3z4_M", spec({ y(3),z(4)}, seq), "tensor<float>(y[3],z[4])))") + .add("con_z4_N", spec({ z(4)}, seq), "tensor<float>(z[4]))") + .add("con_y2", spec({y(5)}, seq)) + .add("con_y2f", spec({y(5)}, seq), "tensor<float>(y[2]))"); +} +EvalFixture::ParamRepo param_repo = make_params(); + +void verify_equal(const vespalib::string &expr) { + EvalFixture fixture(prod_engine, expr, param_repo, true, true); + EXPECT_EQUAL(fixture.result(), EvalFixture::ref(expr, param_repo)); +} + + +TEST("require that non-overlapping dense join works") { + TEST_DO(verify_equal("con_x5_A-con_y3_E")); + TEST_DO(verify_equal("con_x5_A+con_y3_E")); + TEST_DO(verify_equal("con_x5_A*con_y3_E")); + + TEST_DO(verify_equal("con_x5_A-con_y3z4_F")); + TEST_DO(verify_equal("con_x5_A+con_y3z4_F")); + TEST_DO(verify_equal("con_x5_A*con_y3z4_F")); + + TEST_DO(verify_equal("con_x5_A-con_z4_G")); + TEST_DO(verify_equal("con_x5_A+con_z4_G")); + TEST_DO(verify_equal("con_x5_A*con_z4_G")); + + TEST_DO(verify_equal("con_x5y3_B-con_z4_G")); + TEST_DO(verify_equal("con_x5y3_B+con_z4_G")); + TEST_DO(verify_equal("con_x5y3_B*con_z4_G")); + + TEST_DO(verify_equal("con_y3_E-con_z4_G")); + TEST_DO(verify_equal("con_y3_E+con_z4_G")); + TEST_DO(verify_equal("con_y3_E*con_z4_G")); +} + +TEST("require that overlapping dense join works") { + TEST_DO(verify_equal("con_x5_A-con_x5y3_B")); + TEST_DO(verify_equal("con_x5_A+con_x5y3_B")); + TEST_DO(verify_equal("con_x5_A*con_x5y3_B")); + + TEST_DO(verify_equal("con_x5_A-con_x5z4_C")); + TEST_DO(verify_equal("con_x5_A+con_x5z4_C")); + TEST_DO(verify_equal("con_x5_A*con_x5z4_C")); + + TEST_DO(verify_equal("con_x5y3_B-con_y3_E")); + TEST_DO(verify_equal("con_x5y3_B+con_y3_E")); + TEST_DO(verify_equal("con_x5y3_B*con_y3_E")); + + TEST_DO(verify_equal("con_x5y3_B-con_y3z4_F")); + TEST_DO(verify_equal("con_x5y3_B+con_y3z4_F")); + TEST_DO(verify_equal("con_x5y3_B*con_y3z4_F")); + + TEST_DO(verify_equal("con_x5y3z4_D-con_x5y3_B")); + TEST_DO(verify_equal("con_x5y3z4_D+con_x5y3_B")); + TEST_DO(verify_equal("con_x5y3z4_D*con_x5y3_B")); + + TEST_DO(verify_equal("con_x5y3z4_D-con_x5z4_C")); + TEST_DO(verify_equal("con_x5y3z4_D+con_x5z4_C")); + TEST_DO(verify_equal("con_x5y3z4_D*con_x5z4_C")); + + TEST_DO(verify_equal("con_x5y3z4_D-con_y3z4_F")); + TEST_DO(verify_equal("con_x5y3z4_D+con_y3z4_F")); + TEST_DO(verify_equal("con_x5y3z4_D*con_y3z4_F")); + + TEST_DO(verify_equal("con_x5y3z4_D-con_y3z4_F")); + TEST_DO(verify_equal("con_x5y3z4_D+con_y3z4_F")); + TEST_DO(verify_equal("con_x5y3z4_D*con_y3z4_F")); + + TEST_DO(verify_equal("con_y3_E-con_y3z4_F")); + TEST_DO(verify_equal("con_y3_E+con_y3z4_F")); + TEST_DO(verify_equal("con_y3_E*con_y3z4_F")); + + TEST_DO(verify_equal("con_y3z4_F-con_z4_G")); + TEST_DO(verify_equal("con_y3z4_F+con_z4_G")); + TEST_DO(verify_equal("con_y3z4_F*con_z4_G")); +} + +TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/eval/src/tests/tensor/dense_replace_type_function/dense_replace_type_function_test.cpp b/eval/src/tests/tensor/dense_replace_type_function/dense_replace_type_function_test.cpp index 7e6b933d7c6..0533b1c92b7 100644 --- a/eval/src/tests/tensor/dense_replace_type_function/dense_replace_type_function_test.cpp +++ b/eval/src/tests/tensor/dense_replace_type_function/dense_replace_type_function_test.cpp @@ -13,11 +13,9 @@ using namespace vespalib::eval; using namespace vespalib::tensor; using namespace vespalib; -using CellsRef = DenseTensorView::CellsRef; - const TensorEngine &engine = DefaultTensorEngine::ref(); -CellsRef getCellsRef(const eval::Value &value) { +TypedCells getCellsRef(const eval::Value &value) { return static_cast<const DenseTensorView &>(value).cellsRef(); } @@ -58,8 +56,8 @@ TEST_F("require that DenseReplaceTypeFunction works as expected", Fixture()) { f1.mock_child.is_mutable = false; EXPECT_EQUAL(f1.my_fun.result_is_mutable(), false); EXPECT_EQUAL(&f1.children[0].get().get(), &f1.mock_child); - EXPECT_EQUAL(getCellsRef(f1.state.stack[0]).begin(), getCellsRef(*f1.my_value).begin()); - EXPECT_EQUAL(getCellsRef(f1.state.stack[0]).end(), getCellsRef(*f1.my_value).end()); + EXPECT_EQUAL(getCellsRef(f1.state.stack[0]).data, getCellsRef(*f1.my_value).data); + EXPECT_EQUAL(getCellsRef(f1.state.stack[0]).size, getCellsRef(*f1.my_value).size); EXPECT_EQUAL(f1.state.stack[0].get().type(), f1.new_type); fprintf(stderr, "%s\n", f1.my_fun.as_string().c_str()); } diff --git a/eval/src/tests/tensor/direct_dense_tensor_builder/direct_dense_tensor_builder_test.cpp b/eval/src/tests/tensor/direct_dense_tensor_builder/direct_dense_tensor_builder_test.cpp index a0b7b60e2da..3f4641ed2ee 100644 --- a/eval/src/tests/tensor/direct_dense_tensor_builder/direct_dense_tensor_builder_test.cpp +++ b/eval/src/tests/tensor/direct_dense_tensor_builder/direct_dense_tensor_builder_test.cpp @@ -2,31 +2,36 @@ #include <vespa/vespalib/test/insertion_operators.h> #include <vespa/vespalib/testkit/test_kit.h> -#include <vespa/eval/tensor/dense/direct_dense_tensor_builder.h> +#include <vespa/eval/tensor/dense/typed_dense_tensor_builder.h> #include <vespa/vespalib/util/exceptions.h> using namespace vespalib::tensor; using vespalib::IllegalArgumentException; -using Builder = DirectDenseTensorBuilder; +using BuilderDbl = TypedDenseTensorBuilder<double>; +using BuilderFlt = TypedDenseTensorBuilder<float>; using vespalib::eval::TensorSpec; using vespalib::eval::ValueType; using vespalib::ConstArrayRef; -template <typename T> std::vector<T> make_vector(const ConstArrayRef<T> &ref) { - std::vector<T> vec; - for (const T &t: ref) { - vec.push_back(t); +struct CallMakeVector { + template <typename T> + static std::vector<double> call(const ConstArrayRef<T> &ref) { + std::vector<double> result; + result.reserve(ref.size()); + for (T v : ref) { + result.push_back(v); + } + return result; } - return vec; -} +}; void assertTensor(const vespalib::string &type_spec, - const DenseTensor::Cells &expCells, + const std::vector<double> &expCells, const Tensor &tensor) { - const DenseTensor &realTensor = dynamic_cast<const DenseTensor &>(tensor); + const DenseTensorView &realTensor = dynamic_cast<const DenseTensorView &>(tensor); EXPECT_EQUAL(ValueType::from_spec(type_spec), realTensor.type()); - EXPECT_EQUAL(expCells, make_vector(realTensor.cellsRef())); + EXPECT_EQUAL(expCells, dispatch_1<CallMakeVector>(realTensor.cellsRef())); } void assertTensorSpec(const TensorSpec &expSpec, const Tensor &tensor) { @@ -35,7 +40,7 @@ void assertTensorSpec(const TensorSpec &expSpec, const Tensor &tensor) { } Tensor::UP build1DTensor() { - Builder builder(ValueType::from_spec("tensor(x[3])")); + BuilderDbl builder(ValueType::from_spec("tensor(x[3])")); builder.insertCell(0, 10); builder.insertCell(1, 11); builder.insertCell(2, 12); @@ -55,7 +60,7 @@ TEST("require that 1d tensor can be converted to tensor spec") { } Tensor::UP build2DTensor() { - Builder builder(ValueType::from_spec("tensor(x[3],y[2])")); + BuilderDbl builder(ValueType::from_spec("tensor(x[3],y[2])")); builder.insertCell({0, 0}, 10); builder.insertCell({0, 1}, 11); builder.insertCell({1, 0}, 12); @@ -81,7 +86,7 @@ TEST("require that 2d tensor can be converted to tensor spec") { } TEST("require that 3d tensor can be constructed") { - Builder builder(ValueType::from_spec("tensor(x[3],y[2],z[2])")); + BuilderDbl builder(ValueType::from_spec("tensor(x[3],y[2],z[2])")); builder.insertCell({0, 0, 0}, 10); builder.insertCell({0, 0, 1}, 11); builder.insertCell({0, 1, 0}, 12); @@ -99,16 +104,26 @@ TEST("require that 3d tensor can be constructed") { *builder.build()); } +TEST("require that 2d tensor with float cells can be constructed") { + BuilderFlt builder(ValueType::from_spec("tensor<float>(x[3],y[2])")); + builder.insertCell({0, 1}, 2.5); + builder.insertCell({1, 0}, 1.5); + builder.insertCell({2, 0}, -0.25); + builder.insertCell({2, 1}, 0.75); + assertTensor("tensor<float>(x[3],y[2])", {0,2.5,1.5,0,-0.25,0.75}, + *builder.build()); +} + TEST("require that cells get default value 0 if not specified") { - Builder builder(ValueType::from_spec("tensor(x[3])")); + BuilderDbl builder(ValueType::from_spec("tensor(x[3])")); builder.insertCell(1, 11); assertTensor("tensor(x[3])", {0,11,0}, *builder.build()); } -void assertTensorCell(const DenseTensor::Address &expAddress, +void assertTensorCell(const DenseTensorView::Address &expAddress, double expCell, - const DenseTensor::CellsIterator &itr) + const DenseTensorView::CellsIterator &itr) { EXPECT_TRUE(itr.valid()); EXPECT_EQUAL(expAddress, itr.address()); @@ -118,14 +133,14 @@ void assertTensorCell(const DenseTensor::Address &expAddress, TEST("require that dense tensor cells iterator works for 1d tensor") { Tensor::UP tensor; { - Builder builder(ValueType::from_spec("tensor(x[2])")); + BuilderDbl builder(ValueType::from_spec("tensor(x[2])")); builder.insertCell(0, 2); builder.insertCell(1, 3); tensor = builder.build(); } - const DenseTensor &denseTensor = dynamic_cast<const DenseTensor &>(*tensor); - DenseTensor::CellsIterator itr = denseTensor.cellsIterator(); + const DenseTensorView &denseTensor = dynamic_cast<const DenseTensorView &>(*tensor); + DenseTensorView::CellsIterator itr = denseTensor.cellsIterator(); assertTensorCell({0}, 2, itr); itr.next(); @@ -137,7 +152,7 @@ TEST("require that dense tensor cells iterator works for 1d tensor") { TEST("require that dense tensor cells iterator works for 2d tensor") { Tensor::UP tensor; { - Builder builder(ValueType::from_spec("tensor(x[2],y[2])")); + BuilderDbl builder(ValueType::from_spec("tensor(x[2],y[2])")); builder.insertCell({0, 0}, 2); builder.insertCell({0, 1}, 3); builder.insertCell({1, 0}, 5); @@ -145,8 +160,8 @@ TEST("require that dense tensor cells iterator works for 2d tensor") { tensor = builder.build(); } - const DenseTensor &denseTensor = dynamic_cast<const DenseTensor &>(*tensor); - DenseTensor::CellsIterator itr = denseTensor.cellsIterator(); + const DenseTensorView &denseTensor = dynamic_cast<const DenseTensorView &>(*tensor); + DenseTensorView::CellsIterator itr = denseTensor.cellsIterator(); assertTensorCell({0,0}, 2, itr); itr.next(); diff --git a/eval/src/tests/tensor/tensor_mapper/.gitignore b/eval/src/tests/tensor/tensor_mapper/.gitignore deleted file mode 100644 index 8a312ff3157..00000000000 --- a/eval/src/tests/tensor/tensor_mapper/.gitignore +++ /dev/null @@ -1 +0,0 @@ -vespalib_tensor_mapper_test_app diff --git a/eval/src/tests/tensor/tensor_mapper/CMakeLists.txt b/eval/src/tests/tensor/tensor_mapper/CMakeLists.txt deleted file mode 100644 index dfc35b4f018..00000000000 --- a/eval/src/tests/tensor/tensor_mapper/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_tensor_mapper_test_app TEST - SOURCES - tensor_mapper_test.cpp - DEPENDS - vespaeval -) -vespa_add_test(NAME eval_tensor_mapper_test_app COMMAND eval_tensor_mapper_test_app) diff --git a/eval/src/tests/tensor/tensor_mapper/tensor_mapper_test.cpp b/eval/src/tests/tensor/tensor_mapper/tensor_mapper_test.cpp deleted file mode 100644 index 60b17930f0b..00000000000 --- a/eval/src/tests/tensor/tensor_mapper/tensor_mapper_test.cpp +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - -#include <vespa/eval/eval/simple_tensor.h> -#include <vespa/eval/eval/tensor_spec.h> -#include <vespa/eval/tensor/default_tensor_engine.h> -#include <vespa/eval/tensor/tensor_mapper.h> -#include <vespa/eval/tensor/test/test_utils.h> -#include <vespa/eval/tensor/wrapped_simple_tensor.h> -#include <vespa/vespalib/testkit/test_kit.h> -#include <vespa/vespalib/util/stringfmt.h> - -using vespalib::eval::ValueType; -using vespalib::eval::Value; -using vespalib::eval::TensorSpec; -using vespalib::eval::SimpleTensor; -using vespalib::tensor::test::makeTensor; -using namespace vespalib::tensor; - -void -verify_wrapped(const TensorSpec &source, const vespalib::string &type, const TensorSpec &expect) -{ - auto tensor = std::make_unique<WrappedSimpleTensor>(SimpleTensor::create(source)); - auto mapped = TensorMapper::mapToWrapped(*tensor, ValueType::from_spec(type)); - TensorSpec actual = mapped->toSpec(); - EXPECT_EQUAL(actual, expect); -} - -void -verify(const TensorSpec &source, const vespalib::string &type, const TensorSpec &expect) -{ - auto tensor = makeTensor<Tensor>(source); - TensorMapper mapper(ValueType::from_spec(type)); - auto mapped = mapper.map(*tensor); - TensorSpec actual = mapped->toSpec(); - EXPECT_EQUAL(actual, expect); - TEST_DO(verify_wrapped(source, type, expect)); -} - -TEST("require that sparse tensors can be mapped to sparse type") { - TEST_DO(verify(TensorSpec("tensor(x{},y{})") - .add({{"x","1"},{"y","1"}}, 1) - .add({{"x","2"},{"y","1"}}, 3) - .add({{"x","1"},{"y","2"}}, 5) - .add({{"x","2"},{"y","2"}}, 7), - "tensor(y{})", - TensorSpec("tensor(y{})") - .add({{"y","1"}}, 4) - .add({{"y","2"}}, 12))); - - TEST_DO(verify(TensorSpec("tensor(x{},y{})") - .add({{"x","1"},{"y","1"}}, 1) - .add({{"x","2"},{"y","1"}}, 3) - .add({{"x","1"},{"y","2"}}, 5) - .add({{"x","2"},{"y","2"}}, 7), - "tensor(x{})", - TensorSpec("tensor(x{})") - .add({{"x","1"}}, 6) - .add({{"x","2"}}, 10))); -} - -TEST("require that sparse tensors can be mapped to dense type") { - TEST_DO(verify(TensorSpec("tensor(x{},y{})") - .add({{"x","1"},{"y","0"}}, 1) - .add({{"x","2"},{"y","0"}}, 3) - .add({{"x","1"},{"y","1"}}, 5) - .add({{"x","2"},{"y","1"}}, 7), - "tensor(y[3])", - TensorSpec("tensor(y[3])") - .add({{"y",0}}, 4) - .add({{"y",1}}, 12) - .add({{"y",2}}, 0))); - - TEST_DO(verify(TensorSpec("tensor(x{},y{})") - .add({{"x","1"},{"y","0x"}}, 1) - .add({{"x","2"},{"y",""}}, 3) - .add({{"x","1"},{"y","1"}}, 5) - .add({{"x","2"},{"y","10"}}, 7), - "tensor(y[3])", - TensorSpec("tensor(y[3])") - .add({{"y",0}}, 3) - .add({{"y",1}}, 5) - .add({{"y",2}}, 0))); - - TEST_DO(verify(TensorSpec("tensor(x{},y{})") - .add({{"x","0"},{"y","0"}}, 1) - .add({{"x","1"},{"y","0"}}, 3) - .add({{"x","0"},{"y","1"}}, 5) - .add({{"x","10"},{"y","1"}}, 7), - "tensor(x[2],y[3])", - TensorSpec("tensor(x[2],y[3])") - .add({{"x",0},{"y",0}}, 1) - .add({{"x",0},{"y",1}}, 5) - .add({{"x",0},{"y",2}}, 0) - .add({{"x",1},{"y",0}}, 3) - .add({{"x",1},{"y",1}}, 0) - .add({{"x",1},{"y",2}}, 0))); -} - -TEST("require that dense tensors can be mapped to sparse type") { - TEST_DO(verify(TensorSpec("tensor(x[2],y[2])") - .add({{"x",0},{"y",0}}, 1) - .add({{"x",0},{"y",1}}, 3) - .add({{"x",1},{"y",0}}, 5) - .add({{"x",1},{"y",1}}, 7), - "tensor(x{})", - TensorSpec("tensor(x{})") - .add({{"x","0"}}, 4) - .add({{"x","1"}}, 12))); -} - -TEST("require that mixed tensors can be mapped to sparse type") { - TEST_DO(verify(TensorSpec("tensor(x[2],y{})") - .add({{"x",0},{"y","0"}}, 1) - .add({{"x",0},{"y","1"}}, 3) - .add({{"x",1},{"y","0"}}, 5) - .add({{"x",1},{"y","1"}}, 7), - "tensor(x{})", - TensorSpec("tensor(x{})") - .add({{"x","0"}}, 4) - .add({{"x","1"}}, 12))); -} - -TEST("require that mixed tensors can be mapped to dense type") { - TEST_DO(verify(TensorSpec("tensor(x[2],y{})") - .add({{"x",0},{"y","0"}}, 1) - .add({{"x",0},{"y","1"}}, 3) - .add({{"x",1},{"y","0"}}, 5) - .add({{"x",1},{"y","1"}}, 7), - "tensor(y[2])", - TensorSpec("tensor(y[2])") - .add({{"y",0}}, 6) - .add({{"y",1}}, 10))); -} - -TEST("require that mixed tensors can be mapped to mixed type") { - TEST_DO(verify(TensorSpec("tensor(x[2],y{})") - .add({{"x",0},{"y","0"}}, 1) - .add({{"x",0},{"y","1"}}, 3) - .add({{"x",1},{"y","0"}}, 5) - .add({{"x",1},{"y","1"}}, 7), - "tensor(x{},y[2])", - TensorSpec("tensor(x{},y[2])") - .add({{"x","0"},{"y",0}}, 1) - .add({{"x","0"},{"y",1}}, 3) - .add({{"x","1"},{"y",0}}, 5) - .add({{"x","1"},{"y",1}}, 7))); -} - -TEST("require that dense tensors can be mapped to mixed type") { - TEST_DO(verify(TensorSpec("tensor(x[2],y[2])") - .add({{"x",0},{"y",0}}, 1) - .add({{"x",0},{"y",1}}, 3) - .add({{"x",1},{"y",0}}, 5) - .add({{"x",1},{"y",1}}, 7), - "tensor(x{},y[2])", - TensorSpec("tensor(x{},y[2])") - .add({{"x","0"},{"y",0}}, 1) - .add({{"x","0"},{"y",1}}, 3) - .add({{"x","1"},{"y",0}}, 5) - .add({{"x","1"},{"y",1}}, 7))); -} - -TEST("require that sparse tensors can be mapped to mixed type") { - TEST_DO(verify(TensorSpec("tensor(x{},y{})") - .add({{"x","0"},{"y","0"}}, 1) - .add({{"x","0"},{"y","1"}}, 3) - .add({{"x","1"},{"y","0"}}, 5) - .add({{"x","1"},{"y","1"}}, 7), - "tensor(x[2],y{})", - TensorSpec("tensor(x[2],y{})") - .add({{"x",0},{"y","0"}}, 1) - .add({{"x",0},{"y","1"}}, 3) - .add({{"x",1},{"y","0"}}, 5) - .add({{"x",1},{"y","1"}}, 7))); -} - -TEST("require that missing dimensions are added appropriately") { - TEST_DO(verify(TensorSpec("tensor(x{})") - .add({{"x","foo"}}, 42), - "tensor(x{},y{})", - TensorSpec("tensor(x{},y{})") - .add({{"x","foo"},{"y",""}}, 42))); - - TEST_DO(verify(TensorSpec("tensor(x[1])") - .add({{"x",0}}, 42), - "tensor(x[1],y[1],z[2])", - TensorSpec("tensor(x[1],y[1],z[2])") - .add({{"x",0},{"y",0},{"z",0}}, 42) - .add({{"x",0},{"y",0},{"z",1}}, 0))); - - TEST_DO(verify(TensorSpec("tensor(a{})") - .add({{"a","foo"}}, 42), - "tensor(a{},b[1],c{},d[2])", - TensorSpec("tensor(a{},b[1],c{},d[2])") - .add({{"a","foo"},{"b",0},{"c",""},{"d",0}}, 42) - .add({{"a","foo"},{"b",0},{"c",""},{"d",1}}, 0))); -} - -TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/eval/src/vespa/eval/eval/value_type.h b/eval/src/vespa/eval/eval/value_type.h index 81788c933d7..423c8e15d92 100644 --- a/eval/src/vespa/eval/eval/value_type.h +++ b/eval/src/vespa/eval/eval/value_type.h @@ -16,7 +16,7 @@ class ValueType { public: enum class Type { ERROR, DOUBLE, TENSOR }; - enum class CellType { FLOAT, DOUBLE }; + enum class CellType : char { FLOAT, DOUBLE }; struct Dimension { using size_type = uint32_t; static constexpr size_type npos = -1; @@ -85,4 +85,9 @@ public: std::ostream &operator<<(std::ostream &os, const ValueType &type); -} +// utility template +template <typename T> inline bool check_cell_type(ValueType::CellType type); +template <> inline bool check_cell_type<double>(ValueType::CellType type) { return (type == ValueType::CellType::DOUBLE); } +template <> inline bool check_cell_type<float>(ValueType::CellType type) { return (type == ValueType::CellType::FLOAT); } + +} // namespace diff --git a/eval/src/vespa/eval/tensor/CMakeLists.txt b/eval/src/vespa/eval/tensor/CMakeLists.txt index 4e9940d3c0a..bc0a4d340b8 100644 --- a/eval/src/vespa/eval/tensor/CMakeLists.txt +++ b/eval/src/vespa/eval/tensor/CMakeLists.txt @@ -5,6 +5,5 @@ vespa_add_library(eval_tensor OBJECT tensor.cpp tensor_address.cpp tensor_apply.cpp - tensor_mapper.cpp wrapped_simple_tensor.cpp ) diff --git a/eval/src/vespa/eval/tensor/default_tensor_engine.cpp b/eval/src/vespa/eval/tensor/default_tensor_engine.cpp index dc658d0b2da..58db90f5557 100644 --- a/eval/src/vespa/eval/tensor/default_tensor_engine.cpp +++ b/eval/src/vespa/eval/tensor/default_tensor_engine.cpp @@ -7,7 +7,7 @@ #include "sparse/sparse_tensor_address_builder.h" #include "sparse/direct_sparse_tensor_builder.h" #include "dense/dense_tensor.h" -#include "dense/direct_dense_tensor_builder.h" +#include "dense/typed_dense_tensor_builder.h" #include "dense/dense_dot_product_function.h" #include "dense/dense_xw_product_function.h" #include "dense/dense_fast_rename_optimizer.h" @@ -159,6 +159,24 @@ DefaultTensorEngine::to_spec(const Value &value) const } } +struct CallDenseTensorBuilder { + template <typename CT> + static Value::UP + call(const ValueType &type, const TensorSpec &spec) + { + TypedDenseTensorBuilder<CT> builder(type); + for (const auto &cell: spec.cells()) { + const auto &address = cell.first; + size_t cell_idx = calculate_cell_index(type, address); + if (cell_idx == UNDEFINED_IDX) { + bad_spec(spec); + } + builder.insertCell(cell_idx, cell.second); + } + return builder.build(); + } +}; + Value::UP DefaultTensorEngine::from_spec(const TensorSpec &spec) const { @@ -169,16 +187,7 @@ DefaultTensorEngine::from_spec(const TensorSpec &spec) const double value = spec.cells().empty() ? 0.0 : spec.cells().begin()->second.value; return std::make_unique<DoubleValue>(value); } else if (type.is_dense()) { - DirectDenseTensorBuilder builder(type); - for (const auto &cell: spec.cells()) { - const auto &address = cell.first; - size_t cell_idx = calculate_cell_index(type, address); - if (cell_idx == UNDEFINED_IDX) { - bad_spec(spec); - } - builder.insertCell(cell_idx, cell.second); - } - return builder.build(); + return dispatch_0<CallDenseTensorBuilder>(type.cell_type(), type, spec); } else if (type.is_sparse()) { DirectSparseTensorBuilder builder(type); SparseTensorAddressBuilder address_builder; @@ -223,7 +232,7 @@ DefaultTensorEngine::encode(const Value &value, nbostream &output) const if (auto tensor = value.as_tensor()) { TypedBinaryFormat::serialize(output, static_cast<const tensor::Tensor &>(*tensor)); } else { - TypedBinaryFormat::serialize(output, DenseTensor(ValueType::double_type(), {value.as_double()})); + TypedBinaryFormat::serialize(output, DenseTensor<double>(ValueType::double_type(), {value.as_double()})); } } @@ -357,12 +366,18 @@ size_t vector_size(const ValueType &type, const vespalib::string &dimension) { } } +struct CallAppendVector { + template <typename CT> + static void call(const ConstArrayRef<CT> &arr, double *&pos) { + for (CT cell : arr) { *pos++ = cell; } + } +}; + void append_vector(double *&pos, const Value &value) { if (auto tensor = value.as_tensor()) { const DenseTensorView *view = static_cast<const DenseTensorView *>(tensor); - for (double cell: view->cellsRef()) { - *pos++ = cell; - } + TypedCells cellsRef = view->cellsRef(); + dispatch_1<CallAppendVector>(cellsRef, pos); } else { *pos++ = value.as_double(); } @@ -375,7 +390,7 @@ const Value &concat_vectors(const Value &a, const Value &b, const vespalib::stri append_vector(pos, b); assert(pos == cells.end()); const ValueType &type = stash.create<ValueType>(ValueType::tensor_type({ValueType::Dimension(dimension, vector_size)})); - return stash.create<DenseTensorView>(type, cells); + return stash.create<DenseTensorView>(type, TypedCells(cells)); } const Value & diff --git a/eval/src/vespa/eval/tensor/dense/CMakeLists.txt b/eval/src/vespa/eval/tensor/dense/CMakeLists.txt index ce20d6ba6d9..78723fddc17 100644 --- a/eval/src/vespa/eval/tensor/dense/CMakeLists.txt +++ b/eval/src/vespa/eval/tensor/dense/CMakeLists.txt @@ -16,7 +16,8 @@ vespa_add_library(eval_tensor_dense OBJECT dense_tensor_reduce.cpp dense_tensor_view.cpp dense_xw_product_function.cpp - direct_dense_tensor_builder.cpp mutable_dense_tensor_view.cpp + typed_cells.cpp + typed_dense_tensor_builder.cpp vector_from_doubles_function.cpp ) diff --git a/eval/src/vespa/eval/tensor/dense/dense_dot_product_function.cpp b/eval/src/vespa/eval/tensor/dense/dense_dot_product_function.cpp index 988edba7d55..c925f288c4a 100644 --- a/eval/src/vespa/eval/tensor/dense/dense_dot_product_function.cpp +++ b/eval/src/vespa/eval/tensor/dense/dense_dot_product_function.cpp @@ -9,7 +9,6 @@ namespace vespalib::tensor { -using CellsRef = DenseTensorView::CellsRef; using eval::ValueType; using eval::TensorFunction; using eval::as; @@ -19,17 +18,19 @@ using namespace eval::operation; namespace { -CellsRef getCellsRef(const eval::Value &value) { +TypedCells getCellsRef(const eval::Value &value) { const DenseTensorView &denseTensor = static_cast<const DenseTensorView &>(value); return denseTensor.cellsRef(); } void my_dot_product_op(eval::InterpretedFunction::State &state, uint64_t param) { auto *hw_accelerator = (hwaccelrated::IAccelrated *)(param); - DenseTensorView::CellsRef lhsCells = getCellsRef(state.peek(1)); - DenseTensorView::CellsRef rhsCells = getCellsRef(state.peek(0)); - size_t numCells = std::min(lhsCells.size(), rhsCells.size()); - double result = hw_accelerator->dotProduct(lhsCells.cbegin(), rhsCells.cbegin(), numCells); + TypedCells lhsCells = getCellsRef(state.peek(1)); + TypedCells rhsCells = getCellsRef(state.peek(0)); + size_t numCells = std::min(lhsCells.size, rhsCells.size); + const ConstArrayRef<double> lhs = lhsCells.typify<double>(); + const ConstArrayRef<double> rhs = rhsCells.typify<double>(); + double result = hw_accelerator->dotProduct(lhs.cbegin(), rhs.cbegin(), numCells); state.pop_pop_push(state.stash.create<eval::DoubleValue>(result)); } diff --git a/eval/src/vespa/eval/tensor/dense/dense_fast_rename_optimizer.cpp b/eval/src/vespa/eval/tensor/dense/dense_fast_rename_optimizer.cpp index 09977df25b7..d8e1876ac64 100644 --- a/eval/src/vespa/eval/tensor/dense/dense_fast_rename_optimizer.cpp +++ b/eval/src/vespa/eval/tensor/dense/dense_fast_rename_optimizer.cpp @@ -9,7 +9,6 @@ namespace vespalib::tensor { -using CellsRef = DenseTensorView::CellsRef; using eval::Value; using eval::ValueType; using eval::TensorFunction; diff --git a/eval/src/vespa/eval/tensor/dense/dense_inplace_join_function.cpp b/eval/src/vespa/eval/tensor/dense/dense_inplace_join_function.cpp index ce6b1743951..5fdfdbc4e9f 100644 --- a/eval/src/vespa/eval/tensor/dense/dense_inplace_join_function.cpp +++ b/eval/src/vespa/eval/tensor/dense/dense_inplace_join_function.cpp @@ -9,7 +9,6 @@ namespace vespalib::tensor { -using CellsRef = DenseTensorView::CellsRef; using eval::Value; using eval::ValueType; using eval::TensorFunction; @@ -18,7 +17,7 @@ using namespace eval::tensor_function; namespace { -CellsRef getCellsRef(const eval::Value &value) { +TypedCells getCellsRef(const eval::Value &value) { const DenseTensorView &denseTensor = static_cast<const DenseTensorView &>(value); return denseTensor.cellsRef(); } @@ -26,8 +25,8 @@ CellsRef getCellsRef(const eval::Value &value) { template <bool write_left> void my_inplace_join_op(eval::InterpretedFunction::State &state, uint64_t param) { join_fun_t function = (join_fun_t)param; - CellsRef lhs_cells = getCellsRef(state.peek(1)); - CellsRef rhs_cells = getCellsRef(state.peek(0)); + ConstArrayRef<double> lhs_cells = getCellsRef(state.peek(1)).typify<double>(); + ConstArrayRef<double> rhs_cells = getCellsRef(state.peek(0)).typify<double>(); auto dst_cells = unconstify(write_left ? lhs_cells : rhs_cells); for (size_t i = 0; i < dst_cells.size(); ++i) { dst_cells[i] = function(lhs_cells[i], rhs_cells[i]); diff --git a/eval/src/vespa/eval/tensor/dense/dense_inplace_map_function.cpp b/eval/src/vespa/eval/tensor/dense/dense_inplace_map_function.cpp index c72889ca0ed..b38a6b175dc 100644 --- a/eval/src/vespa/eval/tensor/dense/dense_inplace_map_function.cpp +++ b/eval/src/vespa/eval/tensor/dense/dense_inplace_map_function.cpp @@ -8,7 +8,6 @@ namespace vespalib::tensor { -using CellsRef = DenseTensorView::CellsRef; using eval::Value; using eval::ValueType; using eval::TensorFunction; @@ -19,7 +18,7 @@ namespace { ArrayRef<double> getMutableCells(const eval::Value &value) { const DenseTensorView &denseTensor = static_cast<const DenseTensorView &>(value); - return unconstify(denseTensor.cellsRef()); + return unconstify(denseTensor.cellsRef().typify<double>()); } void my_inplace_map_op(eval::InterpretedFunction::State &state, uint64_t param) { diff --git a/eval/src/vespa/eval/tensor/dense/dense_replace_type_function.cpp b/eval/src/vespa/eval/tensor/dense/dense_replace_type_function.cpp index 9deac5437e0..b81b0f2c876 100644 --- a/eval/src/vespa/eval/tensor/dense/dense_replace_type_function.cpp +++ b/eval/src/vespa/eval/tensor/dense/dense_replace_type_function.cpp @@ -6,7 +6,6 @@ namespace vespalib::tensor { -using CellsRef = DenseTensorView::CellsRef; using eval::Value; using eval::ValueType; using eval::TensorFunction; @@ -15,14 +14,14 @@ using namespace eval::tensor_function; namespace { -CellsRef getCellsRef(const eval::Value &value) { +TypedCells getCellsRef(const eval::Value &value) { const DenseTensorView &denseTensor = static_cast<const DenseTensorView &>(value); return denseTensor.cellsRef(); } void my_replace_type_op(eval::InterpretedFunction::State &state, uint64_t param) { const ValueType *type = (const ValueType *)(param); - CellsRef cells = getCellsRef(state.peek(0)); + TypedCells cells = getCellsRef(state.peek(0)); state.pop_push(state.stash.create<DenseTensorView>(*type, cells)); } diff --git a/eval/src/vespa/eval/tensor/dense/dense_tensor.cpp b/eval/src/vespa/eval/tensor/dense/dense_tensor.cpp index c183e5c1db3..73cdfc12a38 100644 --- a/eval/src/vespa/eval/tensor/dense/dense_tensor.cpp +++ b/eval/src/vespa/eval/tensor/dense/dense_tensor.cpp @@ -21,68 +21,57 @@ calcCellsSize(const eval::ValueType &type) return cellsSize; } +template<typename T> void -checkCellsSize(const DenseTensor &arg) +checkCellsSize(const DenseTensor<T> &arg) { auto cellsSize = calcCellsSize(arg.fast_type()); - if (arg.cellsRef().size() != cellsSize) { + if (arg.cellsRef().size != cellsSize) { throw IllegalStateException(make_string("Wrong cell size, " "expected=%zu, " "actual=%zu", cellsSize, - arg.cellsRef().size())); + arg.cellsRef().size)); + } + if (arg.fast_type().cell_type() != arg.cellsRef().type) { + throw IllegalStateException(make_string("Wrong cell type, " + "expected=%u, " + "actual=%u", + (unsigned char)arg.fast_type().cell_type(), + (unsigned char)arg.cellsRef().type)); } } } -DenseTensor::DenseTensor() - : DenseTensorView(_type), - _type(eval::ValueType::double_type()), - _cells(1) -{ - initCellsRef(CellsRef(_cells)); -} - -DenseTensor::DenseTensor(const eval::ValueType &type_in, - const Cells &cells_in) - : DenseTensorView(_type), - _type(type_in), - _cells(cells_in) -{ - initCellsRef(CellsRef(_cells)); - checkCellsSize(*this); -} - - -DenseTensor::DenseTensor(const eval::ValueType &type_in, - Cells &&cells_in) - : DenseTensorView(_type), - _type(type_in), - _cells(std::move(cells_in)) -{ - initCellsRef(CellsRef(_cells)); - checkCellsSize(*this); -} - -DenseTensor::DenseTensor(eval::ValueType &&type_in, - Cells &&cells_in) +template <typename CT> +DenseTensor<CT>::DenseTensor(eval::ValueType type_in, + std::vector<CT> &&cells_in) : DenseTensorView(_type), _type(std::move(type_in)), _cells(std::move(cells_in)) { - initCellsRef(CellsRef(_cells)); + initCellsRef(TypedCells(_cells)); checkCellsSize(*this); } -DenseTensor::~DenseTensor() = default; +template <typename CT> +DenseTensor<CT>::~DenseTensor() = default; +template <typename CT> +template <typename RCT> bool -DenseTensor::operator==(const DenseTensor &rhs) const +DenseTensor<CT>::operator==(const DenseTensor<RCT> &rhs) const { - return (_type == rhs._type) && - (_cells == rhs._cells); + if (_type != rhs._type) return false; + if (_cells.size != rhs._cells.size) return false; + for (size_t i = 0; i < _cells.size; i++) { + if (_cells[i] != rhs._cells[i]) return false; + } + return true; } -} +template class DenseTensor<float>; +template class DenseTensor<double>; +} diff --git a/eval/src/vespa/eval/tensor/dense/dense_tensor.h b/eval/src/vespa/eval/tensor/dense/dense_tensor.h index 3795831c914..d0246fef635 100644 --- a/eval/src/vespa/eval/tensor/dense/dense_tensor.h +++ b/eval/src/vespa/eval/tensor/dense/dense_tensor.h @@ -10,20 +10,20 @@ namespace vespalib::tensor { * A dense tensor where all dimensions are indexed. * Tensor cells are stored in an underlying array according to the order of the dimensions. */ +template <typename CT> class DenseTensor : public DenseTensorView { public: - DenseTensor(); + DenseTensor() = delete; ~DenseTensor() override; - DenseTensor(const eval::ValueType &type_in, const Cells &cells_in); - DenseTensor(const eval::ValueType &type_in, Cells &&cells_in); - DenseTensor(eval::ValueType &&type_in, Cells &&cells_in); - bool operator==(const DenseTensor &rhs) const; + DenseTensor(eval::ValueType type_in, std::vector<CT> &&cells_in); + + // for unit tests + template <typename RCT> + bool operator==(const DenseTensor<RCT> &rhs) const; private: eval::ValueType _type; - Cells _cells; - + std::vector<CT> _cells; }; } - diff --git a/eval/src/vespa/eval/tensor/dense/dense_tensor_address_mapper.cpp b/eval/src/vespa/eval/tensor/dense/dense_tensor_address_mapper.cpp index 09bc546c982..c1c24d28b7f 100644 --- a/eval/src/vespa/eval/tensor/dense/dense_tensor_address_mapper.cpp +++ b/eval/src/vespa/eval/tensor/dense/dense_tensor_address_mapper.cpp @@ -25,7 +25,7 @@ DenseTensorAddressMapper::mapLabelToNumber(stringref label) } uint32_t -DenseTensorAddressMapper::mapAddressToIndex(const TensorAddress &address, const eval::ValueType type) +DenseTensorAddressMapper::mapAddressToIndex(const TensorAddress &address, const eval::ValueType &type) { uint32_t idx = 0; TensorAddressElementIterator<TensorAddress> addressIterator(address); diff --git a/eval/src/vespa/eval/tensor/dense/dense_tensor_address_mapper.h b/eval/src/vespa/eval/tensor/dense/dense_tensor_address_mapper.h index cf20fc6ad3c..7cd776a260c 100644 --- a/eval/src/vespa/eval/tensor/dense/dense_tensor_address_mapper.h +++ b/eval/src/vespa/eval/tensor/dense/dense_tensor_address_mapper.h @@ -21,7 +21,7 @@ public: static constexpr uint32_t BAD_LABEL = std::numeric_limits<uint32_t>::max(); static constexpr uint32_t BAD_ADDRESS = std::numeric_limits<uint32_t>::max(); static uint32_t mapLabelToNumber(stringref label); - static uint32_t mapAddressToIndex(const TensorAddress &address, const eval::ValueType type); + static uint32_t mapAddressToIndex(const TensorAddress &address, const eval::ValueType &type); }; } diff --git a/eval/src/vespa/eval/tensor/dense/dense_tensor_apply.h b/eval/src/vespa/eval/tensor/dense/dense_tensor_apply.h index 49e075f6999..cd524b27171 100644 --- a/eval/src/vespa/eval/tensor/dense/dense_tensor_apply.h +++ b/eval/src/vespa/eval/tensor/dense/dense_tensor_apply.h @@ -4,7 +4,6 @@ namespace vespalib::tensor { class Tensor; - class DenseTensor; } namespace vespalib::tensor::dense { @@ -17,6 +16,7 @@ namespace vespalib::tensor::dense { template <typename Function> std::unique_ptr<Tensor> apply(const DenseTensorView &lhs, const Tensor &rhs, Function &&func); + template <typename Function> std::unique_ptr<Tensor> apply(const DenseTensorView &lhs, const DenseTensorView &rhs, Function &&func); 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 e71840f392c..409e1ad087f 100644 --- a/eval/src/vespa/eval/tensor/dense/dense_tensor_apply.hpp +++ b/eval/src/vespa/eval/tensor/dense/dense_tensor_apply.hpp @@ -4,21 +4,23 @@ #include "dense_tensor_apply.h" #include "dense_dimension_combiner.h" -#include "direct_dense_tensor_builder.h" +#include "typed_dense_tensor_builder.h" namespace vespalib::tensor::dense { -template <typename Function> +template <typename LCT, typename RCT, typename OCT, typename Function> std::unique_ptr<Tensor> -apply(DenseDimensionCombiner & combiner, DirectDenseTensorBuilder & builder, - const DenseTensorView::CellsRef & lhsCells, - const DenseTensorView::CellsRef & rhsCells, Function &&func) __attribute__((noinline)); +apply(DenseDimensionCombiner & combiner, + TypedDenseTensorBuilder<OCT> & builder, + const ConstArrayRef<LCT> & lhsCells, + const ConstArrayRef<RCT> & rhsCells, Function &&func) __attribute__((noinline)); -template <typename Function> +template <typename LCT, typename RCT, typename OCT, typename Function> std::unique_ptr<Tensor> -apply(DenseDimensionCombiner & combiner, DirectDenseTensorBuilder & builder, - const DenseTensorView::CellsRef & lhsCells, - const DenseTensorView::CellsRef & rhsCells, Function &&func) +apply(DenseDimensionCombiner & combiner, + TypedDenseTensorBuilder<OCT> & builder, + const ConstArrayRef<LCT> & lhsCells, + const ConstArrayRef<RCT> & rhsCells, Function &&func) { for (combiner.leftReset(); combiner.leftInRange(); combiner.stepLeft()) { for (combiner.rightReset(); combiner.rightInRange(); combiner.stepRight()) { @@ -33,6 +35,20 @@ apply(DenseDimensionCombiner & combiner, DirectDenseTensorBuilder & builder, return builder.build(); } +struct CallApply { + template <typename LCT, typename RCT, typename Function> + static std::unique_ptr<Tensor> + call(const ConstArrayRef<LCT> & lhsArr, + const ConstArrayRef<RCT> & rhsArr, + DenseDimensionCombiner & combiner, + Function &&func) + { + using OCT = typename OutputCellType<LCT, RCT>::output_type; + TypedDenseTensorBuilder<OCT> builder(combiner.result_type); + return apply(combiner, builder, lhsArr, rhsArr, std::move(func)); + } +}; + template <typename Function> std::unique_ptr<Tensor> apply(const DenseTensorView &lhs, const Tensor &rhs, Function &&func) @@ -40,8 +56,9 @@ apply(const DenseTensorView &lhs, const Tensor &rhs, Function &&func) const DenseTensorView *view = dynamic_cast<const DenseTensorView *>(&rhs); if (view) { DenseDimensionCombiner combiner(lhs.fast_type(), view->fast_type()); - DirectDenseTensorBuilder builder(combiner.result_type); - return apply(combiner, builder, lhs.cellsRef(), view->cellsRef(), std::move(func)); + TypedCells lhsCells = lhs.cellsRef(); + TypedCells rhsCells = view->cellsRef(); + return dispatch_2<CallApply>(lhsCells, rhsCells, combiner, std::move(func)); } return Tensor::UP(); } diff --git a/eval/src/vespa/eval/tensor/dense/dense_tensor_cells_iterator.cpp b/eval/src/vespa/eval/tensor/dense/dense_tensor_cells_iterator.cpp index 15c09db44b3..55d0e29bc35 100644 --- a/eval/src/vespa/eval/tensor/dense/dense_tensor_cells_iterator.cpp +++ b/eval/src/vespa/eval/tensor/dense/dense_tensor_cells_iterator.cpp @@ -4,7 +4,7 @@ namespace vespalib::tensor { -DenseTensorCellsIterator::DenseTensorCellsIterator(const eval::ValueType &type_in, CellsRef cells) +DenseTensorCellsIterator::DenseTensorCellsIterator(const eval::ValueType &type_in, TypedCells cells) : _type(type_in), _cells(cells), _cellIdx(0), diff --git a/eval/src/vespa/eval/tensor/dense/dense_tensor_cells_iterator.h b/eval/src/vespa/eval/tensor/dense/dense_tensor_cells_iterator.h index caf92d6c8c7..8d189027be2 100644 --- a/eval/src/vespa/eval/tensor/dense/dense_tensor_cells_iterator.h +++ b/eval/src/vespa/eval/tensor/dense/dense_tensor_cells_iterator.h @@ -4,6 +4,7 @@ #include <vespa/eval/eval/value_type.h> #include <vespa/vespalib/util/arrayref.h> +#include "typed_cells.h" namespace vespalib::tensor { @@ -16,14 +17,14 @@ public: using size_type = eval::ValueType::Dimension::size_type; using Address = std::vector<size_type>; private: - using CellsRef = vespalib::ConstArrayRef<double>; + const eval::ValueType &_type; - CellsRef _cells; + TypedCells _cells; size_t _cellIdx; const int32_t _lastDimension; Address _address; public: - DenseTensorCellsIterator(const eval::ValueType &type_in, CellsRef cells); + DenseTensorCellsIterator(const eval::ValueType &type_in, TypedCells cells); ~DenseTensorCellsIterator(); void next() { ++_cellIdx; @@ -37,8 +38,8 @@ public: } } } - bool valid() const { return _cellIdx < _cells.size(); } - double cell() const { return _cells[_cellIdx]; } + bool valid() const { return _cellIdx < _cells.size; } + double cell() const { return _cells.get(_cellIdx); } const Address &address() const { return _address; } const eval::ValueType &fast_type() const { return _type; } }; diff --git a/eval/src/vespa/eval/tensor/dense/dense_tensor_modify.cpp b/eval/src/vespa/eval/tensor/dense/dense_tensor_modify.cpp index 4e2940f2516..4777abcbdef 100644 --- a/eval/src/vespa/eval/tensor/dense/dense_tensor_modify.cpp +++ b/eval/src/vespa/eval/tensor/dense/dense_tensor_modify.cpp @@ -6,28 +6,37 @@ namespace vespalib::tensor { -DenseTensorModify::DenseTensorModify(join_fun_t op, const eval::ValueType &type, Cells cells) +template <class CT> +DenseTensorModify<CT>::DenseTensorModify(join_fun_t op, const eval::ValueType &type, std::vector<CT> &&cells) : _op(op), _type(type), _cells(std::move(cells)) { + assert(vespalib::eval::check_cell_type<CT>(type.cell_type())); } - -DenseTensorModify::~DenseTensorModify() = default; +template <class CT> +DenseTensorModify<CT>::~DenseTensorModify() = default; + +template <class CT> void -DenseTensorModify::visit(const TensorAddress &address, double value) +DenseTensorModify<CT>::visit(const TensorAddress &address, double value) { uint32_t idx = DenseTensorAddressMapper::mapAddressToIndex(address, _type); if (idx != DenseTensorAddressMapper::BAD_ADDRESS) { - _cells[idx] = _op(_cells[idx], value); + double nv = _op(_cells[idx], value); + _cells[idx] = (CT) nv; } } +template <class CT> std::unique_ptr<Tensor> -DenseTensorModify::build() +DenseTensorModify<CT>::build() { - return std::make_unique<DenseTensor>(std::move(_type), std::move(_cells)); + return std::make_unique<DenseTensor<CT>>(_type, std::move(_cells)); } -} +template class DenseTensorModify<float>; +template class DenseTensorModify<double>; + +} // namespace diff --git a/eval/src/vespa/eval/tensor/dense/dense_tensor_modify.h b/eval/src/vespa/eval/tensor/dense/dense_tensor_modify.h index 848e6e559c2..3d975c8595e 100644 --- a/eval/src/vespa/eval/tensor/dense/dense_tensor_modify.h +++ b/eval/src/vespa/eval/tensor/dense/dense_tensor_modify.h @@ -12,16 +12,16 @@ namespace vespalib::tensor { * For all cells visited, a join function is applied to determine * the new cell value. */ +template <class CT> class DenseTensorModify : public TensorVisitor { using join_fun_t = Tensor::join_fun_t; - using Cells = DenseTensorView::Cells; join_fun_t _op; - eval::ValueType _type; - Cells _cells; + const eval::ValueType &_type; + std::vector<CT> _cells; public: - DenseTensorModify(join_fun_t op, const eval::ValueType &type, Cells cells); + DenseTensorModify(join_fun_t op, const eval::ValueType &type, std::vector<CT> &&cells); ~DenseTensorModify(); void visit(const TensorAddress &address, double value) override; std::unique_ptr<Tensor> build(); diff --git a/eval/src/vespa/eval/tensor/dense/dense_tensor_reduce.cpp b/eval/src/vespa/eval/tensor/dense/dense_tensor_reduce.cpp index 41643d8c266..252be199208 100644 --- a/eval/src/vespa/eval/tensor/dense/dense_tensor_reduce.cpp +++ b/eval/src/vespa/eval/tensor/dense/dense_tensor_reduce.cpp @@ -4,10 +4,8 @@ namespace vespalib::tensor::dense { -namespace { - size_t -calcCellsSize(const eval::ValueType &type) +DimensionReducer::calcCellsSize(const eval::ValueType &type) { size_t cellsSize = 1; for (const auto &dim : type.dimensions()) { @@ -16,11 +14,9 @@ calcCellsSize(const eval::ValueType &type) return cellsSize; } -} DimensionReducer::DimensionReducer(const eval::ValueType &oldType, const string &dimensionToRemove) : _type(oldType.reduce({ dimensionToRemove })), - _cellsResult(calcCellsSize(_type)), _innerDimSize(1), _sumDimSize(1), _outerDimSize(1) diff --git a/eval/src/vespa/eval/tensor/dense/dense_tensor_reduce.hpp b/eval/src/vespa/eval/tensor/dense/dense_tensor_reduce.hpp index 98db89dd2a7..5673a35c7e6 100644 --- a/eval/src/vespa/eval/tensor/dense/dense_tensor_reduce.hpp +++ b/eval/src/vespa/eval/tensor/dense/dense_tensor_reduce.hpp @@ -6,29 +6,27 @@ namespace vespalib::tensor::dense { -using Cells = DenseTensorView::Cells; -using CellsRef = DenseTensorView::CellsRef; - class DimensionReducer { private: eval::ValueType _type; - Cells _cellsResult; size_t _innerDimSize; size_t _sumDimSize; size_t _outerDimSize; + static size_t calcCellsSize(const eval::ValueType &type); void setup(const eval::ValueType &oldType, const vespalib::string &dimensionToRemove); - public: DimensionReducer(const eval::ValueType &oldType, const string &dimensionToRemove); ~DimensionReducer(); - template <typename Function> + template <typename T, typename Function> std::unique_ptr<DenseTensorView> - reduceCells(CellsRef cellsIn, Function &&func) { + reduceCells(ConstArrayRef<T> cellsIn, Function &&func) { + size_t resultSize = calcCellsSize(_type); + std::vector<T> cellsOut(resultSize); auto itr_in = cellsIn.cbegin(); - auto itr_out = _cellsResult.begin(); + auto itr_out = cellsOut.begin(); for (size_t outerDim = 0; outerDim < _outerDimSize; ++outerDim) { auto saved_itr = itr_out; for (size_t innerDim = 0; innerDim < _innerDimSize; ++innerDim) { @@ -45,20 +43,47 @@ public: } } } - assert(itr_out == _cellsResult.end()); + assert(itr_out == cellsOut.end()); assert(itr_in == cellsIn.cend()); - return std::make_unique<DenseTensor>(std::move(_type), std::move(_cellsResult)); + return std::make_unique<DenseTensor<T>>(std::move(_type), std::move(cellsOut)); } }; namespace { +struct CallReduceCells { + template <typename CT, typename Function> + static std::unique_ptr<DenseTensorView> + call(const ConstArrayRef<CT> &oldCells, DimensionReducer &reducer, Function &&func) { + return reducer.reduceCells(oldCells, func); + } + + template <typename CT, typename Function> + static double + call(const ConstArrayRef<CT> &oldCells, Function &&func) { + assert(oldCells.size() > 0); + double result = oldCells[0]; + for (size_t i = 1; i < oldCells.size(); ++i) { + result = func(result, oldCells[i]); + } + return result; + } +}; + template <typename Function> std::unique_ptr<DenseTensorView> reduce(const DenseTensorView &tensor, const vespalib::string &dimensionToRemove, Function &&func) { DimensionReducer reducer(tensor.fast_type(), dimensionToRemove); - return reducer.reduceCells(tensor.cellsRef(), func); + TypedCells oldCells = tensor.cellsRef(); + return dispatch_1<CallReduceCells>(oldCells, reducer, func); +} + +template <typename Function> +double +reduce_all_dimensions(TypedCells oldCells, Function &&func) +{ + return dispatch_1<CallReduceCells>(oldCells, func); } } @@ -67,18 +92,21 @@ template <typename Function> std::unique_ptr<Tensor> reduce(const DenseTensorView &tensor, const std::vector<vespalib::string> &dimensions, Function &&func) { - if (dimensions.size() == 1) { - return reduce(tensor, dimensions[0], func); - } else if (dimensions.size() > 0) { - std::unique_ptr<DenseTensorView> result = reduce(tensor, dimensions[0], func); - for (size_t i = 1; i < dimensions.size(); ++i) { - std::unique_ptr<DenseTensorView> tmpResult = reduce(*result, dimensions[i], func); - result = std::move(tmpResult); - } - return result; - } else { - return std::unique_ptr<Tensor>(); + if ((dimensions.size() == 0) || + (dimensions.size() == tensor.fast_type().dimensions().size())) + { + eval::ValueType newType = tensor.fast_type().reduce(dimensions); + assert(newType.is_double()); + double result = reduce_all_dimensions(tensor.cellsRef(), func); + std::vector<double> newCells({result}); + return std::make_unique<DenseTensor<double>>(std::move(newType), std::move(newCells)); + } + std::unique_ptr<DenseTensorView> result = reduce(tensor, dimensions[0], func); + for (size_t i = 1; i < dimensions.size(); ++i) { + std::unique_ptr<DenseTensorView> tmpResult = reduce(*result, dimensions[i], func); + result = std::move(tmpResult); } + return result; } } diff --git a/eval/src/vespa/eval/tensor/dense/dense_tensor_view.cpp b/eval/src/vespa/eval/tensor/dense/dense_tensor_view.cpp index 73b2e7b3ffb..1ae198d1171 100644 --- a/eval/src/vespa/eval/tensor/dense/dense_tensor_view.cpp +++ b/eval/src/vespa/eval/tensor/dense/dense_tensor_view.cpp @@ -54,12 +54,12 @@ void checkCellsSize(const DenseTensorView &arg) { auto cellsSize = calcCellsSize(arg.fast_type()); - if (arg.cellsRef().size() != cellsSize) { + if (arg.cellsRef().size != cellsSize) { throw IllegalStateException(make_string("wrong cell size, " "expected=%zu, " "actual=%zu", cellsSize, - arg.cellsRef().size())); + arg.cellsRef().size)); } } @@ -67,7 +67,7 @@ void checkDimensions(const DenseTensorView &lhs, const DenseTensorView &rhs, vespalib::stringref operation) { - if (lhs.fast_type() != rhs.fast_type()) { + if (lhs.fast_type().dimensions() != rhs.fast_type().dimensions()) { throw IllegalStateException(make_string("mismatching dimensions for " "dense tensor %s, " "lhs dimensions = '%s', " @@ -87,24 +87,52 @@ checkDimensions(const DenseTensorView &lhs, const DenseTensorView &rhs, * The given function is used to calculate the resulting cell value * for overlapping cells. */ +template <typename LCT, typename RCT, typename Function> +static Tensor::UP +sameShapeJoin(const ConstArrayRef<LCT> &lhs, const ConstArrayRef<RCT> &rhs, + const eval::ValueType &lhs_type, + Function &&func) +{ + size_t sz = lhs.size(); + assert(sz == rhs.size()); + using OutputSelector = OutputCellType<LCT, RCT>; + using OCT = typename OutputSelector::output_type; + std::vector<OCT> newCells; + newCells.reserve(sz); + auto rhsCellItr = rhs.cbegin(); + for (const auto &lhsCell : lhs) { + OCT v = func(lhsCell, *rhsCellItr); + newCells.push_back(v); + ++rhsCellItr; + } + assert(rhsCellItr == rhs.cend()); + assert(newCells.size() == sz); + auto newType = eval::ValueType::tensor_type(lhs_type.dimensions(), OutputSelector::output_cell_type()); + return std::make_unique<DenseTensor<OCT>>(std::move(newType), std::move(newCells)); +} + +struct CallJoin +{ + template <typename LCT, typename RCT, typename Function> + static Tensor::UP + call(const ConstArrayRef<LCT> &lhs, const ConstArrayRef<RCT> &rhs, + const eval::ValueType &lhs_type, + Function &&func) + { + return sameShapeJoin(lhs, rhs, lhs_type, std::move(func)); + } +}; + template <typename Function> Tensor::UP joinDenseTensors(const DenseTensorView &lhs, const DenseTensorView &rhs, Function &&func) { - DenseTensor::Cells cells; - cells.reserve(lhs.cellsRef().size()); - auto rhsCellItr = rhs.cellsRef().cbegin(); - for (const auto &lhsCell : lhs.cellsRef()) { - cells.push_back(func(lhsCell, *rhsCellItr)); - ++rhsCellItr; - } - assert(rhsCellItr == rhs.cellsRef().cend()); - return std::make_unique<DenseTensor>(lhs.fast_type(), - std::move(cells)); + TypedCells lhsCells = lhs.cellsRef(); + TypedCells rhsCells = rhs.cellsRef(); + return dispatch_2<CallJoin>(lhsCells, rhsCells, lhs.fast_type(), std::move(func)); } - template <typename Function> Tensor::UP joinDenseTensors(const DenseTensorView &lhs, const Tensor &rhs, @@ -119,13 +147,13 @@ joinDenseTensors(const DenseTensorView &lhs, const Tensor &rhs, return Tensor::UP(); } -bool sameCells(DenseTensorView::CellsRef lhs, DenseTensorView::CellsRef rhs) +bool sameCells(TypedCells lhs, TypedCells rhs) { - if (lhs.size() != rhs.size()) { + if (lhs.size != rhs.size) { return false; } - for (size_t i = 0; i < lhs.size(); ++i) { - if (lhs[i] != rhs[i]) { + for (size_t i = 0; i < lhs.size; ++i) { + if (lhs.get(i) != rhs.get(i)) { return false; } } @@ -146,27 +174,43 @@ DenseTensorView::type() const return _typeRef; } +struct CallSum { + template <typename CT> + static double + call(const ConstArrayRef<CT> &arr) { + double res = 0.0; + for (CT val : arr) { + res += val; + } + return res; + } +}; + double DenseTensorView::as_double() const { - double result = 0.0; - for (const auto &cell : _cellsRef) { - result += cell; - } - return result; + return dispatch_1<CallSum>(_cellsRef); } +struct CallApply { + template <typename CT> + static Tensor::UP + call(const ConstArrayRef<CT> &oldCells, const eval::ValueType &newType, const CellFunction &func) + { + std::vector<CT> newCells; + newCells.reserve(oldCells.size()); + for (const auto &cell : oldCells) { + CT nv = func.apply(cell); + newCells.push_back(nv); + } + return std::make_unique<DenseTensor<CT>>(newType, std::move(newCells)); + } +}; + Tensor::UP DenseTensorView::apply(const CellFunction &func) const { - Cells newCells(_cellsRef.size()); - auto itr = newCells.begin(); - for (const auto &cell : _cellsRef) { - *itr = func.apply(cell); - ++itr; - } - assert(itr == newCells.end()); - return std::make_unique<DenseTensor>(_typeRef, std::move(newCells)); + return dispatch_1<CallApply>(_cellsRef, _typeRef, func); } bool @@ -179,11 +223,20 @@ DenseTensorView::equals(const Tensor &arg) const return false; } +struct CallClone { + template<class CT> + static Tensor::UP + call(const ConstArrayRef<CT> &cells, eval::ValueType newType) + { + std::vector<CT> newCells(cells.begin(), cells.end()); + return std::make_unique<DenseTensor<CT>>(std::move(newType), std::move(newCells)); + } +}; + Tensor::UP DenseTensorView::clone() const { - return std::make_unique<DenseTensor>(_typeRef, - Cells(_cellsRef.cbegin(), _cellsRef.cend())); + return dispatch_1<CallClone>(_cellsRef, _typeRef); } namespace { @@ -277,12 +330,25 @@ DenseTensorView::reduce(join_fun_t op, const std::vector<vespalib::string> &dime : reduce_all(op, dimensions); } +struct CallModify +{ + using join_fun_t = DenseTensorView::join_fun_t; + + template <typename CT> + static std::unique_ptr<Tensor> + call(const ConstArrayRef<CT> &arr, join_fun_t op, const eval::ValueType &typeRef, const CellValues &cellValues) + { + std::vector newCells(arr.begin(), arr.end()); + DenseTensorModify<CT> modifier(op, typeRef, std::move(newCells)); + cellValues.accept(modifier); + return modifier.build(); + } +}; + std::unique_ptr<Tensor> DenseTensorView::modify(join_fun_t op, const CellValues &cellValues) const { - DenseTensorModify modifier(op, _typeRef, Cells(_cellsRef.cbegin(), _cellsRef.cend())); - cellValues.accept(modifier); - return modifier.build(); + return dispatch_1<CallModify>(_cellsRef, op, _typeRef, cellValues); } std::unique_ptr<Tensor> diff --git a/eval/src/vespa/eval/tensor/dense/dense_tensor_view.h b/eval/src/vespa/eval/tensor/dense/dense_tensor_view.h index 09b6b72375e..60f85c38659 100644 --- a/eval/src/vespa/eval/tensor/dense/dense_tensor_view.h +++ b/eval/src/vespa/eval/tensor/dense/dense_tensor_view.h @@ -2,6 +2,7 @@ #pragma once +#include "typed_cells.h" #include "dense_tensor_cells_iterator.h" #include <vespa/eval/tensor/tensor.h> @@ -14,12 +15,10 @@ namespace vespalib::tensor { class DenseTensorView : public Tensor { public: - using Cells = std::vector<double>; - using CellsRef = ConstArrayRef<double>; using CellsIterator = DenseTensorCellsIterator; using Address = std::vector<eval::ValueType::Dimension::size_type>; - DenseTensorView(const eval::ValueType &type_in, CellsRef cells_in) + DenseTensorView(const eval::ValueType &type_in, TypedCells cells_in) : _typeRef(type_in), _cellsRef(cells_in) {} @@ -29,7 +28,7 @@ public: {} const eval::ValueType &fast_type() const { return _typeRef; } - const CellsRef &cellsRef() const { return _cellsRef; } + const TypedCells &cellsRef() const { return _cellsRef; } bool operator==(const DenseTensorView &rhs) const; CellsIterator cellsIterator() const { return CellsIterator(_typeRef, _cellsRef); } @@ -46,14 +45,14 @@ public: eval::TensorSpec toSpec() const override; void accept(TensorVisitor &visitor) const override; protected: - void initCellsRef(CellsRef cells_in) { + void initCellsRef(TypedCells cells_in) { _cellsRef = cells_in; } private: Tensor::UP reduce_all(join_fun_t op, const std::vector<vespalib::string> &dimensions) const; const eval::ValueType &_typeRef; - CellsRef _cellsRef; + TypedCells _cellsRef; }; } diff --git a/eval/src/vespa/eval/tensor/dense/dense_xw_product_function.cpp b/eval/src/vespa/eval/tensor/dense/dense_xw_product_function.cpp index a3056311fab..b6ac87ce012 100644 --- a/eval/src/vespa/eval/tensor/dense/dense_xw_product_function.cpp +++ b/eval/src/vespa/eval/tensor/dense/dense_xw_product_function.cpp @@ -12,7 +12,6 @@ namespace vespalib::tensor { -using CellsRef = DenseTensorView::CellsRef; using eval::ValueType; using eval::TensorFunction; using eval::as; @@ -22,9 +21,11 @@ using namespace eval::operation; namespace { -CellsRef getCellsRef(const eval::Value &value) { +XWInput getCellsRef(const eval::Value &value) { const DenseTensorView &denseTensor = static_cast<const DenseTensorView &>(value); - return denseTensor.cellsRef(); + TypedCells ref = denseTensor.cellsRef(); + assert(ref.type == CellType::DOUBLE); + return ref.typify<double>(); } void multiDotProduct(const DenseXWProductFunction::Self &self, @@ -62,8 +63,8 @@ template <bool commonDimensionInnermost> void my_xw_product_op(eval::InterpretedFunction::State &state, uint64_t param) { DenseXWProductFunction::Self *self = (DenseXWProductFunction::Self *)(param); - CellsRef vectorCells = getCellsRef(state.peek(1)); - CellsRef matrixCells = getCellsRef(state.peek(0)); + XWInput vectorCells = getCellsRef(state.peek(1)); + XWInput matrixCells = getCellsRef(state.peek(0)); ArrayRef<double> outputCells = state.stash.create_array<double>(self->_resultSize); @@ -72,7 +73,7 @@ void my_xw_product_op(eval::InterpretedFunction::State &state, uint64_t param) { } else { transposedProduct(*self, vectorCells, matrixCells, outputCells); } - state.pop_pop_push(state.stash.create<DenseTensorView>(self->_resultType, outputCells)); + state.pop_pop_push(state.stash.create<DenseTensorView>(self->_resultType, TypedCells(outputCells))); } bool isConcreteDenseTensor(const ValueType &type, size_t d) { diff --git a/eval/src/vespa/eval/tensor/dense/dense_xw_product_function.h b/eval/src/vespa/eval/tensor/dense/dense_xw_product_function.h index a8ccdd331cf..9f1bc12b110 100644 --- a/eval/src/vespa/eval/tensor/dense/dense_xw_product_function.h +++ b/eval/src/vespa/eval/tensor/dense/dense_xw_product_function.h @@ -8,7 +8,7 @@ namespace vespalib::tensor { -using XWInput = DenseTensorView::CellsRef; +using XWInput = ConstArrayRef<double>; using XWOutput = ArrayRef<double>; /** diff --git a/eval/src/vespa/eval/tensor/dense/direct_dense_tensor_builder.cpp b/eval/src/vespa/eval/tensor/dense/direct_dense_tensor_builder.cpp deleted file mode 100644 index f800750bf8f..00000000000 --- a/eval/src/vespa/eval/tensor/dense/direct_dense_tensor_builder.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - -#include "direct_dense_tensor_builder.h" - -namespace vespalib::tensor { - -using Address = DirectDenseTensorBuilder::Address; -using eval::ValueType; - -namespace { - -size_t -calculateCellsSize(const ValueType &type) -{ - size_t cellsSize = 1; - for (const auto &dim : type.dimensions()) { - cellsSize *= dim.size; - } - return cellsSize; -} - -} - -DirectDenseTensorBuilder::~DirectDenseTensorBuilder() = default; - -DirectDenseTensorBuilder::DirectDenseTensorBuilder(const ValueType &type_in) - : _type(type_in), - _cells(calculateCellsSize(_type)) -{ -} - -Tensor::UP -DirectDenseTensorBuilder::build() -{ - return std::make_unique<DenseTensor>(std::move(_type), std::move(_cells)); -} - -} - diff --git a/eval/src/vespa/eval/tensor/dense/mutable_dense_tensor_view.cpp b/eval/src/vespa/eval/tensor/dense/mutable_dense_tensor_view.cpp index 79a8b994480..08abc391179 100644 --- a/eval/src/vespa/eval/tensor/dense/mutable_dense_tensor_view.cpp +++ b/eval/src/vespa/eval/tensor/dense/mutable_dense_tensor_view.cpp @@ -21,12 +21,12 @@ MutableDenseTensorView::MutableValueType::MutableValueType(ValueType type_in) MutableDenseTensorView::MutableValueType::~MutableValueType() = default; MutableDenseTensorView::MutableDenseTensorView(ValueType type_in) - : DenseTensorView(_concreteType._type, CellsRef()), + : DenseTensorView(_concreteType._type), _concreteType(type_in) { } -MutableDenseTensorView::MutableDenseTensorView(ValueType type_in, CellsRef cells_in) +MutableDenseTensorView::MutableDenseTensorView(ValueType type_in, TypedCells cells_in) : DenseTensorView(_concreteType._type, cells_in), _concreteType(type_in) { diff --git a/eval/src/vespa/eval/tensor/dense/mutable_dense_tensor_view.h b/eval/src/vespa/eval/tensor/dense/mutable_dense_tensor_view.h index 260e71b6f76..d71903d6c47 100644 --- a/eval/src/vespa/eval/tensor/dense/mutable_dense_tensor_view.h +++ b/eval/src/vespa/eval/tensor/dense/mutable_dense_tensor_view.h @@ -42,8 +42,8 @@ private: public: MutableDenseTensorView(eval::ValueType type_in); - MutableDenseTensorView(eval::ValueType type_in, CellsRef cells_in); - void setCells(CellsRef cells_in) { + MutableDenseTensorView(eval::ValueType type_in, TypedCells cells_in); + void setCells(TypedCells cells_in) { initCellsRef(cells_in); } void setUnboundDimensions(const uint32_t *unboundDimSizeBegin, const uint32_t *unboundDimSizeEnd) { diff --git a/eval/src/vespa/eval/tensor/dense/typed_cells.cpp b/eval/src/vespa/eval/tensor/dense/typed_cells.cpp new file mode 100644 index 00000000000..e56325bffd2 --- /dev/null +++ b/eval/src/vespa/eval/tensor/dense/typed_cells.cpp @@ -0,0 +1,7 @@ +// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include "typed_cells.h" + +namespace vespalib::tensor { + +} // namespace diff --git a/eval/src/vespa/eval/tensor/dense/typed_cells.h b/eval/src/vespa/eval/tensor/dense/typed_cells.h new file mode 100644 index 00000000000..d1b6058bfbe --- /dev/null +++ b/eval/src/vespa/eval/tensor/dense/typed_cells.h @@ -0,0 +1,96 @@ +// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#pragma once + +#include <assert.h> +#include <vespa/vespalib/util/arrayref.h> +#include <vespa/eval/eval/value_type.h> + +namespace vespalib::tensor { + +// Low-level typed cells reference + +using CellType = vespalib::eval::ValueType::CellType; + + +template<typename LCT, typename RCT> struct OutputCellType; +template<> struct OutputCellType<double, double> { + typedef double output_type; + static constexpr CellType output_cell_type() { return CellType::DOUBLE; }; +}; +template<> struct OutputCellType<float, double> { + typedef double output_type; + static constexpr CellType output_cell_type() { return CellType::DOUBLE; }; +}; +template<> struct OutputCellType<double, float> { + typedef double output_type; + static constexpr CellType output_cell_type() { return CellType::DOUBLE; }; +}; +template<> struct OutputCellType<float, float> { + typedef float output_type; + static constexpr CellType output_cell_type() { return CellType::FLOAT; }; +}; + +struct TypedCells { + const void *data; + CellType type; + size_t size:56; + + explicit TypedCells(ConstArrayRef<double> cells) : data(cells.begin()), type(CellType::DOUBLE), size(cells.size()) {} + explicit TypedCells(ConstArrayRef<float> cells) : data(cells.begin()), type(CellType::FLOAT), size(cells.size()) {} + + TypedCells() : data(nullptr), type(CellType::DOUBLE), size(0) {} + TypedCells(const void *dp, CellType ct, size_t sz) : data(dp), type(ct), size(sz) {} + + template <typename T> bool check_type() const { return vespalib::eval::check_cell_type<T>(type); } + template <typename T> ConstArrayRef<T> typify() const { + assert(check_type<T>()); + return ConstArrayRef<T>((const T *)data, size); + } + template <typename T> ConstArrayRef<T> unsafe_typify() const { + return ConstArrayRef<T>((const T *)data, size); + } + + double get(size_t idx) const { + if (type == CellType::DOUBLE) { + const double *p = (const double *)data; + return p[idx]; + } + if (type == CellType::FLOAT) { + const float *p = (const float *)data; + return p[idx]; + } + abort(); + } + + TypedCells & operator= (const TypedCells &other) = default; +}; + +template <typename TGT, typename... Args> +auto dispatch_0(CellType ct, Args &&...args) { + switch (ct) { + case CellType::DOUBLE: return TGT::template call<double>(std::forward<Args>(args)...); + case CellType::FLOAT: return TGT::template call<float>(std::forward<Args>(args)...); + } + abort(); +} + +template <typename TGT, typename... Args> +auto dispatch_1(const TypedCells &a, Args &&...args) { + switch (a.type) { + case CellType::DOUBLE: return TGT::call(a.unsafe_typify<double>(), std::forward<Args>(args)...); + case CellType::FLOAT: return TGT::call(a.unsafe_typify<float>(), std::forward<Args>(args)...); + } + abort(); +} + +template <typename TGT, typename A1, typename... Args> +auto dispatch_2(A1 &&a, const TypedCells &b, Args &&...args) { + switch (b.type) { + case CellType::DOUBLE: return dispatch_1<TGT>(std::forward<A1>(a), b.unsafe_typify<double>(), std::forward<Args>(args)...); + case CellType::FLOAT: return dispatch_1<TGT>(std::forward<A1>(a), b.unsafe_typify<float>(), std::forward<Args>(args)...); + } + abort(); +} + +} // namespace diff --git a/eval/src/vespa/eval/tensor/dense/typed_dense_tensor_builder.cpp b/eval/src/vespa/eval/tensor/dense/typed_dense_tensor_builder.cpp new file mode 100644 index 00000000000..3e7d234fd54 --- /dev/null +++ b/eval/src/vespa/eval/tensor/dense/typed_dense_tensor_builder.cpp @@ -0,0 +1,45 @@ + + +#include "typed_dense_tensor_builder.h" + +namespace vespalib::tensor { + +using Address = DenseTensorView::Address; +using eval::ValueType; + +namespace { + +size_t +calculateCellsSize(const ValueType &type) +{ + size_t cellsSize = 1; + for (const auto &dim : type.dimensions()) { + cellsSize *= dim.size; + } + return cellsSize; +} + +} // namespace + +template <typename CT> +TypedDenseTensorBuilder<CT>::~TypedDenseTensorBuilder() = default; + +template <typename CT> +TypedDenseTensorBuilder<CT>::TypedDenseTensorBuilder(const ValueType &type_in) + : _type(type_in), + _cells(calculateCellsSize(_type)) +{ + assert(vespalib::eval::check_cell_type<CT>(_type.cell_type())); +} + +template <typename CT> +Tensor::UP +TypedDenseTensorBuilder<CT>::build() +{ + return std::make_unique<DenseTensor<CT>>(std::move(_type), std::move(_cells)); +} + +template class TypedDenseTensorBuilder<double>; +template class TypedDenseTensorBuilder<float>; + +} // namespace diff --git a/eval/src/vespa/eval/tensor/dense/direct_dense_tensor_builder.h b/eval/src/vespa/eval/tensor/dense/typed_dense_tensor_builder.h index 935b5c20373..770ea4ae5ea 100644 --- a/eval/src/vespa/eval/tensor/dense/direct_dense_tensor_builder.h +++ b/eval/src/vespa/eval/tensor/dense/typed_dense_tensor_builder.h @@ -9,15 +9,14 @@ namespace vespalib::tensor { /** * Class for building a dense tensor by inserting cell values directly into underlying array of cells. */ -class DirectDenseTensorBuilder +template <typename CT> +class TypedDenseTensorBuilder { public: - using Cells = DenseTensor::Cells; - using Address = DenseTensor::Address; - + using Address = DenseTensorView::Address; private: eval::ValueType _type; - Cells _cells; + std::vector<CT> _cells; static size_t calculateCellAddress(const Address &address, const eval::ValueType &type) { size_t result = 0; @@ -28,16 +27,15 @@ private: return result; } public: - DirectDenseTensorBuilder(const eval::ValueType &type_in); - ~DirectDenseTensorBuilder(); - void insertCell(const Address &address, double cellValue) { + TypedDenseTensorBuilder(const eval::ValueType &type_in); + ~TypedDenseTensorBuilder(); + void insertCell(const Address &address, CT cellValue) { insertCell(calculateCellAddress(address, _type), cellValue); } - void insertCell(size_t index, double cellValue) { + void insertCell(size_t index, CT cellValue) { _cells[index] = cellValue; } Tensor::UP build(); }; } - diff --git a/eval/src/vespa/eval/tensor/dense/vector_from_doubles_function.cpp b/eval/src/vespa/eval/tensor/dense/vector_from_doubles_function.cpp index 445b08ab114..4694aea717d 100644 --- a/eval/src/vespa/eval/tensor/dense/vector_from_doubles_function.cpp +++ b/eval/src/vespa/eval/tensor/dense/vector_from_doubles_function.cpp @@ -9,7 +9,6 @@ namespace vespalib::tensor { -using CellsRef = DenseTensorView::CellsRef; using eval::Value; using eval::ValueType; using eval::TensorFunction; @@ -20,14 +19,25 @@ using namespace eval::operation; namespace { +struct CallVectorFromDoubles { + template <typename CT> + static TypedCells + call(eval::InterpretedFunction::State &state, size_t numCells) { + ArrayRef<CT> outputCells = state.stash.create_array<CT>(numCells); + for (size_t i = numCells; i-- > 0; ) { + outputCells[i] = (CT) state.peek(0).as_double(); + state.stack.pop_back(); + } + return TypedCells(outputCells); + } +}; + void my_vector_from_doubles_op(eval::InterpretedFunction::State &state, uint64_t param) { const auto *self = (const VectorFromDoublesFunction::Self *)(param); - ArrayRef<double> outputCells = state.stash.create_array<double>(self->resultSize); - for (size_t i = self->resultSize; i-- > 0; ) { - outputCells[i] = state.peek(0).as_double(); - state.stack.pop_back(); - } - const Value &result = state.stash.create<DenseTensorView>(self->resultType, outputCells); + CellType ct = self->resultType.cell_type(); + size_t numCells = self->resultSize; + TypedCells cells = dispatch_0<CallVectorFromDoubles>(ct, state, numCells); + const Value &result = state.stash.create<DenseTensorView>(self->resultType, cells); state.stack.push_back(result); } diff --git a/eval/src/vespa/eval/tensor/serialization/dense_binary_format.cpp b/eval/src/vespa/eval/tensor/serialization/dense_binary_format.cpp index 677fb40b0f4..493e4af3caf 100644 --- a/eval/src/vespa/eval/tensor/serialization/dense_binary_format.cpp +++ b/eval/src/vespa/eval/tensor/serialization/dense_binary_format.cpp @@ -29,9 +29,10 @@ size_t encodeDimensions(nbostream &stream, const eval::ValueType & type) { } template<typename T> -void encodeCells(nbostream &stream, DenseTensorView::CellsRef cells) { - for (const auto &value : cells) { - stream << static_cast<T>(value); +void encodeCells(nbostream &stream, TypedCells cells) { + auto arr = cells.typify<T>(); + for (const auto &value : arr) { + stream << value; } } @@ -76,8 +77,8 @@ void DenseBinaryFormat::serialize(nbostream &stream, const DenseTensorView &tensor) { size_t cellsSize = encodeDimensions(stream, tensor.fast_type()); - DenseTensorView::CellsRef cells = tensor.cellsRef(); - assert(cells.size() == cellsSize); + TypedCells cells = tensor.cellsRef(); + assert(cells.size == cellsSize); switch (tensor.fast_type().cell_type()) { case CellType::DOUBLE: encodeCells<double>(stream, cells); @@ -88,15 +89,24 @@ DenseBinaryFormat::serialize(nbostream &stream, const DenseTensorView &tensor) } } -std::unique_ptr<DenseTensor> +struct CallDecodeCells { + template <typename CT> + static std::unique_ptr<DenseTensorView> + call(nbostream &stream, size_t numCells, ValueType &&newType) { + std::vector<CT> newCells; + newCells.reserve(numCells); + decodeCells<CT>(stream, numCells, newCells); + return std::make_unique<DenseTensor<CT>>(std::move(newType), std::move(newCells)); + } +}; + +std::unique_ptr<DenseTensorView> DenseBinaryFormat::deserialize(nbostream &stream, CellType cell_type) { std::vector<Dimension> dimensions; - size_t cellsSize = decodeDimensions(stream,dimensions); - DenseTensor::Cells cells; - cells.reserve(cellsSize); - decodeCells(cell_type, stream, cellsSize, cells); - return std::make_unique<DenseTensor>(ValueType::tensor_type(std::move(dimensions), cell_type), std::move(cells)); + size_t numCells = decodeDimensions(stream, dimensions); + ValueType newType = ValueType::tensor_type(std::move(dimensions), cell_type); + return dispatch_0<CallDecodeCells>(cell_type, stream, numCells, std::move(newType)); } template <typename T> @@ -104,7 +114,7 @@ void DenseBinaryFormat::deserializeCellsOnly(nbostream &stream, std::vector<T> &cells, CellType cell_type) { std::vector<Dimension> dimensions; - size_t cellsSize = decodeDimensions(stream,dimensions); + size_t cellsSize = decodeDimensions(stream, dimensions); cells.clear(); cells.reserve(cellsSize); decodeCells(cell_type, stream, cellsSize, cells); diff --git a/eval/src/vespa/eval/tensor/serialization/dense_binary_format.h b/eval/src/vespa/eval/tensor/serialization/dense_binary_format.h index 9e860b3c1e4..21618dcb6ce 100644 --- a/eval/src/vespa/eval/tensor/serialization/dense_binary_format.h +++ b/eval/src/vespa/eval/tensor/serialization/dense_binary_format.h @@ -10,7 +10,6 @@ namespace vespalib { class nbostream; } namespace vespalib::tensor { -class DenseTensor; class DenseTensorView; /** @@ -22,7 +21,7 @@ public: using CellType = eval::ValueType::CellType; static void serialize(nbostream &stream, const DenseTensorView &tensor); - static std::unique_ptr<DenseTensor> deserialize(nbostream &stream, CellType cell_type); + static std::unique_ptr<DenseTensorView> deserialize(nbostream &stream, CellType cell_type); // This is a temporary method untill we get full support for typed tensors template <typename T> diff --git a/eval/src/vespa/eval/tensor/tensor_mapper.cpp b/eval/src/vespa/eval/tensor/tensor_mapper.cpp deleted file mode 100644 index 6f2b094af9e..00000000000 --- a/eval/src/vespa/eval/tensor/tensor_mapper.cpp +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - -#include "tensor_mapper.h" -#include "tensor.h" -#include "tensor_visitor.h" -#include "tensor_address_element_iterator.h" -#include "wrapped_simple_tensor.h" -#include <vespa/eval/tensor/sparse/direct_sparse_tensor_builder.h> -#include <vespa/eval/tensor/dense/dense_tensor.h> -#include <vespa/eval/tensor/dense/dense_tensor_address_mapper.h> -#include <vespa/vespalib/stllike/hash_map.hpp> -#include <limits> - -using vespalib::eval::ValueType; -using vespalib::eval::TensorSpec; - -namespace vespalib::tensor { - -namespace { - -//----------------------------------------------------------------------------- - -template <class TensorT> -class SparseTensorMapper : public TensorVisitor -{ - using Builder = DirectSparseTensorBuilder; - using AddressBuilderType = typename Builder::AddressBuilderType; - - Builder _builder; - AddressBuilderType _addressBuilder; - - void mapAddress(const TensorAddress &address); - virtual void visit(const TensorAddress &address, double value) override; - - SparseTensorMapper(const ValueType &type); - - ~SparseTensorMapper(); - - std::unique_ptr<Tensor> build(); -public: - static std::unique_ptr<Tensor> - map(const Tensor &tensor, const ValueType &type); -}; - -template <class TensorT> -SparseTensorMapper<TensorT>:: -SparseTensorMapper(const ValueType &type) - : TensorVisitor(), - _builder(type), - _addressBuilder() -{ -} - -template <class TensorT> -SparseTensorMapper<TensorT>::~SparseTensorMapper() = default; - -template <class TensorT> -std::unique_ptr<Tensor> -SparseTensorMapper<TensorT>::build() -{ - return _builder.build(); -} - -template <> -void -SparseTensorMapper<SparseTensor>:: -mapAddress(const TensorAddress &address) -{ - _addressBuilder.clear(); - TensorAddressElementIterator<TensorAddress> addressIterator(address); - for (const auto &dimension : _builder.fast_type().dimensions()) { - if (addressIterator.skipToDimension(dimension.name)) { - _addressBuilder.add(addressIterator.label()); - addressIterator.next(); - } else { - // output dimension not in input - _addressBuilder.addUndefined(); - } - } -} - -template <class TensorT> -void -SparseTensorMapper<TensorT>::visit(const TensorAddress &address, double value) -{ - mapAddress(address); - _builder.insertCell(_addressBuilder, value, - [](double oldValue, double newValue) - { return oldValue + newValue; }); -} - -template <class TensorT> -std::unique_ptr<Tensor> -SparseTensorMapper<TensorT>::map(const Tensor &tensor, - const ValueType &type) -{ - SparseTensorMapper<TensorT> mapper(type); - tensor.accept(mapper); - return mapper.build(); -} - -//----------------------------------------------------------------------------- - -class DenseTensorMapper : public TensorVisitor -{ - ValueType _type; - DenseTensor::Cells _cells; - - uint32_t mapAddressToIndex(const TensorAddress &address); - virtual void visit(const TensorAddress &address, double value) override; - - DenseTensorMapper(const ValueType &type); - ~DenseTensorMapper(); - - std::unique_ptr<Tensor> build(); -public: - static std::unique_ptr<Tensor> - map(const Tensor &tensor, const ValueType &type); -}; - -DenseTensorMapper::DenseTensorMapper(const ValueType &type) - : _type(type), - _cells() -{ - size_t size = 1; - for (const auto &dimension : type.dimensions()) { - size *= dimension.size; - } - _cells.resize(size); -} - -DenseTensorMapper::~DenseTensorMapper() -{ -} - -std::unique_ptr<Tensor> -DenseTensorMapper::build() -{ - return std::make_unique<DenseTensor>(std::move(_type), - std::move(_cells)); -} - -void -DenseTensorMapper::visit(const TensorAddress &address, double value) -{ - uint32_t idx = DenseTensorAddressMapper::mapAddressToIndex(address, _type); - if (idx != DenseTensorAddressMapper::BAD_ADDRESS) { - assert(idx < _cells.size()); - _cells[idx] += value; - } -} - -std::unique_ptr<Tensor> -DenseTensorMapper::map(const Tensor &tensor, const ValueType &type) -{ - DenseTensorMapper mapper(type); - tensor.accept(mapper); - return mapper.build(); -} - -//----------------------------------------------------------------------------- - -class WrappedTensorMapper : public TensorVisitor -{ - using Label = TensorSpec::Label; - - ValueType _type; - TensorSpec _spec; - - WrappedTensorMapper(const ValueType &type) - : _type(type), _spec(type.to_spec()) {} - ~WrappedTensorMapper() {} - - void visit(const TensorAddress &address, double value) override; - - std::unique_ptr<Tensor> build() { - auto tensor = eval::SimpleTensor::create(_spec); - return std::make_unique<WrappedSimpleTensor>(std::move(tensor)); - } - -public: - static std::unique_ptr<Tensor> - map(const Tensor &tensor, const ValueType &type); -}; - -void -WrappedTensorMapper::visit(const TensorAddress &address, double value) -{ - TensorSpec::Address addr; - TensorAddressElementIterator<TensorAddress> addressIterator(address); - for (const auto &dimension: _type.dimensions()) { - if (addressIterator.skipToDimension(dimension.name)) { - if (dimension.is_indexed()) { - uint32_t label = DenseTensorAddressMapper::mapLabelToNumber(addressIterator.label()); - if ((label == DenseTensorAddressMapper::BAD_LABEL) || (label >= dimension.size)) { - return; // bad address; ignore cell - } - addr.emplace(dimension.name, label); - } else { - addr.emplace(dimension.name, addressIterator.label()); - } - addressIterator.next(); - } else { - if (dimension.is_indexed()) { - addr.emplace(dimension.name, size_t(0)); - } else { - addr.emplace(dimension.name, vespalib::string()); - } - } - } - _spec.add(addr, value); -} - -std::unique_ptr<Tensor> -WrappedTensorMapper::map(const Tensor &tensor, const ValueType &type) -{ - WrappedTensorMapper mapper(type); - tensor.accept(mapper); - return mapper.build(); -} - -//----------------------------------------------------------------------------- - -} // namespace vespalib::tensor::<anonymous> - -TensorMapper::TensorMapper(const ValueType &type) - : _type(type) -{ -} - -TensorMapper::~TensorMapper() -{ -} - -template <typename TensorT> -std::unique_ptr<Tensor> -TensorMapper::mapToSparse(const Tensor &tensor, const ValueType &type) -{ - assert(type.is_sparse()); - return SparseTensorMapper<TensorT>::map(tensor, type); -} - -std::unique_ptr<Tensor> -TensorMapper::mapToDense(const Tensor &tensor, const ValueType &type) -{ - assert(type.is_dense()); - return DenseTensorMapper::map(tensor, type); -} - -std::unique_ptr<Tensor> -TensorMapper::mapToWrapped(const Tensor &tensor, const ValueType &type) -{ - assert(!type.dimensions().empty()); - return WrappedTensorMapper::map(tensor, type); -} - -std::unique_ptr<Tensor> -TensorMapper::map(const Tensor &tensor) const -{ - if (_type.is_sparse()) { - return mapToSparse<SparseTensor>(tensor, _type); - } else if (_type.is_dense()) { - return mapToDense(tensor, _type); - } else { - return mapToWrapped(tensor, _type); - } -} - -template -std::unique_ptr<Tensor> -TensorMapper::mapToSparse<SparseTensor>(const Tensor &tensor, - const ValueType &type); - -} diff --git a/eval/src/vespa/eval/tensor/tensor_mapper.h b/eval/src/vespa/eval/tensor/tensor_mapper.h deleted file mode 100644 index 95c6cce8fc6..00000000000 --- a/eval/src/vespa/eval/tensor/tensor_mapper.h +++ /dev/null @@ -1,44 +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 <vespa/eval/eval/value_type.h> - -namespace vespalib::tensor { - -class Tensor; - -/** - * Class to map a tensor to a given tensor type. Dimensions in input - * tensor not present in tensor type are ignored. Dimensions in tensor - * type not present in input tensor gets default label (undefined - * (empty string) for sparse tensors, 0 for dense tensors). Values are - * accumulated for identical mapped addresses. - * - * Dense tensor type has further restrictions: label must contain only - * numerical digits (0-9). Empty string equals 0. If the label is - * parsed to a value outside the dimension range or the parsing fails, - * then the cell ((address, value) pair) is ignored. - */ -class TensorMapper -{ - eval::ValueType _type; -public: - TensorMapper(const eval::ValueType &type); - ~TensorMapper(); - - template <typename TensorT> - static std::unique_ptr<Tensor> - mapToSparse(const Tensor &tensor, const eval::ValueType &type); - - static std::unique_ptr<Tensor> - mapToDense(const Tensor &tensor, const eval::ValueType &type); - - static std::unique_ptr<Tensor> - mapToWrapped(const Tensor &tensor, const eval::ValueType &type); - - std::unique_ptr<Tensor> map(const Tensor &tensor) const; -}; - - -} diff --git a/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp b/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp index e0ec4a6fd06..7e0fcdc0ccc 100644 --- a/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp +++ b/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp @@ -44,7 +44,7 @@ vespalib::string denseSpec("tensor(x[2],y[3])"); Tensor::UP createTensor(const TensorSpec &spec) { auto value = DefaultTensorEngine::ref().from_spec(spec); if (value->is_double()) { - return Tensor::UP(new DenseTensor(ValueType::double_type(), {value->as_double()})); + return Tensor::UP(new DenseTensor<double>(ValueType::double_type(), {value->as_double()})); } Tensor *tensor = dynamic_cast<Tensor*>(value.get()); ASSERT_TRUE(tensor != nullptr); diff --git a/searchlib/src/tests/features/imported_dot_product/imported_dot_product_test.cpp b/searchlib/src/tests/features/imported_dot_product/imported_dot_product_test.cpp index 6ef680e0505..b7fb3d2b6a1 100644 --- a/searchlib/src/tests/features/imported_dot_product/imported_dot_product_test.cpp +++ b/searchlib/src/tests/features/imported_dot_product/imported_dot_product_test.cpp @@ -197,25 +197,25 @@ TEST_F("prepareSharedState emits double vector for double imported attribute", A TEST_F("prepareSharedState handles tensor as float from tensor for double imported attribute", ArrayFixture) { f.setup_float_mappings(BasicType::DOUBLE); - vespalib::tensor::DenseTensor tensor(vespalib::eval::ValueType::from_spec("tensor<float>(x[3])"), {10.1, 20.2, 30.3}); + vespalib::tensor::DenseTensor<float> tensor(vespalib::eval::ValueType::from_spec("tensor<float>(x[3])"), {10.1, 20.2, 30.3}); f.template check_prepare_state_output(tensor, dotproduct::ArrayParam<double>({10.1, 20.2, 30.3})); } TEST_F("prepareSharedState handles tensor as double from tensor for double imported attribute", ArrayFixture) { f.setup_float_mappings(BasicType::DOUBLE); - vespalib::tensor::DenseTensor tensor(vespalib::eval::ValueType::from_spec("tensor(x[3])"), {10.1, 20.2, 30.3}); + vespalib::tensor::DenseTensor<double> tensor(vespalib::eval::ValueType::from_spec("tensor(x[3])"), {10.1, 20.2, 30.3}); f.template check_prepare_state_output(tensor, dotproduct::ArrayParam<double>({10.1, 20.2, 30.3})); } TEST_F("prepareSharedState handles tensor as float from tensor for float imported attribute", ArrayFixture) { f.setup_float_mappings(BasicType::FLOAT); - vespalib::tensor::DenseTensor tensor(vespalib::eval::ValueType::from_spec("tensor<float>(x[3])"), {10.1, 20.2, 30.3}); + vespalib::tensor::DenseTensor<float> tensor(vespalib::eval::ValueType::from_spec("tensor<float>(x[3])"), {10.1, 20.2, 30.3}); f.template check_prepare_state_output(tensor, dotproduct::ArrayParam<float>({10.1, 20.2, 30.3})); } TEST_F("prepareSharedState handles tensor as double from tensor for float imported attribute", ArrayFixture) { f.setup_float_mappings(BasicType::FLOAT); - vespalib::tensor::DenseTensor tensor(vespalib::eval::ValueType::from_spec("tensor(x[3])"), {10.1, 20.2, 30.3}); + vespalib::tensor::DenseTensor<double> tensor(vespalib::eval::ValueType::from_spec("tensor(x[3])"), {10.1, 20.2, 30.3}); f.template check_prepare_state_output(tensor, dotproduct::ArrayParam<float>({10.1, 20.2, 30.3})); } diff --git a/searchlib/src/vespa/searchlib/tensor/dense_tensor_store.cpp b/searchlib/src/vespa/searchlib/tensor/dense_tensor_store.cpp index 205c686df81..f741002ea5e 100644 --- a/searchlib/src/vespa/searchlib/tensor/dense_tensor_store.cpp +++ b/searchlib/src/vespa/searchlib/tensor/dense_tensor_store.cpp @@ -10,7 +10,6 @@ using search::datastore::Handle; using vespalib::tensor::Tensor; -using vespalib::tensor::DenseTensor; using vespalib::tensor::DenseTensorView; using vespalib::tensor::MutableDenseTensorView; using vespalib::eval::ValueType; @@ -41,8 +40,7 @@ DenseTensorStore::TensorSizeCalc::TensorSizeCalc(const ValueType &type) size_t DenseTensorStore::TensorSizeCalc::arraySize() const { - size_t tensorSize = _numBoundCells * _cellSize + - _numUnboundDims * sizeof(uint32_t); + size_t tensorSize = (_numBoundCells * _cellSize) + (_numUnboundDims * sizeof(uint32_t)); return DenseTensorStore::BufferType::align(tensorSize, DENSE_TENSOR_ALIGNMENT); } @@ -185,16 +183,16 @@ void makeConcreteType(MutableDenseTensorView &tensor, std::unique_ptr<Tensor> DenseTensorStore::getTensor(EntryRef ref) const { - using CellsRef = DenseTensorView::CellsRef; if (!ref.valid()) { return std::unique_ptr<Tensor>(); } auto raw = getRawBuffer(ref); size_t numCells = getNumCells(raw); + vespalib::tensor::TypedCells cells_ref(raw, _type.cell_type(), numCells); if (_tensorSizeCalc._numUnboundDims == 0) { - return std::make_unique<DenseTensorView>(_type, CellsRef(static_cast<const double *>(raw), numCells)); + return std::make_unique<DenseTensorView>(_type, cells_ref); } else { - auto result = std::make_unique<MutableDenseTensorView>(_type, CellsRef(static_cast<const double *>(raw), numCells)); + auto result = std::make_unique<MutableDenseTensorView>(_type, cells_ref); makeConcreteType(*result, raw, _tensorSizeCalc._numUnboundDims); return result; } @@ -204,14 +202,16 @@ void DenseTensorStore::getTensor(EntryRef ref, MutableDenseTensorView &tensor) const { if (!ref.valid()) { - tensor.setCells(DenseTensorView::CellsRef(&_emptyCells[0], _emptyCells.size())); + vespalib::tensor::TypedCells cells_ref(&_emptyCells[0], _type.cell_type(), _emptyCells.size()); + tensor.setCells(cells_ref); if (_tensorSizeCalc._numUnboundDims > 0) { tensor.setUnboundDimensionsForEmptyTensor(); } } else { auto raw = getRawBuffer(ref); size_t numCells = getNumCells(raw); - tensor.setCells(DenseTensorView::CellsRef(static_cast<const double *>(raw), numCells)); + vespalib::tensor::TypedCells cells_ref(raw, _type.cell_type(), numCells); + tensor.setCells(cells_ref); if (_tensorSizeCalc._numUnboundDims > 0) { makeConcreteType(tensor, raw, _tensorSizeCalc._numUnboundDims); } @@ -268,11 +268,11 @@ template <class TensorType> TensorStore::EntryRef DenseTensorStore::setDenseTensor(const TensorType &tensor) { - size_t numCells = tensor.cellsRef().size(); + size_t numCells = tensor.cellsRef().size; checkMatchingType(_type, tensor.type(), numCells); auto raw = allocRawBuffer(numCells); setDenseTensorUnboundDimSizes(raw.data, _type, _tensorSizeCalc._numUnboundDims, tensor.type()); - memcpy(raw.data, &tensor.cellsRef()[0], numCells * _tensorSizeCalc._cellSize); + memcpy(raw.data, tensor.cellsRef().data, numCells * _tensorSizeCalc._cellSize); return raw.ref; } diff --git a/searchlib/src/vespa/searchlib/tensor/tensor_attribute.cpp b/searchlib/src/vespa/searchlib/tensor/tensor_attribute.cpp index 79b02b09cba..7dd666ac74f 100644 --- a/searchlib/src/vespa/searchlib/tensor/tensor_attribute.cpp +++ b/searchlib/src/vespa/searchlib/tensor/tensor_attribute.cpp @@ -4,7 +4,7 @@ #include <vespa/document/base/exceptions.h> #include <vespa/document/datatype/tensor_data_type.h> #include <vespa/eval/eval/simple_tensor.h> -#include <vespa/eval/tensor/dense/dense_tensor.h> +#include <vespa/eval/tensor/dense/typed_dense_tensor_builder.h> #include <vespa/eval/tensor/sparse/sparse_tensor.h> #include <vespa/eval/tensor/wrapped_simple_tensor.h> #include <vespa/vespalib/util/rcuvector.hpp> @@ -12,7 +12,8 @@ using vespalib::eval::SimpleTensor; using vespalib::eval::ValueType; using vespalib::tensor::Tensor; -using vespalib::tensor::DenseTensor; +using vespalib::tensor::TypedDenseTensorBuilder; +using vespalib::tensor::dispatch_0; using vespalib::tensor::SparseTensor; using vespalib::tensor::WrappedSimpleTensor; using document::TensorDataType; @@ -44,17 +45,21 @@ createEmptyTensorType(const ValueType &type) return ValueType::tensor_type(std::move(list)); } +struct CallMakeEmptyTensor { + template <typename CT> + static Tensor::UP call(const ValueType &type) { + TypedDenseTensorBuilder<CT> builder(type); + return builder.build(); + } +}; + Tensor::UP createEmptyTensor(const ValueType &type) { if (type.is_sparse()) { return std::make_unique<SparseTensor>(type, SparseTensor::Cells()); } else if (type.is_dense()) { - size_t size = 1; - for (const auto &dimension : type.dimensions()) { - size *= dimension.size; - } - return std::make_unique<DenseTensor>(type, DenseTensor::Cells(size)); + return dispatch_0<CallMakeEmptyTensor>(type.cell_type(), type); } else { return std::make_unique<WrappedSimpleTensor>(std::make_unique<SimpleTensor>(type, SimpleTensor::Cells())); } |