summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArne H Juul <arnej27959@users.noreply.github.com>2020-10-19 16:07:33 +0200
committerGitHub <noreply@github.com>2020-10-19 16:07:33 +0200
commit8fd4f84b036b55d53324088393bdee8312fbd262 (patch)
treeffda0eec401abf11264117bc4a29e832c15aef4a
parent60bfc13f5d855705d888f1c80a6265913bd0d98d (diff)
parent07c7bec5b7b0c94b3b8a18db6ae922ac5258471f (diff)
Merge pull request #14945 from vespa-engine/arnej/add-wrapped-simple-value-2
Arnej/add wrapped simple value 2
-rw-r--r--eval/src/tests/instruction/generic_concat/generic_concat_test.cpp20
-rw-r--r--eval/src/tests/instruction/generic_join/generic_join_test.cpp22
-rw-r--r--eval/src/tests/instruction/generic_map/generic_map_test.cpp20
-rw-r--r--eval/src/tests/instruction/generic_merge/generic_merge_test.cpp22
-rw-r--r--eval/src/tests/instruction/generic_reduce/generic_reduce_test.cpp24
-rw-r--r--eval/src/tests/instruction/generic_rename/generic_rename_test.cpp25
-rw-r--r--eval/src/tests/tensor/tensor_conformance/tensor_conformance_test.cpp4
-rw-r--r--eval/src/vespa/eval/eval/engine_or_factory.cpp45
-rw-r--r--eval/src/vespa/eval/eval/test/tensor_conformance.cpp28
-rw-r--r--eval/src/vespa/eval/instruction/generic_concat.cpp43
-rw-r--r--eval/src/vespa/eval/instruction/generic_concat.h5
-rw-r--r--eval/src/vespa/eval/instruction/generic_join.cpp39
-rw-r--r--eval/src/vespa/eval/instruction/generic_join.h7
-rw-r--r--eval/src/vespa/eval/instruction/generic_map.cpp38
-rw-r--r--eval/src/vespa/eval/instruction/generic_map.h4
-rw-r--r--eval/src/vespa/eval/instruction/generic_merge.cpp21
-rw-r--r--eval/src/vespa/eval/instruction/generic_merge.h7
-rw-r--r--eval/src/vespa/eval/instruction/generic_reduce.cpp34
-rw-r--r--eval/src/vespa/eval/instruction/generic_reduce.h22
-rw-r--r--eval/src/vespa/eval/instruction/generic_rename.cpp22
-rw-r--r--eval/src/vespa/eval/instruction/generic_rename.h6
-rw-r--r--eval/src/vespa/eval/tensor/CMakeLists.txt1
-rw-r--r--eval/src/vespa/eval/tensor/wrapped_simple_value.cpp174
-rw-r--r--eval/src/vespa/eval/tensor/wrapped_simple_value.h53
24 files changed, 628 insertions, 58 deletions
diff --git a/eval/src/tests/instruction/generic_concat/generic_concat_test.cpp b/eval/src/tests/instruction/generic_concat/generic_concat_test.cpp
index b48c1e2253c..675aa36d63a 100644
--- a/eval/src/tests/instruction/generic_concat/generic_concat_test.cpp
+++ b/eval/src/tests/instruction/generic_concat/generic_concat_test.cpp
@@ -177,4 +177,24 @@ TEST(GenericConcatTest, dense_concat_plan_can_be_created) {
EXPECT_EQ(plan.right.out_stride, expect_right_out_s);
}
+TensorSpec immediate_generic_concat(const TensorSpec &a, const TensorSpec &b, const std::string &concat_dim) {
+ const auto &factory = SimpleValueBuilderFactory::get();
+ auto lhs = value_from_spec(a, factory);
+ auto rhs = value_from_spec(b, factory);
+ auto up = GenericConcat::perform_concat(*lhs, *rhs, concat_dim, factory);
+ return spec_from_value(*up);
+}
+
+TEST(GenericConcatTest, immediate_generic_concat_works) {
+ ASSERT_TRUE((concat_layouts.size() % 2) == 0);
+ for (size_t i = 0; i < concat_layouts.size(); i += 2) {
+ const TensorSpec lhs = spec(concat_layouts[i], N());
+ const TensorSpec rhs = spec(concat_layouts[i + 1], Div16(N()));
+ SCOPED_TRACE(fmt("\n===\nin LHS: %s\nin RHS: %s\n===\n", lhs.to_string().c_str(), rhs.to_string().c_str()));
+ auto actual = immediate_generic_concat(lhs, rhs, "y");
+ auto expect = reference_concat(lhs, rhs, "y");
+ EXPECT_EQ(actual, expect);
+ }
+}
+
GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/eval/src/tests/instruction/generic_join/generic_join_test.cpp b/eval/src/tests/instruction/generic_join/generic_join_test.cpp
index 82696bbbd5e..cf04b2ca990 100644
--- a/eval/src/tests/instruction/generic_join/generic_join_test.cpp
+++ b/eval/src/tests/instruction/generic_join/generic_join_test.cpp
@@ -148,4 +148,26 @@ TEST(GenericJoinTest, generic_join_works_for_simple_and_fast_values) {
}
}
+TensorSpec immediate_generic_join(const TensorSpec &a, const TensorSpec &b, join_fun_t function) {
+ const auto &factory = SimpleValueBuilderFactory::get();
+ auto lhs = value_from_spec(a, factory);
+ auto rhs = value_from_spec(b, factory);
+ auto up = GenericJoin::perform_join(*lhs, *rhs, function, factory);
+ return spec_from_value(*up);
+}
+
+TEST(GenericJoinTest, immediate_generic_join_works) {
+ ASSERT_TRUE((join_layouts.size() % 2) == 0);
+ for (size_t i = 0; i < join_layouts.size(); i += 2) {
+ TensorSpec lhs = spec(join_layouts[i], Div16(N()));
+ TensorSpec rhs = spec(join_layouts[i + 1], Div16(N()));
+ for (auto fun: {operation::Add::f, operation::Sub::f, operation::Mul::f, operation::Div::f}) {
+ SCOPED_TRACE(fmt("\n===\nLHS: %s\nRHS: %s\n===\n", lhs.to_string().c_str(), rhs.to_string().c_str()));
+ auto expect = reference_join(lhs, rhs, fun);
+ auto actual = immediate_generic_join(lhs, rhs, fun);
+ EXPECT_EQ(actual, expect);
+ }
+ }
+}
+
GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/eval/src/tests/instruction/generic_map/generic_map_test.cpp b/eval/src/tests/instruction/generic_map/generic_map_test.cpp
index 77f5ffc33db..69fb417e0ad 100644
--- a/eval/src/tests/instruction/generic_map/generic_map_test.cpp
+++ b/eval/src/tests/instruction/generic_map/generic_map_test.cpp
@@ -64,4 +64,24 @@ TEST(GenericMapTest, generic_map_works_for_simple_values) {
test_generic_map(SimpleValueBuilderFactory::get());
}
+TensorSpec immediate_generic_map(const TensorSpec &a, map_fun_t func, const ValueBuilderFactory &factory)
+{
+ auto lhs = value_from_spec(a, factory);
+ auto up = GenericMap::perform_map(*lhs, func, factory);
+ return spec_from_value(*up);
+}
+
+TEST(GenericMapTest, immediate_generic_map_works) {
+ for (const auto & layout : map_layouts) {
+ TensorSpec lhs = spec(layout, Div16(N()));
+ ValueType lhs_type = ValueType::from_spec(lhs.type());
+ for (auto func : {operation::Floor::f, operation::Fabs::f, operation::Square::f, operation::Inv::f}) {
+ SCOPED_TRACE(fmt("\n===\nLHS: %s\n===\n", lhs.to_string().c_str()));
+ auto expect = reference_map(lhs, func);
+ auto actual = immediate_generic_map(lhs, func, SimpleValueBuilderFactory::get());
+ EXPECT_EQ(actual, expect);
+ }
+ }
+}
+
GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/eval/src/tests/instruction/generic_merge/generic_merge_test.cpp b/eval/src/tests/instruction/generic_merge/generic_merge_test.cpp
index ae2e100176b..fd9b8513acb 100644
--- a/eval/src/tests/instruction/generic_merge/generic_merge_test.cpp
+++ b/eval/src/tests/instruction/generic_merge/generic_merge_test.cpp
@@ -79,4 +79,26 @@ TEST(GenericMergeTest, generic_merge_works_for_simple_values) {
}
}
+TensorSpec immediate_generic_merge(const TensorSpec &a, const TensorSpec &b, join_fun_t fun) {
+ const auto &factory = SimpleValueBuilderFactory::get();
+ auto lhs = value_from_spec(a, factory);
+ auto rhs = value_from_spec(b, factory);
+ auto up = GenericMerge::perform_merge(*lhs, *rhs, fun, factory);
+ return spec_from_value(*up);
+}
+
+TEST(GenericMergeTest, immediate_generic_merge_works) {
+ ASSERT_TRUE((merge_layouts.size() % 2) == 0);
+ for (size_t i = 0; i < merge_layouts.size(); i += 2) {
+ TensorSpec lhs = spec(merge_layouts[i], N());
+ TensorSpec rhs = spec(merge_layouts[i + 1], Div16(N()));
+ SCOPED_TRACE(fmt("\n===\nLHS: %s\nRHS: %s\n===\n", lhs.to_string().c_str(), rhs.to_string().c_str()));
+ for (auto fun: {operation::Add::f, operation::Mul::f, operation::Sub::f, operation::Max::f}) {
+ auto expect = reference_merge(lhs, rhs, fun);
+ auto actual = immediate_generic_merge(lhs, rhs, fun);
+ EXPECT_EQ(actual, expect);
+ }
+ }
+}
+
GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/eval/src/tests/instruction/generic_reduce/generic_reduce_test.cpp b/eval/src/tests/instruction/generic_reduce/generic_reduce_test.cpp
index 1329ad549c5..490055bf02f 100644
--- a/eval/src/tests/instruction/generic_reduce/generic_reduce_test.cpp
+++ b/eval/src/tests/instruction/generic_reduce/generic_reduce_test.cpp
@@ -110,4 +110,28 @@ TEST(GenericReduceTest, generic_reduce_works_for_simple_values) {
}
}
+TensorSpec immediate_generic_reduce(const TensorSpec &a, const std::vector<vespalib::string> &dims, Aggr aggr) {
+ const auto &factory = SimpleValueBuilderFactory::get();
+ auto lhs = value_from_spec(a, factory);
+ auto up = GenericReduce::perform_reduce(*lhs, aggr, dims, factory);
+ return spec_from_value(*up);
+}
+
+TEST(GenericReduceTest, immediate_generic_reduce_works) {
+ for (const Layout &layout: layouts) {
+ TensorSpec input = spec(layout, Div16(N()));
+ for (Aggr aggr: {Aggr::SUM, Aggr::AVG, Aggr::MIN, Aggr::MAX}) {
+ for (const Domain &domain: layout) {
+ auto expect = reference_reduce(input, {domain.dimension}, aggr);
+ auto actual = immediate_generic_reduce(input, {domain.dimension}, aggr);
+ EXPECT_EQ(actual, expect);
+ }
+ auto expect = reference_reduce(input, {}, aggr);
+ auto actual = immediate_generic_reduce(input, {}, aggr);
+ EXPECT_EQ(actual, expect);
+ }
+ }
+}
+
+
GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/eval/src/tests/instruction/generic_rename/generic_rename_test.cpp b/eval/src/tests/instruction/generic_rename/generic_rename_test.cpp
index 3f151af1fd6..124462f0e0f 100644
--- a/eval/src/tests/instruction/generic_rename/generic_rename_test.cpp
+++ b/eval/src/tests/instruction/generic_rename/generic_rename_test.cpp
@@ -142,4 +142,29 @@ TEST(GenericRenameTest, generic_rename_works_for_simple_values) {
test_generic_rename(SimpleValueBuilderFactory::get());
}
+TensorSpec immediate_generic_rename(const TensorSpec &a, const FromTo &ft)
+{
+ auto &factory = SimpleValueBuilderFactory::get();
+ auto lhs = value_from_spec(a, factory);
+ auto up = GenericRename::perform_rename(*lhs, ft.from, ft.to, factory);
+ return spec_from_value(*up);
+}
+
+TEST(GenericRenameTest, immediate_generic_rename_works) {
+ for (const auto & layout : rename_layouts) {
+ TensorSpec lhs = spec(layout, N());
+ ValueType lhs_type = ValueType::from_spec(lhs.type());
+ // printf("lhs_type: %s\n", lhs_type.to_spec().c_str());
+ for (const auto & from_to : rename_from_to) {
+ ValueType renamed_type = lhs_type.rename(from_to.from, from_to.to);
+ if (renamed_type.is_error()) continue;
+ // printf("type %s -> %s\n", lhs_type.to_spec().c_str(), renamed_type.to_spec().c_str());
+ SCOPED_TRACE(fmt("\n===\nLHS: %s\n===\n", lhs.to_string().c_str()));
+ auto expect = reference_rename(lhs, from_to);
+ auto actual = immediate_generic_rename(lhs, from_to);
+ EXPECT_EQ(actual, expect);
+ }
+ }
+}
+
GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/eval/src/tests/tensor/tensor_conformance/tensor_conformance_test.cpp b/eval/src/tests/tensor/tensor_conformance/tensor_conformance_test.cpp
index a9681cd85c9..233aff0e425 100644
--- a/eval/src/tests/tensor/tensor_conformance/tensor_conformance_test.cpp
+++ b/eval/src/tests/tensor/tensor_conformance/tensor_conformance_test.cpp
@@ -25,11 +25,11 @@ TEST("require that production tensor implementation passes all conformance tests
TEST_DO(TensorConformance::run_tests(module_src_path, DefaultTensorEngine::ref()));
}
-TEST("require that SimpleValue implementation passes all conformance tests (except ImmediateAPI tests)") {
+TEST("require that SimpleValue implementation passes all conformance tests") {
TEST_DO(TensorConformance::run_tests(module_src_path, SimpleValueBuilderFactory::get()));
}
-TEST("require that FastValue implementation passes all conformance tests (except ImmediateAPI tests)") {
+TEST("require that FastValue implementation passes all conformance tests") {
TEST_DO(TensorConformance::run_tests(module_src_path, FastValueBuilderFactory::get()));
}
diff --git a/eval/src/vespa/eval/eval/engine_or_factory.cpp b/eval/src/vespa/eval/eval/engine_or_factory.cpp
index 2a3ba2e543f..e4f710be625 100644
--- a/eval/src/vespa/eval/eval/engine_or_factory.cpp
+++ b/eval/src/vespa/eval/eval/engine_or_factory.cpp
@@ -5,16 +5,25 @@
#include "simple_value.h"
#include "value_codec.h"
#include "simple_tensor_engine.h"
+#include <vespa/eval/instruction/generic_concat.h>
+#include <vespa/eval/instruction/generic_join.h>
+#include <vespa/eval/instruction/generic_map.h>
+#include <vespa/eval/instruction/generic_merge.h>
+#include <vespa/eval/instruction/generic_reduce.h>
+#include <vespa/eval/instruction/generic_rename.h>
#include <vespa/eval/tensor/default_tensor_engine.h>
#include <vespa/eval/tensor/default_value_builder_factory.h>
#include <vespa/eval/tensor/mixed/packed_mixed_tensor_builder_factory.h>
#include <vespa/vespalib/data/memory.h>
#include <vespa/vespalib/objects/nbostream.h>
#include <vespa/vespalib/util/exceptions.h>
+#include <vespa/vespalib/util/stash.h>
#include <vespa/vespalib/util/stringfmt.h>
using vespalib::make_string_short::fmt;
+using namespace vespalib::eval::instruction;
+
namespace vespalib::eval {
EngineOrFactory EngineOrFactory::_default{tensor::DefaultTensorEngine::ref()};
@@ -88,32 +97,56 @@ EngineOrFactory::copy(const Value &value)
const Value &
EngineOrFactory::map(const Value &a, operation::op1_t function, Stash &stash) const {
- return engine().map(a, function, stash);
+ if (is_engine()) {
+ return engine().map(a, function, stash);
+ } else {
+ return *stash.create<Value::UP>(GenericMap::perform_map(a, function, factory()));
+ }
}
const Value &
EngineOrFactory::join(const Value &a, const Value &b, operation::op2_t function, Stash &stash) const {
- return engine().join(a, b, function, stash);
+ if (is_engine()) {
+ return engine().join(a, b, function, stash);
+ } else {
+ return *stash.create<Value::UP>(GenericJoin::perform_join(a, b, function, factory()));
+ }
}
const Value &
EngineOrFactory::merge(const Value &a, const Value &b, operation::op2_t function, Stash &stash) const {
- return engine().merge(a, b, function, stash);
+ if (is_engine()) {
+ return engine().merge(a, b, function, stash);
+ } else {
+ return *stash.create<Value::UP>(GenericMerge::perform_merge(a, b, function, factory()));
+ }
}
const Value &
EngineOrFactory::reduce(const Value &a, Aggr aggr, const std::vector<vespalib::string> &dimensions, Stash &stash) const {
- return engine().reduce(a, aggr, dimensions, stash);
+ if (is_engine()) {
+ return engine().reduce(a, aggr, dimensions, stash);
+ } else {
+ return *stash.create<Value::UP>(GenericReduce::perform_reduce(a, aggr, dimensions, factory()));
+ }
}
const Value &
EngineOrFactory::concat(const Value &a, const Value &b, const vespalib::string &dimension, Stash &stash) const {
- return engine().concat(a, b, dimension, stash);
+ if (is_engine()) {
+ return engine().concat(a, b, dimension, stash);
+ } else {
+ return *stash.create<Value::UP>(GenericConcat::perform_concat(a, b, dimension, factory()));
+ }
}
const Value &
EngineOrFactory::rename(const Value &a, const std::vector<vespalib::string> &from, const std::vector<vespalib::string> &to, Stash &stash) const {
- return engine().rename(a, from, to, stash);
+ if (is_engine()) {
+ return engine().rename(a, from, to, stash);
+ } else {
+ return *stash.create<Value::UP>(GenericRename::perform_rename(a, from, to, factory()));
+ }
}
void
diff --git a/eval/src/vespa/eval/eval/test/tensor_conformance.cpp b/eval/src/vespa/eval/eval/test/tensor_conformance.cpp
index c6f73d611cc..0e703e81073 100644
--- a/eval/src/vespa/eval/eval/test/tensor_conformance.cpp
+++ b/eval/src/vespa/eval/eval/test/tensor_conformance.cpp
@@ -339,9 +339,7 @@ struct TestContext {
vespalib::string expr = make_string("reduce(a,%s,%s)",
AggrNames::name_of(aggr)->c_str(), domain.dimension.c_str());
TEST_DO(verify_reduce_result(Expr_T(expr), input, expect));
- if (engine.is_engine()) {
- TEST_DO(verify_reduce_result(ImmediateReduce(aggr, domain.dimension), input, expect));
- }
+ TEST_DO(verify_reduce_result(ImmediateReduce(aggr, domain.dimension), input, expect));
}
{
Eval::Result expect = ImmediateReduce(aggr).eval(ref_engine, input);
@@ -349,9 +347,7 @@ struct TestContext {
infer_type(layout).c_str()).c_str());
vespalib::string expr = make_string("reduce(a,%s)", AggrNames::name_of(aggr)->c_str());
TEST_DO(verify_reduce_result(Expr_T(expr), input, expect));
- if (engine.is_engine()) {
- TEST_DO(verify_reduce_result(ImmediateReduce(aggr), input, expect));
- }
+ TEST_DO(verify_reduce_result(ImmediateReduce(aggr), input, expect));
}
}
}
@@ -388,9 +384,7 @@ struct TestContext {
}
void test_map_op(const vespalib::string &expr, map_fun_t op, const Sequence &seq) {
- if (engine.is_engine()) {
- TEST_DO(test_map_op(ImmediateMap(op), op, seq));
- }
+ TEST_DO(test_map_op(ImmediateMap(op), op, seq));
TEST_DO(test_map_op(Expr_T(expr), op, seq));
TEST_DO(test_map_op(Expr_T(make_string("map(x,f(a)(%s))", expr.c_str())), op, seq));
}
@@ -667,9 +661,7 @@ struct TestContext {
}
void test_apply_op(const vespalib::string &expr, join_fun_t op, const Sequence &seq) {
- if (engine.is_engine()) {
- TEST_DO(test_apply_op(ImmediateJoin(op), op, seq));
- }
+ TEST_DO(test_apply_op(ImmediateJoin(op), op, seq));
TEST_DO(test_apply_op(Expr_TT(expr), op, seq));
TEST_DO(test_apply_op(Expr_TT(make_string("join(x,y,f(a,b)(%s))", expr.c_str())), op, seq));
}
@@ -732,10 +724,8 @@ struct TestContext {
const TensorSpec &expect)
{
vespalib::string expr = make_string("concat(a,b,%s)", dimension.c_str());
- if (engine.is_engine()) {
- ImmediateConcat eval(dimension);
- TEST_DO(verify_result(eval.eval(engine, a, b), expect));
- }
+ ImmediateConcat eval(dimension);
+ TEST_DO(verify_result(eval.eval(engine, a, b), expect));
TEST_DO(verify_result(Expr_TT(expr).eval(engine, a, b), expect));
}
@@ -773,10 +763,8 @@ struct TestContext {
const std::vector<vespalib::string> &to,
const TensorSpec &expect)
{
- if (engine.is_engine()) {
- ImmediateRename eval(from, to);
- TEST_DO(verify_result(eval.eval(engine, input), expect));
- }
+ ImmediateRename eval(from, to);
+ TEST_DO(verify_result(eval.eval(engine, input), expect));
TEST_DO(verify_result(Expr_T(expr).eval(engine, input), expect));
}
diff --git a/eval/src/vespa/eval/instruction/generic_concat.cpp b/eval/src/vespa/eval/instruction/generic_concat.cpp
index cb8b5ee3b2f..eaa2dbf95ae 100644
--- a/eval/src/vespa/eval/instruction/generic_concat.cpp
+++ b/eval/src/vespa/eval/instruction/generic_concat.cpp
@@ -121,6 +121,15 @@ struct SelectGenericConcatOp {
}
};
+struct PerformGenericConcat {
+ template <typename LCT, typename RCT, typename OCT>
+ static auto invoke(const Value &a, const Value &b, const ConcatParam &param) {
+ return generic_concat<LCT, RCT, OCT>(a, b,
+ param.sparse_plan, param.dense_plan,
+ param.res_type, param.factory);
+ }
+};
+
enum class Case { NONE, OUT, CONCAT, BOTH };
} // namespace <unnamed>
@@ -183,6 +192,21 @@ DenseConcatPlan::InOutLoop::fill_from(const ValueType &in_type,
return std::make_pair(offset_for_concat, output_size_for_concat);
}
+DenseConcatPlan::DenseConcatPlan(const ValueType &lhs_type,
+ const ValueType &rhs_type,
+ std::string concat_dimension,
+ const ValueType &out_type)
+{
+ std::tie(right_offset, output_size) = left.fill_from(lhs_type, concat_dimension, out_type);
+ auto [ other_offset, other_size ] = right.fill_from(rhs_type, concat_dimension, out_type);
+ assert(other_offset > 0);
+ assert(output_size == other_size);
+}
+
+DenseConcatPlan::~DenseConcatPlan() = default;
+DenseConcatPlan::InOutLoop::~InOutLoop() = default;
+
+
InterpretedFunction::Instruction
GenericConcat::make_instruction(const ValueType &lhs_type, const ValueType &rhs_type,
const vespalib::string &dimension,
@@ -195,18 +219,15 @@ GenericConcat::make_instruction(const ValueType &lhs_type, const ValueType &rhs_
return Instruction(fun, wrap_param<ConcatParam>(param));
}
-DenseConcatPlan::DenseConcatPlan(const ValueType &lhs_type,
- const ValueType &rhs_type,
- std::string concat_dimension,
- const ValueType &out_type)
+Value::UP
+GenericConcat::perform_concat(const Value &a, const Value &b,
+ const vespalib::string &dimension,
+ const ValueBuilderFactory &factory)
{
- std::tie(right_offset, output_size) = left.fill_from(lhs_type, concat_dimension, out_type);
- auto [ other_offset, other_size ] = right.fill_from(rhs_type, concat_dimension, out_type);
- assert(other_offset > 0);
- assert(output_size == other_size);
+ ConcatParam param(a.type(), b.type(), dimension, factory);
+ return typify_invoke<3,TypifyCellType,PerformGenericConcat>(
+ a.type().cell_type(), b.type().cell_type(), param.res_type.cell_type(),
+ a, b, param);
}
-DenseConcatPlan::~DenseConcatPlan() = default;
-DenseConcatPlan::InOutLoop::~InOutLoop() = default;
-
} // namespace
diff --git a/eval/src/vespa/eval/instruction/generic_concat.h b/eval/src/vespa/eval/instruction/generic_concat.h
index 5578c5a0dca..d41d161900a 100644
--- a/eval/src/vespa/eval/instruction/generic_concat.h
+++ b/eval/src/vespa/eval/instruction/generic_concat.h
@@ -17,6 +17,11 @@ struct GenericConcat {
make_instruction(const ValueType &lhs_type, const ValueType &rhs_type,
const vespalib::string &dimension,
const ValueBuilderFactory &factory, Stash &stash);
+
+ static Value::UP
+ perform_concat(const Value &a, const Value &b,
+ const vespalib::string &dimension,
+ const ValueBuilderFactory &factory);
};
struct DenseConcatPlan {
diff --git a/eval/src/vespa/eval/instruction/generic_join.cpp b/eval/src/vespa/eval/instruction/generic_join.cpp
index 83c50fdc0a2..c7bb17a808e 100644
--- a/eval/src/vespa/eval/instruction/generic_join.cpp
+++ b/eval/src/vespa/eval/instruction/generic_join.cpp
@@ -24,8 +24,9 @@ namespace {
//-----------------------------------------------------------------------------
template <typename LCT, typename RCT, typename OCT, typename Fun>
-void my_mixed_join_op(State &state, uint64_t param_in) {
- const auto &param = unwrap_param<JoinParam>(param_in);
+Value::UP
+generic_mixed_join(const Value &lhs, const Value &rhs, const JoinParam &param)
+{
Fun fun(param.function);
auto dense_join = [&](const LCT *my_lhs, const RCT *my_rhs, OCT *my_res)
{
@@ -33,8 +34,6 @@ void my_mixed_join_op(State &state, uint64_t param_in) {
*my_res++ = fun(my_lhs[lhs_idx], my_rhs[rhs_idx]);
});
};
- const Value &lhs = state.peek(1);
- const Value &rhs = state.peek(0);
auto lhs_cells = lhs.cells().typify<LCT>();
auto rhs_cells = rhs.cells().typify<RCT>();
SparseJoinState sparse(param.sparse_plan, lhs.index(), rhs.index());
@@ -50,7 +49,16 @@ void my_mixed_join_op(State &state, uint64_t param_in) {
builder->add_subspace(sparse.full_address).begin());
}
}
- auto &result = state.stash.create<std::unique_ptr<Value>>(builder->build(std::move(builder)));
+ return builder->build(std::move(builder));
+};
+
+template <typename LCT, typename RCT, typename OCT, typename Fun>
+void my_mixed_join_op(State &state, uint64_t param_in) {
+ const auto &param = unwrap_param<JoinParam>(param_in);
+ const Value &lhs = state.peek(1);
+ const Value &rhs = state.peek(0);
+ auto up = generic_mixed_join<LCT, RCT, OCT, Fun>(lhs, rhs, param);
+ auto &result = state.stash.create<std::unique_ptr<Value>>(std::move(up));
const Value &result_ref = *(result.get());
state.pop_pop_push(result_ref);
};
@@ -141,6 +149,16 @@ struct SelectGenericJoinOp {
}
};
+struct PerformGenericJoin {
+ template <typename LCT, typename RCT, typename OCT, typename Fun>
+ static auto invoke(const Value &a, const Value &b, const JoinParam &param)
+ {
+ return generic_mixed_join<LCT, RCT, OCT, Fun>(a, b, param);
+ }
+};
+
+
+
//-----------------------------------------------------------------------------
} // namespace <unnamed>
@@ -263,4 +281,15 @@ GenericJoin::make_instruction(const ValueType &lhs_type, const ValueType &rhs_ty
return Instruction(fun, wrap_param<JoinParam>(param));
}
+
+Value::UP
+GenericJoin::perform_join(const Value &a, const Value &b, join_fun_t function,
+ const ValueBuilderFactory &factory)
+{
+ JoinParam param(a.type(), b.type(), function, factory);
+ return typify_invoke<4,JoinTypify,PerformGenericJoin>(
+ a.type().cell_type(), b.type().cell_type(), param.res_type.cell_type(), function,
+ a, b, param);
+}
+
} // namespace
diff --git a/eval/src/vespa/eval/instruction/generic_join.h b/eval/src/vespa/eval/instruction/generic_join.h
index 3b1b4433032..49cdb3499a9 100644
--- a/eval/src/vespa/eval/instruction/generic_join.h
+++ b/eval/src/vespa/eval/instruction/generic_join.h
@@ -18,8 +18,13 @@ using join_fun_t = vespalib::eval::operation::op2_t;
struct GenericJoin {
static InterpretedFunction::Instruction
- make_instruction(const ValueType &lhs_type, const ValueType &rhs_type, join_fun_t function,
+ make_instruction(const ValueType &lhs_type, const ValueType &rhs_type,
+ join_fun_t function,
const ValueBuilderFactory &factory, Stash &stash);
+
+ static Value::UP
+ perform_join(const Value &a, const Value &b, join_fun_t function,
+ const ValueBuilderFactory &factory);
};
//-----------------------------------------------------------------------------
diff --git a/eval/src/vespa/eval/instruction/generic_map.cpp b/eval/src/vespa/eval/instruction/generic_map.cpp
index 7ead705328a..689339af5b3 100644
--- a/eval/src/vespa/eval/instruction/generic_map.cpp
+++ b/eval/src/vespa/eval/instruction/generic_map.cpp
@@ -38,6 +38,36 @@ struct SelectGenericMapOp {
}
};
+struct PerformGenericMap {
+ template <typename CT, typename Func>
+ static auto invoke(const Value &input, map_fun_t function_in,
+ const ValueBuilderFactory &factory)
+ {
+ Func fun(function_in);
+ const auto &type = input.type();
+ size_t subspace_size = type.dense_subspace_size();
+ size_t num_mapped = type.count_mapped_dimensions();
+ auto builder = factory.create_value_builder<CT>(type, num_mapped, subspace_size, input.index().size());
+ auto input_cells = input.cells().typify<CT>();
+ auto view = input.index().create_view({});
+ std::vector<vespalib::stringref> output_address(num_mapped);
+ std::vector<vespalib::stringref *> input_address;
+ for (auto & label : output_address) {
+ input_address.push_back(&label);
+ }
+ view->lookup({});
+ size_t subspace;
+ while (view->next_result(input_address, subspace)) {
+ auto dst = builder->add_subspace(output_address);
+ size_t input_offset = subspace_size * subspace;
+ for (size_t i = 0; i < subspace_size; ++i) {
+ dst[i] = fun(input_cells[input_offset + i]);
+ }
+ }
+ return builder->build(std::move(builder));
+ }
+};
+
} // namespace <unnamed>
using MapTypify = TypifyValue<TypifyCellType,operation::TypifyOp1>;
@@ -49,4 +79,12 @@ GenericMap::make_instruction(const ValueType &lhs_type, map_fun_t function)
return Instruction(op, to_param(function));
}
+Value::UP
+GenericMap::perform_map(const Value &a, map_fun_t function,
+ const ValueBuilderFactory &factory)
+{
+ return typify_invoke<2,MapTypify,PerformGenericMap>(a.type().cell_type(), function,
+ a, function, factory);
+}
+
} // namespace
diff --git a/eval/src/vespa/eval/instruction/generic_map.h b/eval/src/vespa/eval/instruction/generic_map.h
index ad29d2c1073..9c8fb17c153 100644
--- a/eval/src/vespa/eval/instruction/generic_map.h
+++ b/eval/src/vespa/eval/instruction/generic_map.h
@@ -15,6 +15,10 @@ using map_fun_t = vespalib::eval::operation::op1_t;
struct GenericMap {
static InterpretedFunction::Instruction
make_instruction(const ValueType &input_type, map_fun_t function);
+
+ static Value::UP
+ perform_map(const Value &a, map_fun_t function,
+ const ValueBuilderFactory &factory);
};
} // namespace
diff --git a/eval/src/vespa/eval/instruction/generic_merge.cpp b/eval/src/vespa/eval/instruction/generic_merge.cpp
index f54ae651374..51c9dcdbb15 100644
--- a/eval/src/vespa/eval/instruction/generic_merge.cpp
+++ b/eval/src/vespa/eval/instruction/generic_merge.cpp
@@ -122,6 +122,13 @@ struct SelectGenericMergeOp {
}
};
+struct PerformGenericMerge {
+ template <typename LCT, typename RCT, typename OCT, typename Fun>
+ static auto invoke(const Value &a, const Value &b, const MergeParam &param) {
+ return generic_mixed_merge<LCT,RCT,OCT,Fun>(a, b, param);
+ }
+};
+
//-----------------------------------------------------------------------------
} // namespace <unnamed>
@@ -137,4 +144,18 @@ GenericMerge::make_instruction(const ValueType &lhs_type, const ValueType &rhs_t
return Instruction(fun, wrap_param<MergeParam>(param));
}
+
+Value::UP
+GenericMerge::perform_merge(const Value &a, const Value &b, join_fun_t function,
+ const ValueBuilderFactory &factory)
+{
+ MergeParam param(a.type(), b.type(), function, factory);
+ return typify_invoke<4,MergeTypify,PerformGenericMerge>(
+ a.type().cell_type(),
+ b.type().cell_type(),
+ param.res_type.cell_type(), function,
+ a, b, param);
+}
+
+
} // namespace
diff --git a/eval/src/vespa/eval/instruction/generic_merge.h b/eval/src/vespa/eval/instruction/generic_merge.h
index 02e2d18715a..e9ffcc87997 100644
--- a/eval/src/vespa/eval/instruction/generic_merge.h
+++ b/eval/src/vespa/eval/instruction/generic_merge.h
@@ -8,8 +8,13 @@ namespace vespalib::eval::instruction {
struct GenericMerge {
static InterpretedFunction::Instruction
- make_instruction(const ValueType &lhs_type, const ValueType &rhs_type, join_fun_t function,
+ make_instruction(const ValueType &lhs_type, const ValueType &rhs_type,
+ join_fun_t function,
const ValueBuilderFactory &factory, Stash &stash);
+
+ static Value::UP
+ perform_merge(const Value &a, const Value &b, join_fun_t function,
+ const ValueBuilderFactory &factory);
};
} // namespace
diff --git a/eval/src/vespa/eval/instruction/generic_reduce.cpp b/eval/src/vespa/eval/instruction/generic_reduce.cpp
index a76ee322759..305f51ea445 100644
--- a/eval/src/vespa/eval/instruction/generic_reduce.cpp
+++ b/eval/src/vespa/eval/instruction/generic_reduce.cpp
@@ -94,10 +94,9 @@ struct SparseReduceState {
SparseReduceState::~SparseReduceState() = default;
template <typename ICT, typename OCT, typename AGGR>
-void my_generic_reduce_op(State &state, uint64_t param_in) {
- const auto &param = unwrap_param<ReduceParam>(param_in);
+Value::UP
+generic_reduce(const Value &value, const ReduceParam &param) {
SparseReduceState sparse(param.sparse_plan);
- const Value &value = state.peek(0);
sparse.populate_map(value.index().create_view({}));
auto cells = value.cells().typify<ICT>();
AGGR aggr;
@@ -123,7 +122,15 @@ void my_generic_reduce_op(State &state, uint64_t param_in) {
zero[i] = OCT{};
}
}
- auto &result = state.stash.create<std::unique_ptr<Value>>(builder->build(std::move(builder)));
+ return builder->build(std::move(builder));
+}
+
+template <typename ICT, typename OCT, typename AGGR>
+void my_generic_reduce_op(State &state, uint64_t param_in) {
+ const auto &param = unwrap_param<ReduceParam>(param_in);
+ const Value &value = state.peek(0);
+ auto up = generic_reduce<ICT, OCT, AGGR>(value, param);
+ auto &result = state.stash.create<std::unique_ptr<Value>>(std::move(up));
const Value &result_ref = *(result.get());
state.pop_push(result_ref);
};
@@ -134,6 +141,13 @@ struct SelectGenericReduceOp {
}
};
+struct PerformGenericReduce {
+ template <typename ICT, typename OCT, typename AGGR>
+ static auto invoke(const Value &input, const ReduceParam &param) {
+ return generic_reduce<ICT, OCT, typename AGGR::template templ<ICT>>(input, param);
+ }
+};
+
//-----------------------------------------------------------------------------
} // namespace <unnamed>
@@ -213,4 +227,16 @@ GenericReduce::make_instruction(const ValueType &type, Aggr aggr, const std::vec
return Instruction(fun, wrap_param<ReduceParam>(param));
}
+
+Value::UP
+GenericReduce::perform_reduce(const Value &a, Aggr aggr,
+ const std::vector<vespalib::string> &dimensions,
+ const ValueBuilderFactory &factory)
+{
+ ReduceParam param(a.type(), dimensions, factory);
+ return typify_invoke<3,ReduceTypify,PerformGenericReduce>(
+ a.type().cell_type(), param.res_type.cell_type(), aggr,
+ a, param);
+}
+
} // namespace
diff --git a/eval/src/vespa/eval/instruction/generic_reduce.h b/eval/src/vespa/eval/instruction/generic_reduce.h
index e412f8795ac..856cfe362ae 100644
--- a/eval/src/vespa/eval/instruction/generic_reduce.h
+++ b/eval/src/vespa/eval/instruction/generic_reduce.h
@@ -14,14 +14,6 @@ namespace vespalib::eval::instruction {
//-----------------------------------------------------------------------------
-struct GenericReduce {
- static InterpretedFunction::Instruction
- make_instruction(const ValueType &type, Aggr aggr, const std::vector<vespalib::string> &dimensions,
- const ValueBuilderFactory &factory, Stash &stash);
-};
-
-//-----------------------------------------------------------------------------
-
struct DenseReducePlan {
size_t in_size;
size_t out_size;
@@ -51,4 +43,18 @@ struct SparseReducePlan {
//-----------------------------------------------------------------------------
+struct GenericReduce {
+ static InterpretedFunction::Instruction
+ make_instruction(const ValueType &type, Aggr aggr,
+ const std::vector<vespalib::string> &dimensions,
+ const ValueBuilderFactory &factory, Stash &stash);
+
+ static Value::UP
+ perform_reduce(const Value &a, Aggr aggr,
+ const std::vector<vespalib::string> &dimensions,
+ const ValueBuilderFactory &factory);
+};
+
+//-----------------------------------------------------------------------------
+
} // namespace
diff --git a/eval/src/vespa/eval/instruction/generic_rename.cpp b/eval/src/vespa/eval/instruction/generic_rename.cpp
index 1ce18597ec2..3d8de356001 100644
--- a/eval/src/vespa/eval/instruction/generic_rename.cpp
+++ b/eval/src/vespa/eval/instruction/generic_rename.cpp
@@ -107,6 +107,14 @@ struct SelectGenericRenameOp {
}
};
+struct PerformGenericRename {
+ template <typename CT>
+ static auto invoke(const Value &a, const RenameParam &param) {
+ return generic_rename<CT>(a, param.sparse_plan, param.dense_plan,
+ param.res_type, param.factory);
+ }
+};
+
} // namespace <unnamed>
//-----------------------------------------------------------------------------
@@ -186,5 +194,19 @@ GenericRename::make_instruction(const ValueType &lhs_type,
return Instruction(fun, wrap_param<RenameParam>(param));
}
+
+Value::UP
+GenericRename::perform_rename(const Value &a,
+ const std::vector<vespalib::string> &rename_dimension_from,
+ const std::vector<vespalib::string> &rename_dimension_to,
+ const ValueBuilderFactory &factory)
+{
+ RenameParam param(a.type(),
+ rename_dimension_from, rename_dimension_to,
+ factory);
+ return typify_invoke<1,TypifyCellType,PerformGenericRename>(a.type().cell_type(),
+ a, param);
+}
+
} // namespace
diff --git a/eval/src/vespa/eval/instruction/generic_rename.h b/eval/src/vespa/eval/instruction/generic_rename.h
index 6c94ff02b24..4088e817b90 100644
--- a/eval/src/vespa/eval/instruction/generic_rename.h
+++ b/eval/src/vespa/eval/instruction/generic_rename.h
@@ -44,6 +44,12 @@ struct GenericRename {
const std::vector<vespalib::string> &rename_dimension_from,
const std::vector<vespalib::string> &rename_dimension_to,
const ValueBuilderFactory &factory, Stash &stash);
+
+ static Value::UP
+ perform_rename(const Value &a,
+ const std::vector<vespalib::string> &rename_dimension_from,
+ const std::vector<vespalib::string> &rename_dimension_to,
+ const ValueBuilderFactory &factory);
};
} // namespace
diff --git a/eval/src/vespa/eval/tensor/CMakeLists.txt b/eval/src/vespa/eval/tensor/CMakeLists.txt
index b75b34098f5..77ae1daec88 100644
--- a/eval/src/vespa/eval/tensor/CMakeLists.txt
+++ b/eval/src/vespa/eval/tensor/CMakeLists.txt
@@ -7,4 +7,5 @@ vespa_add_library(eval_tensor OBJECT
tensor.cpp
tensor_address.cpp
wrapped_simple_tensor.cpp
+ wrapped_simple_value.cpp
)
diff --git a/eval/src/vespa/eval/tensor/wrapped_simple_value.cpp b/eval/src/vespa/eval/tensor/wrapped_simple_value.cpp
new file mode 100644
index 00000000000..eb552c21aa0
--- /dev/null
+++ b/eval/src/vespa/eval/tensor/wrapped_simple_value.cpp
@@ -0,0 +1,174 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "wrapped_simple_value.h"
+#include "cell_values.h"
+#include "tensor_address_builder.h"
+#include "tensor_visitor.h"
+#include <vespa/eval/eval/memory_usage_stuff.h>
+#include <vespa/eval/eval/simple_value.h>
+#include <vespa/eval/eval/tensor_spec.h>
+#include <vespa/eval/eval/value_codec.h>
+#include <vespa/vespalib/util/stringfmt.h>
+
+#include <vespa/log/log.h>
+LOG_SETUP(".eval.tensor.wrapped_simple_value");
+
+using vespalib::eval::TensorSpec;
+using vespalib::eval::SimpleValueBuilderFactory;
+
+namespace vespalib::tensor {
+
+namespace {
+
+TensorSpec::Address
+sparsify_address(const TensorSpec::Address &address)
+{
+ TensorSpec::Address result;
+ for (const auto &elem : address) {
+ if (elem.second.is_indexed()) {
+ auto val = vespalib::make_string("%zu", elem.second.index);
+ result.emplace(elem.first, TensorSpec::Label(val));
+ } else {
+ result.emplace(elem);
+ }
+ }
+ return result;
+}
+
+TensorSpec::Address
+extract_sparse_address(const TensorSpec::Address &address)
+{
+ TensorSpec::Address result;
+ for (const auto &elem : address) {
+ if (elem.second.is_mapped()) {
+ result.emplace(elem);
+ }
+ }
+ return result;
+}
+
+Tensor::UP wrap(eval::Value::UP value) {
+ return std::make_unique<WrappedSimpleValue>(std::move(value));
+}
+
+} // namespace <unnamed>
+
+
+eval::TensorSpec
+WrappedSimpleValue::toSpec() const
+{
+ return spec_from_value(_tensor);
+}
+
+void
+WrappedSimpleValue::accept(TensorVisitor &visitor) const
+{
+ TensorSpec myspec = toSpec();
+ TensorAddressBuilder addr;
+ for (const auto & cell : myspec.cells()) {
+ auto sparse_addr = sparsify_address(cell.first);
+ addr.clear();
+ for (const auto & dim_and_label : sparse_addr) {
+ addr.add(dim_and_label.first, dim_and_label.second.name);
+ }
+ visitor.visit(addr.build(), cell.second);
+ }
+}
+
+MemoryUsage
+WrappedSimpleValue::get_memory_usage() const
+{
+ MemoryUsage rv = eval::self_memory_usage<WrappedSimpleValue>();
+ if (_space) {
+ rv.merge(_space->get_memory_usage());
+ }
+ return rv;
+}
+
+//-----------------------------------------------------------------------------
+
+Tensor::UP
+WrappedSimpleValue::apply(const CellFunction &) const
+{
+ LOG_ABORT("should not be reached");
+}
+
+Tensor::UP
+WrappedSimpleValue::join(join_fun_t, const Tensor &) const
+{
+ LOG_ABORT("should not be reached");
+}
+
+Tensor::UP
+WrappedSimpleValue::merge(join_fun_t, const Tensor &) const
+{
+ LOG_ABORT("should not be reached");
+}
+
+Tensor::UP
+WrappedSimpleValue::reduce(join_fun_t, const std::vector<vespalib::string> &) const
+{
+ LOG_ABORT("should not be reached");
+}
+
+Tensor::UP
+WrappedSimpleValue::modify(join_fun_t fun, const CellValues &cellValues) const
+{
+ TensorSpec a = toSpec();
+ TensorSpec b = cellValues.toSpec();
+ TensorSpec result(a.type());
+ auto end_iter = b.cells().end();
+ for (const auto &cell: a.cells()) {
+ double v = cell.second;
+ auto sparse_addr = sparsify_address(cell.first);
+ auto iter = b.cells().find(sparse_addr);
+ if (iter == end_iter) {
+ result.add(cell.first, v);
+ } else {
+ result.add(cell.first, fun(v, iter->second));
+ }
+ }
+ return wrap(value_from_spec(result, SimpleValueBuilderFactory::get()));
+}
+
+Tensor::UP
+WrappedSimpleValue::add(const Tensor &rhs) const
+{
+ TensorSpec a = toSpec();
+ TensorSpec b = rhs.toSpec();
+ if (a.type() != b.type()) {
+ return {};
+ }
+ TensorSpec result(a.type());
+ for (const auto &cell: b.cells()) {
+ result.add(cell.first, cell.second);
+ }
+ auto end_iter = b.cells().end();
+ for (const auto &cell: a.cells()) {
+ auto iter = b.cells().find(cell.first);
+ if (iter == end_iter) {
+ result.add(cell.first, cell.second);
+ }
+ }
+ return wrap(value_from_spec(result, SimpleValueBuilderFactory::get()));
+}
+
+
+Tensor::UP
+WrappedSimpleValue::remove(const CellValues &rhs) const
+{
+ TensorSpec a = toSpec();
+ TensorSpec b = rhs.toSpec();
+ TensorSpec result(a.type());
+ auto end_iter = b.cells().end();
+ for (const auto &cell: a.cells()) {
+ TensorSpec::Address mappedAddress = extract_sparse_address(cell.first);
+ auto iter = b.cells().find(mappedAddress);
+ if (iter == end_iter) {
+ result.add(cell.first, cell.second);
+ }
+ }
+ return wrap(value_from_spec(result, SimpleValueBuilderFactory::get()));
+}
+
+}
diff --git a/eval/src/vespa/eval/tensor/wrapped_simple_value.h b/eval/src/vespa/eval/tensor/wrapped_simple_value.h
new file mode 100644
index 00000000000..3d8c3e43757
--- /dev/null
+++ b/eval/src/vespa/eval/tensor/wrapped_simple_value.h
@@ -0,0 +1,53 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "tensor.h"
+#include <vespa/eval/eval/value.h>
+#include <vespa/eval/eval/aggr.h>
+
+namespace vespalib::tensor {
+
+/**
+ * A thin wrapper around a SimpleValue to be used as fallback for tensors with data
+ * layouts not supported by the default tensor implementation.
+ *
+ * Tensor implementation class is currently inferred from its value
+ * type. Consider adding explicit tagging to the tensor::Tensor
+ * default implementation top-level class in the future.
+ **/
+class WrappedSimpleValue : public Tensor
+{
+private:
+ std::unique_ptr<eval::Value> _space;
+ const eval::Value &_tensor;
+public:
+ explicit WrappedSimpleValue(const eval::Value &tensor)
+ : _space(), _tensor(tensor) {}
+ explicit WrappedSimpleValue(std::unique_ptr<eval::Value> tensor)
+ : _space(std::move(tensor)), _tensor(*_space) {}
+ ~WrappedSimpleValue() {}
+ const eval::Value &unwrap() const { return _tensor; }
+
+ // Value API
+ const eval::ValueType &type() const override { return _tensor.type(); }
+ eval::TypedCells cells() const override { return _tensor.cells(); }
+ const Index &index() const override { return _tensor.index(); }
+ double as_double() const override { return _tensor.as_double(); }
+
+ // tensor API
+ eval::TensorSpec toSpec() const override;
+ void accept(TensorVisitor &visitor) const override;
+ MemoryUsage get_memory_usage() const override;
+
+ Tensor::UP join(join_fun_t fun, const Tensor &rhs) const override;
+ Tensor::UP merge(join_fun_t fun, const Tensor &rhs) const override;
+ Tensor::UP reduce(join_fun_t fun, const std::vector<vespalib::string> &dims) const override;
+
+ Tensor::UP apply(const CellFunction & func) const override;
+ Tensor::UP modify(join_fun_t fun, const CellValues &cellValues) const override;
+ Tensor::UP add(const Tensor &rhs) const override;
+ Tensor::UP remove(const CellValues &rhs) const override;
+};
+
+} // namespace vespalib::tensor