summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeir Storli <geirstorli@yahoo.no>2017-01-16 17:01:53 +0100
committerGitHub <noreply@github.com>2017-01-16 17:01:53 +0100
commit1c6960d93f2a5bd3b002f4640453c85f849c8917 (patch)
tree9d3cdb624ed5128dd84ff8692fdd0acfdba0fe38
parent537380f035c2994d5f8c3efe3fece50ef59ce229 (diff)
parent3075f730820c3f6be61c046e97a0f106309657b8 (diff)
Merge pull request #1512 from yahoo/havardpe/extend-tensor-reference-implementation
Havardpe/extend tensor reference implementation
-rw-r--r--vespalib/src/tests/eval/simple_tensor/simple_tensor_test.cpp4
-rw-r--r--vespalib/src/tests/eval/value_type/value_type_test.cpp12
-rw-r--r--vespalib/src/vespa/vespalib/eval/simple_tensor.cpp147
-rw-r--r--vespalib/src/vespa/vespalib/eval/simple_tensor.h6
-rw-r--r--vespalib/src/vespa/vespalib/eval/simple_tensor_engine.cpp22
-rw-r--r--vespalib/src/vespa/vespalib/eval/simple_tensor_engine.h2
-rw-r--r--vespalib/src/vespa/vespalib/eval/tensor_engine.h4
-rw-r--r--vespalib/src/vespa/vespalib/eval/test/tensor_conformance.cpp53
-rw-r--r--vespalib/src/vespa/vespalib/eval/value_type.cpp28
-rw-r--r--vespalib/src/vespa/vespalib/eval/value_type.h1
-rw-r--r--vespalib/src/vespa/vespalib/tensor/default_tensor_engine.cpp10
-rw-r--r--vespalib/src/vespa/vespalib/tensor/default_tensor_engine.h2
12 files changed, 232 insertions, 59 deletions
diff --git a/vespalib/src/tests/eval/simple_tensor/simple_tensor_test.cpp b/vespalib/src/tests/eval/simple_tensor/simple_tensor_test.cpp
index 33812779a30..36cb9f773c1 100644
--- a/vespalib/src/tests/eval/simple_tensor/simple_tensor_test.cpp
+++ b/vespalib/src/tests/eval/simple_tensor/simple_tensor_test.cpp
@@ -91,7 +91,7 @@ TEST("require that simple tensors can have their values negated") {
.add({{"x","1"},{"y","1"}}, -1)
.add({{"x","2"},{"y","1"}}, 3)
.add({{"x","1"},{"y","2"}}, -5));
- auto result = SimpleTensor::perform(operation::Neg(), *tensor);
+ auto result = SimpleTensor::map(operation::Neg(), *tensor);
EXPECT_EQUAL(*expect, *result);
Stash stash;
const Value &result2 = SimpleTensorEngine::ref().map(operation::Neg(), *tensor, stash);
@@ -116,7 +116,7 @@ TEST("require that simple tensors can be multiplied with each other") {
.add({{"x","2"},{"y","1"},{"z","1"}}, 21)
.add({{"x","2"},{"y","1"},{"z","2"}}, 39)
.add({{"x","1"},{"y","2"},{"z","1"}}, 55));
- auto result = SimpleTensor::perform(operation::Mul(), *lhs, *rhs);
+ auto result = SimpleTensor::join(operation::Mul(), *lhs, *rhs);
EXPECT_EQUAL(*expect, *result);
Stash stash;
const Value &result2 = SimpleTensorEngine::ref().apply(operation::Mul(), *lhs, *rhs, stash);
diff --git a/vespalib/src/tests/eval/value_type/value_type_test.cpp b/vespalib/src/tests/eval/value_type/value_type_test.cpp
index f0f9871f45a..1a1f1ae6cca 100644
--- a/vespalib/src/tests/eval/value_type/value_type_test.cpp
+++ b/vespalib/src/tests/eval/value_type/value_type_test.cpp
@@ -59,6 +59,18 @@ TEST("require that dimension names can be obtained") {
std::vector<vespalib::string>({"x", "y", "z"}));
}
+TEST("require that dimension index can be obtained") {
+ EXPECT_EQUAL(ValueType::error_type().dimension_index("x"), ValueType::Dimension::npos);
+ EXPECT_EQUAL(ValueType::any_type().dimension_index("x"), ValueType::Dimension::npos);
+ EXPECT_EQUAL(ValueType::double_type().dimension_index("x"), ValueType::Dimension::npos);
+ EXPECT_EQUAL(ValueType::tensor_type({}).dimension_index("x"), ValueType::Dimension::npos);
+ auto my_type = ValueType::tensor_type({{"y", 10}, {"x"}, {"z", 0}});
+ EXPECT_EQUAL(my_type.dimension_index("x"), 0);
+ EXPECT_EQUAL(my_type.dimension_index("y"), 1);
+ EXPECT_EQUAL(my_type.dimension_index("z"), 2);
+ EXPECT_EQUAL(my_type.dimension_index("w"), ValueType::Dimension::npos);
+}
+
void verify_equal(const ValueType &a, const ValueType &b) {
EXPECT_TRUE(a == b);
EXPECT_TRUE(b == a);
diff --git a/vespalib/src/vespa/vespalib/eval/simple_tensor.cpp b/vespalib/src/vespa/vespalib/eval/simple_tensor.cpp
index 1c5a7d20535..ceb72854e66 100644
--- a/vespalib/src/vespa/vespalib/eval/simple_tensor.cpp
+++ b/vespalib/src/vespa/vespalib/eval/simple_tensor.cpp
@@ -56,6 +56,20 @@ Address select(const Address &a, const Address &b, const IndexList &selector) {
return result;
}
+size_t get_dimension_size(const ValueType &type, size_t dim_idx) {
+ if (dim_idx == ValueType::Dimension::npos) {
+ return 1;
+ }
+ return type.dimensions()[dim_idx].size;
+}
+
+size_t get_dimension_index(const Address &addr, size_t dim_idx) {
+ if (dim_idx == ValueType::Dimension::npos) {
+ return 0;
+ }
+ return addr[dim_idx].index;
+}
+
/**
* Helper class used when building SimpleTensors. While a tensor
* in its final form simply contains a collection of cells, the
@@ -163,61 +177,64 @@ public:
};
/**
- * Helper class used to analyze the combination of types for binary
- * operations performed on SimpleTensors. The type of each tensor is
- * used as input. The constructor will calculate the result type of
- * the operation as well as which dimensions from each tensor is
- * overlapping with the other tensor and also how to build the final
- * address by indicating which labels to select from the concatenation
- * of the input addresses.
+ * Helper class used to calculate which dimensions are shared between
+ * types and which are not. Also calculates how address elements from
+ * cells with the different types should be combined into a single
+ * address.
**/
struct TypeAnalyzer {
- using DimensionList = std::vector<ValueType::Dimension>;
- ValueType result_type;
+ static constexpr size_t npos = -1;
+ IndexList only_a;
IndexList overlap_a;
IndexList overlap_b;
- IndexList selector;
- TypeAnalyzer(const ValueType &lhs, const ValueType &rhs)
- : result_type(ValueType::any_type()), overlap_a(), overlap_b(), selector()
+ IndexList only_b;
+ IndexList combine;
+ size_t ignored_a;
+ size_t ignored_b;
+ TypeAnalyzer(const ValueType &lhs, const ValueType &rhs, const vespalib::string &ignore = "")
+ : only_a(), overlap_a(), overlap_b(), only_b(), combine(), ignored_a(npos), ignored_b(npos)
{
- DimensionList union_dims;
const auto &a = lhs.dimensions();
const auto &b = rhs.dimensions();
size_t b_idx = 0;
for (size_t a_idx = 0; a_idx < a.size(); ++a_idx) {
while ((b_idx < b.size()) && (b[b_idx].name < a[a_idx].name)) {
- selector.push_back(a.size() + b_idx);
- union_dims.push_back(b[b_idx++]);
+ if (b[b_idx].name != ignore) {
+ only_b.push_back(b_idx);
+ combine.push_back(a.size() + b_idx);
+ } else {
+ ignored_b = b_idx;
+ }
+ ++b_idx;
}
if ((b_idx < b.size()) && (b[b_idx].name == a[a_idx].name)) {
- assert(a[a_idx].is_mapped() == b[b_idx].is_mapped());
- overlap_a.push_back(a_idx);
- overlap_b.push_back(b_idx);
- if (b[b_idx].size < a[a_idx].size) {
- selector.push_back(a.size() + b_idx);
- union_dims.push_back(b[b_idx]);
+ if (a[a_idx].name != ignore) {
+ overlap_a.push_back(a_idx);
+ overlap_b.push_back(b_idx);
+ combine.push_back(a_idx);
} else {
- selector.push_back(a_idx);
- union_dims.push_back(a[a_idx]);
+ ignored_a = a_idx;
+ ignored_b = b_idx;
}
++b_idx;
} else {
- selector.push_back(a_idx);
- union_dims.push_back(a[a_idx]);
+ if (a[a_idx].name != ignore) {
+ only_a.push_back(a_idx);
+ combine.push_back(a_idx);
+ } else {
+ ignored_a = a_idx;
+ }
}
}
while (b_idx < b.size()) {
- selector.push_back(a.size() + b_idx);
- union_dims.push_back(b[b_idx++]);
- }
- if (union_dims.empty()) {
- result_type = ValueType::double_type();
- } else {
- result_type = ValueType::tensor_type(union_dims);
+ if (b[b_idx].name != ignore) {
+ only_b.push_back(b_idx);
+ combine.push_back(a.size() + b_idx);
+ } else {
+ ignored_b = b_idx;
+ }
+ ++b_idx;
}
- assert(selector.size() == result_type.dimensions().size());
- assert(overlap_a.size() == overlap_b.size());
- assert_type(result_type);
}
};
@@ -270,15 +287,22 @@ private:
}
public:
- View(const SimpleTensor &tensor, const IndexList &selector_in)
- : _less(selector_in), _refs()
+ View(const SimpleTensor &tensor, const IndexList &selector)
+ : _less(selector), _refs()
{
- _refs.reserve(tensor.cells().size());
for (const auto &cell: tensor.cells()) {
_refs.emplace_back(cell);
}
std::sort(_refs.begin(), _refs.end(), _less);
}
+ View(const EqualRange &range, const IndexList &selector)
+ : _less(selector), _refs()
+ {
+ for (const auto &cell: range) {
+ _refs.emplace_back(cell);
+ }
+ std::sort(_refs.begin(), _refs.end(), _less);
+ }
const IndexList &selector() const { return _less.selector; }
const CellRef *refs_begin() const { return &_refs[0]; }
const CellRef *refs_end() const { return (refs_begin() + _refs.size()); }
@@ -370,6 +394,13 @@ public:
constexpr size_t TensorSpec::Label::npos;
constexpr size_t SimpleTensor::Label::npos;
+SimpleTensor::SimpleTensor(double value)
+ : Tensor(SimpleTensorEngine::ref()),
+ _type(ValueType::double_type()),
+ _cells({Cell({},value)})
+{
+}
+
SimpleTensor::SimpleTensor(const ValueType &type_in, Cells &&cells_in)
: Tensor(SimpleTensorEngine::ref()),
_type(type_in),
@@ -439,7 +470,7 @@ SimpleTensor::equal(const SimpleTensor &a, const SimpleTensor &b)
}
std::unique_ptr<SimpleTensor>
-SimpleTensor::perform(const UnaryOperation &op, const SimpleTensor &a)
+SimpleTensor::map(const UnaryOperation &op, const SimpleTensor &a)
{
Cells cells(a.cells());
for (auto &cell: cells) {
@@ -449,16 +480,17 @@ SimpleTensor::perform(const UnaryOperation &op, const SimpleTensor &a)
}
std::unique_ptr<SimpleTensor>
-SimpleTensor::perform(const BinaryOperation &op, const SimpleTensor &a, const SimpleTensor &b)
+SimpleTensor::join(const BinaryOperation &op, const SimpleTensor &a, const SimpleTensor &b)
{
+ ValueType result_type = ValueType::join(a.type(), b.type());
+ Builder builder(result_type);
TypeAnalyzer type_info(a.type(), b.type());
- Builder builder(type_info.result_type);
View view_a(a, type_info.overlap_a);
View view_b(b, type_info.overlap_b);
for (ViewMatcher matcher(view_a, view_b); matcher.valid(); matcher.next()) {
for (const auto &ref_a: matcher.get_a()) {
for (const auto &ref_b: matcher.get_b()) {
- builder.set(select(ref_a.get().address, ref_b.get().address, type_info.selector),
+ builder.set(select(ref_a.get().address, ref_b.get().address, type_info.combine),
op.eval(ref_a.get().value, ref_b.get().value));
}
}
@@ -466,5 +498,36 @@ SimpleTensor::perform(const BinaryOperation &op, const SimpleTensor &a, const Si
return builder.build();
}
+std::unique_ptr<SimpleTensor>
+SimpleTensor::concat(const SimpleTensor &a, const SimpleTensor &b, const vespalib::string &dimension)
+{
+ ValueType result_type = ValueType::concat(a.type(), b.type(), dimension);
+ Builder builder(result_type);
+ TypeAnalyzer type_info(a.type(), b.type(), dimension);
+ View view_a(a, type_info.overlap_a);
+ View view_b(b, type_info.overlap_b);
+ size_t cat_dim_idx = result_type.dimension_index(dimension);
+ size_t cat_offset = get_dimension_size(a.type(), type_info.ignored_a);
+ for (ViewMatcher matcher(view_a, view_b); matcher.valid(); matcher.next()) {
+ View subview_a(matcher.get_a(), type_info.only_a);
+ View subview_b(matcher.get_b(), type_info.only_b);
+ for (auto range_a = subview_a.first_range(); !range_a.empty(); range_a = subview_a.next_range(range_a)) {
+ for (auto range_b = subview_b.first_range(); !range_b.empty(); range_b = subview_b.next_range(range_b)) {
+ Address addr = select(range_a.begin()->get().address, range_b.begin()->get().address, type_info.combine);
+ addr.insert(addr.begin() + cat_dim_idx, Label(size_t(0)));
+ for (const auto &ref: range_a) {
+ addr[cat_dim_idx].index = get_dimension_index(ref.get().address, type_info.ignored_a);
+ builder.set(addr, ref.get().value);
+ }
+ for (const auto &ref: range_b) {
+ addr[cat_dim_idx].index = cat_offset + get_dimension_index(ref.get().address, type_info.ignored_b);
+ builder.set(addr, ref.get().value);
+ }
+ }
+ }
+ }
+ return builder.build();
+}
+
} // namespace vespalib::eval
} // namespace vespalib
diff --git a/vespalib/src/vespa/vespalib/eval/simple_tensor.h b/vespalib/src/vespa/vespalib/eval/simple_tensor.h
index f2471b0cd04..0d1538ffb18 100644
--- a/vespalib/src/vespa/vespalib/eval/simple_tensor.h
+++ b/vespalib/src/vespa/vespalib/eval/simple_tensor.h
@@ -70,14 +70,16 @@ private:
Cells _cells;
public:
+ explicit SimpleTensor(double value);
SimpleTensor(const ValueType &type_in, Cells &&cells_in);
const ValueType &type() const { return _type; }
const Cells &cells() const { return _cells; }
std::unique_ptr<SimpleTensor> reduce(const BinaryOperation &op, const std::vector<vespalib::string> &dimensions) const;
static std::unique_ptr<SimpleTensor> create(const TensorSpec &spec);
static bool equal(const SimpleTensor &a, const SimpleTensor &b);
- static std::unique_ptr<SimpleTensor> perform(const UnaryOperation &op, const SimpleTensor &a);
- static std::unique_ptr<SimpleTensor> perform(const BinaryOperation &op, const SimpleTensor &a, const SimpleTensor &b);
+ static std::unique_ptr<SimpleTensor> map(const UnaryOperation &op, const SimpleTensor &a);
+ static std::unique_ptr<SimpleTensor> join(const BinaryOperation &op, const SimpleTensor &a, const SimpleTensor &b);
+ static std::unique_ptr<SimpleTensor> concat(const SimpleTensor &a, const SimpleTensor &b, const vespalib::string &dimension);
};
} // namespace vespalib::eval
diff --git a/vespalib/src/vespa/vespalib/eval/simple_tensor_engine.cpp b/vespalib/src/vespa/vespalib/eval/simple_tensor_engine.cpp
index 06e514e51ba..3ba427becb9 100644
--- a/vespalib/src/vespa/vespalib/eval/simple_tensor_engine.cpp
+++ b/vespalib/src/vespa/vespalib/eval/simple_tensor_engine.cpp
@@ -78,6 +78,15 @@ SimpleTensorEngine::to_spec(const Tensor &tensor) const
return spec;
}
+const SimpleTensor &to_simple(const Value &value, Stash &stash) {
+ auto tensor = value.as_tensor();
+ if (tensor) {
+ assert(&tensor->engine() == &SimpleTensorEngine::ref());
+ return static_cast<const SimpleTensor &>(*tensor);
+ }
+ return stash.create<SimpleTensor>(value.as_double());
+}
+
std::unique_ptr<eval::Tensor>
SimpleTensorEngine::create(const TensorSpec &spec) const
{
@@ -102,7 +111,7 @@ SimpleTensorEngine::map(const UnaryOperation &op, const eval::Tensor &a, Stash &
{
assert(&a.engine() == this);
const SimpleTensor &simple_a = static_cast<const SimpleTensor&>(a);
- auto result = SimpleTensor::perform(op, simple_a);
+ auto result = SimpleTensor::map(op, simple_a);
return stash.create<TensorValue>(std::move(result));
}
@@ -113,7 +122,16 @@ SimpleTensorEngine::apply(const BinaryOperation &op, const eval::Tensor &a, cons
assert(&b.engine() == this);
const SimpleTensor &simple_a = static_cast<const SimpleTensor&>(a);
const SimpleTensor &simple_b = static_cast<const SimpleTensor&>(b);
- auto result = SimpleTensor::perform(op, simple_a, simple_b);
+ auto result = SimpleTensor::join(op, simple_a, simple_b);
+ return stash.create<TensorValue>(std::move(result));
+}
+
+const Value &
+SimpleTensorEngine::concat(const Value &a, const Value &b, const vespalib::string &dimension, Stash &stash) const
+{
+ const SimpleTensor &simple_a = to_simple(a, stash);
+ const SimpleTensor &simple_b = to_simple(b, stash);
+ auto result = SimpleTensor::concat(simple_a, simple_b, dimension);
return stash.create<TensorValue>(std::move(result));
}
diff --git a/vespalib/src/vespa/vespalib/eval/simple_tensor_engine.h b/vespalib/src/vespa/vespalib/eval/simple_tensor_engine.h
index c3207c440fb..b8791606084 100644
--- a/vespalib/src/vespa/vespalib/eval/simple_tensor_engine.h
+++ b/vespalib/src/vespa/vespalib/eval/simple_tensor_engine.h
@@ -28,6 +28,8 @@ public:
const Value &reduce(const Tensor &tensor, const BinaryOperation &op, const std::vector<vespalib::string> &dimensions, Stash &stash) const override;
const Value &map(const UnaryOperation &op, const Tensor &a, Stash &stash) const override;
const Value &apply(const BinaryOperation &op, const Tensor &a, const Tensor &b, Stash &stash) const override;
+
+ const Value &concat(const Value &a, const Value &b, const vespalib::string &dimension, Stash &stash) const override;
};
} // namespace vespalib::eval
diff --git a/vespalib/src/vespa/vespalib/eval/tensor_engine.h b/vespalib/src/vespa/vespalib/eval/tensor_engine.h
index 2458da7ff8b..25a38fed69c 100644
--- a/vespalib/src/vespa/vespalib/eval/tensor_engine.h
+++ b/vespalib/src/vespa/vespalib/eval/tensor_engine.h
@@ -49,6 +49,10 @@ struct TensorEngine
virtual const Value &reduce(const Tensor &tensor, const BinaryOperation &op, const std::vector<vespalib::string> &dimensions, Stash &stash) const = 0;
virtual const Value &map(const UnaryOperation &op, const Tensor &a, Stash &stash) const = 0;
virtual const Value &apply(const BinaryOperation &op, const Tensor &a, const Tensor &b, Stash &stash) const = 0;
+
+ // havardpe: new API, WIP
+ virtual const Value &concat(const Value &a, const Value &b, const vespalib::string &dimension, Stash &stash) const = 0;
+
virtual ~TensorEngine() {}
};
diff --git a/vespalib/src/vespa/vespalib/eval/test/tensor_conformance.cpp b/vespalib/src/vespa/vespalib/eval/test/tensor_conformance.cpp
index 716311d818b..b05d9c5b2ad 100644
--- a/vespalib/src/vespa/vespalib/eval/test/tensor_conformance.cpp
+++ b/vespalib/src/vespa/vespalib/eval/test/tensor_conformance.cpp
@@ -376,6 +376,14 @@ struct Expr_TT : Eval {
}
};
+const Value &make_value(const TensorEngine &engine, const TensorSpec &spec, Stash &stash) {
+ if (spec.type() == "double") {
+ double number = spec.cells().empty() ? 0.0 : spec.cells().begin()->second.value;
+ return stash.create<DoubleValue>(number);
+ }
+ return stash.create<TensorValue>(engine.create(spec));
+}
+
// evaluate tensor reduce operation using tensor engine immediate api
struct ImmediateReduce : Eval {
const BinaryOperation &op;
@@ -409,6 +417,18 @@ struct ImmediateApply : Eval {
}
};
+// evaluate tensor concat operation using tensor engine immediate api
+struct ImmediateConcat : Eval {
+ vespalib::string dimension;
+ ImmediateConcat(const vespalib::string &dimension_in) : dimension(dimension_in) {}
+ Result eval(const TensorEngine &engine, const TensorSpec &a, const TensorSpec &b) const override {
+ Stash stash;
+ const auto &lhs = make_value(engine, a, stash);
+ const auto &rhs = make_value(engine, b, stash);
+ return Result(engine.concat(lhs, rhs, dimension, stash));
+ }
+};
+
const size_t tensor_id_a = 11;
const size_t tensor_id_b = 12;
const size_t map_operation_id = 22;
@@ -1013,6 +1033,38 @@ struct TestContext {
//-------------------------------------------------------------------------
+ void test_concat(const TensorSpec &expect,
+ const TensorSpec &a,
+ const TensorSpec &b,
+ const vespalib::string &dimension)
+ {
+ ImmediateConcat eval(dimension);
+ EXPECT_EQUAL(eval.eval(engine, a, b).tensor(), expect);
+ }
+
+ void test_concat() {
+ TEST_DO(test_concat(spec(x(2), Seq({10.0, 20.0})), spec(10.0), spec(20.0), "x"));
+ TEST_DO(test_concat(spec(x(2), Seq({10.0, 20.0})), spec(x(1), Seq({10.0})), spec(20.0), "x"));
+ TEST_DO(test_concat(spec(x(2), Seq({10.0, 20.0})), spec(10.0), spec(x(1), Seq({20.0})), "x"));
+ TEST_DO(test_concat(spec(x(5), Seq({1.0, 2.0, 3.0, 4.0, 5.0})),
+ spec(x(3), Seq({1.0, 2.0, 3.0})),
+ spec(x(2), Seq({4.0, 5.0})), "x"));
+ TEST_DO(test_concat(spec({x(2),y(4)}, Seq({1.0, 2.0, 5.0, 6.0, 3.0, 4.0, 5.0, 6.0})),
+ spec({x(2),y(2)}, Seq({1.0, 2.0, 3.0, 4.0})),
+ spec(y(2), Seq({5.0, 6.0})), "y"));
+ TEST_DO(test_concat(spec({x(4),y(2)}, Seq({1.0, 2.0, 3.0, 4.0, 5.0, 5.0, 6.0, 6.0})),
+ spec({x(2),y(2)}, Seq({1.0, 2.0, 3.0, 4.0})),
+ spec(x(2), Seq({5.0, 6.0})), "x"));
+ TEST_DO(test_concat(spec({x(2),y(2),z(3)}, Seq({1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 4.0, 4.0, 4.0, 5.0, 5.0, 5.0})),
+ spec(z(3), Seq({1.0, 2.0, 3.0})),
+ spec(y(2), Seq({4.0, 5.0})), "x"));
+ TEST_DO(test_concat(spec({x(2), y(2)}, Seq({1.0, 2.0, 4.0, 5.0})),
+ spec(y(3), Seq({1.0, 2.0, 3.0})),
+ spec(y(2), Seq({4.0, 5.0})), "x"));
+ }
+
+ //-------------------------------------------------------------------------
+
void run_tests() {
TEST_DO(test_tensor_create_type());
TEST_DO(test_tensor_equality());
@@ -1021,6 +1073,7 @@ struct TestContext {
TEST_DO(test_tensor_map());
TEST_DO(test_tensor_apply());
TEST_DO(test_dot_product());
+ TEST_DO(test_concat());
}
};
diff --git a/vespalib/src/vespa/vespalib/eval/value_type.cpp b/vespalib/src/vespa/vespalib/eval/value_type.cpp
index ec6bb969289..a038ee46583 100644
--- a/vespalib/src/vespa/vespalib/eval/value_type.cpp
+++ b/vespalib/src/vespa/vespalib/eval/value_type.cpp
@@ -12,22 +12,23 @@ namespace {
using Dimension = ValueType::Dimension;
using DimensionList = std::vector<Dimension>;
-const Dimension *find_dimension(const std::vector<Dimension> &list, const vespalib::string &name) {
- for (const auto &item: list) {
- if (item.name == name) {
- return &item;
+size_t my_dimension_index(const std::vector<Dimension> &list, const vespalib::string &name) {
+ for (size_t idx = 0; idx < list.size(); ++idx) {
+ if (list[idx].name == name) {
+ return idx;
}
}
- return nullptr;
+ return ValueType::Dimension::npos;
}
Dimension *find_dimension(std::vector<Dimension> &list, const vespalib::string &name) {
- for (auto &item: list) {
- if (item.name == name) {
- return &item;
- }
- }
- return nullptr;
+ size_t idx = my_dimension_index(list, name);
+ return (idx != ValueType::Dimension::npos) ? &list[idx] : nullptr;
+}
+
+const Dimension *find_dimension(const std::vector<Dimension> &list, const vespalib::string &name) {
+ size_t idx = my_dimension_index(list, name);
+ return (idx != ValueType::Dimension::npos) ? &list[idx] : nullptr;
}
void sort_dimensions(DimensionList &dimensions) {
@@ -132,6 +133,11 @@ ValueType::is_dense() const
return true;
}
+size_t
+ValueType::dimension_index(const vespalib::string &name) const {
+ return my_dimension_index(_dimensions, name);
+}
+
std::vector<vespalib::string>
ValueType::dimension_names() const
{
diff --git a/vespalib/src/vespa/vespalib/eval/value_type.h b/vespalib/src/vespa/vespalib/eval/value_type.h
index 820e8bcf5dc..f6d02336daa 100644
--- a/vespalib/src/vespa/vespalib/eval/value_type.h
+++ b/vespalib/src/vespa/vespalib/eval/value_type.h
@@ -54,6 +54,7 @@ public:
bool is_sparse() const;
bool is_dense() const;
const std::vector<Dimension> &dimensions() const { return _dimensions; }
+ size_t dimension_index(const vespalib::string &name) const;
std::vector<vespalib::string> dimension_names() const;
bool maybe_tensor() const { return (is_any() || is_tensor()); }
bool unknown_dimensions() const { return (maybe_tensor() && _dimensions.empty()); }
diff --git a/vespalib/src/vespa/vespalib/tensor/default_tensor_engine.cpp b/vespalib/src/vespa/vespalib/tensor/default_tensor_engine.cpp
index bf1645f848e..13e645a2b48 100644
--- a/vespalib/src/vespa/vespalib/tensor/default_tensor_engine.cpp
+++ b/vespalib/src/vespa/vespalib/tensor/default_tensor_engine.cpp
@@ -13,6 +13,7 @@
namespace vespalib {
namespace tensor {
+using Value = eval::Value;
using ErrorValue = eval::ErrorValue;
using DoubleValue = eval::DoubleValue;
using TensorValue = eval::TensorValue;
@@ -220,5 +221,14 @@ DefaultTensorEngine::apply(const BinaryOperation &op, const Tensor &a, const Ten
}
}
+const Value &
+DefaultTensorEngine::concat(const Value &a, const Value &b, const vespalib::string &dimension, Stash &stash) const
+{
+ (void) a;
+ (void) b;
+ (void) dimension;
+ return stash.create<ErrorValue>();
+}
+
} // namespace vespalib::tensor
} // namespace vespalib
diff --git a/vespalib/src/vespa/vespalib/tensor/default_tensor_engine.h b/vespalib/src/vespa/vespalib/tensor/default_tensor_engine.h
index 8e6ea39a625..44e4532a6d5 100644
--- a/vespalib/src/vespa/vespalib/tensor/default_tensor_engine.h
+++ b/vespalib/src/vespa/vespalib/tensor/default_tensor_engine.h
@@ -30,6 +30,8 @@ public:
const Value &reduce(const Tensor &tensor, const BinaryOperation &op, const std::vector<vespalib::string> &dimensions, Stash &stash) const override;
const Value &map(const UnaryOperation &op, const Tensor &a, Stash &stash) const override;
const Value &apply(const BinaryOperation &op, const Tensor &a, const Tensor &b, Stash &stash) const override;
+
+ const Value &concat(const Value &a, const Value &b, const vespalib::string &dimension, Stash &stash) const override;
};
} // namespace vespalib::tensor