summaryrefslogtreecommitdiffstats
path: root/eval
diff options
context:
space:
mode:
authorGeir Storli <geirstorli@yahoo.no>2017-05-09 15:41:29 +0200
committerGitHub <noreply@github.com>2017-05-09 15:41:29 +0200
commit39c689d83745455d7105da09500eb7232fde45cd (patch)
tree669561ea7f1ef6053bbcd811288dc23c9fd60da6 /eval
parentc97203ada807aca5ee4810f1b3f38249bb938263 (diff)
parent2000d4b2cf1345f7b6f3fa7e1f6a73f97daaad27 (diff)
Merge pull request #2418 from yahoo/havardpe/encode-decode-simple-tensors
binary encode/decode as part of tensor engine API
Diffstat (limited to 'eval')
-rw-r--r--eval/src/vespa/eval/eval/simple_tensor.cpp360
-rw-r--r--eval/src/vespa/eval/eval/simple_tensor.h5
-rw-r--r--eval/src/vespa/eval/eval/simple_tensor_engine.cpp14
-rw-r--r--eval/src/vespa/eval/eval/simple_tensor_engine.h2
-rw-r--r--eval/src/vespa/eval/eval/tensor_engine.h3
-rw-r--r--eval/src/vespa/eval/eval/test/tensor_conformance.cpp35
-rw-r--r--eval/src/vespa/eval/tensor/default_tensor_engine.cpp55
-rw-r--r--eval/src/vespa/eval/tensor/default_tensor_engine.h2
8 files changed, 342 insertions, 134 deletions
diff --git a/eval/src/vespa/eval/eval/simple_tensor.cpp b/eval/src/vespa/eval/eval/simple_tensor.cpp
index 8845974acf7..71cf9b9b24f 100644
--- a/eval/src/vespa/eval/eval/simple_tensor.cpp
+++ b/eval/src/vespa/eval/eval/simple_tensor.cpp
@@ -4,6 +4,7 @@
#include "simple_tensor.h"
#include "simple_tensor_engine.h"
#include "operation.h"
+#include <vespa/vespalib/objects/nbostream.h>
#include <algorithm>
namespace vespalib {
@@ -84,6 +85,34 @@ const vespalib::string &reverse_rename(const vespalib::string &name,
}
/**
+ * Meta information about how a type can be decomposed into mapped and
+ * indexed dimensions and also how large each block is. A block is a
+ * dense-subspace consisting of all indexed dimensions that is
+ * uniquely specified by the labels of all mapped dimensions.
+ **/
+struct TypeMeta {
+ IndexList mapped;
+ IndexList indexed;
+ size_t block_size;
+ explicit TypeMeta(const ValueType &type)
+ : mapped(),
+ indexed(),
+ block_size(1)
+ {
+ for (size_t i = 0; i < type.dimensions().size(); ++i) {
+ const auto &dimension = type.dimensions()[i];
+ if (dimension.is_mapped()) {
+ mapped.push_back(i);
+ } else {
+ block_size *= dimension.size;
+ indexed.push_back(i);
+ }
+ }
+ }
+ ~TypeMeta() {}
+};
+
+/**
* Helper class used when building SimpleTensors. While a tensor
* in its final form simply contains a collection of cells, the
* builder keeps track of cell values as a block map instead. Each
@@ -97,60 +126,55 @@ const vespalib::string &reverse_rename(const vespalib::string &name,
**/
class Builder {
private:
- class Block {
- private:
- const ValueType &_type;
- const IndexList &_indexed;
- std::vector<double> _values;
- size_t offset_of(const Address &address) const {
- size_t offset = 0;
- for (size_t index: _indexed) {
- size_t label = address[index].index;
- size_t size = _type.dimensions()[index].size;
- offset = (offset * size) + label;
- }
- return offset;
- }
- void subconvert(Address &address, size_t n, Cells &cells_out) const {
- if (n < _indexed.size()) {
- Label &label = address[_indexed[n]];
- size_t size = _type.dimensions()[_indexed[n]].size;
- for (label.index = 0; label.index < size; ++label.index) {
- subconvert(address, n + 1, cells_out);
- }
- } else {
- cells_out.emplace_back(address, _values[offset_of(address)]);
- }
+ using Block = std::vector<double>;
+ using BlockMap = std::map<Address,Block>;
+
+ ValueType _type;
+ TypeMeta _meta;
+ BlockMap _blocks;
+
+ size_t offset_of(const Address &address) const {
+ size_t offset = 0;
+ for (size_t index: _meta.indexed) {
+ size_t label = address[index].index;
+ size_t size = _type.dimensions()[index].size;
+ offset = (offset * size) + label;
}
- public:
- Block(const ValueType &type, const IndexList &indexed, size_t num_values)
- : _type(type), _indexed(indexed), _values(num_values, 0.0) {}
- void set(const Address &address, double value) { _values[offset_of(address)] = value; }
- void convert(const Address &block_key, const IndexList &mapped, Cells &cells_out) const {
- Address address(_type.dimensions().size(), Label(size_t(0)));
- for (size_t i = 0; i < mapped.size(); ++i) {
- address[mapped[i]] = block_key[i];
+ return offset;
+ }
+
+ void convert(const Block &block, Address &address, size_t n, Cells &cells_out) const {
+ if (n < _meta.indexed.size()) {
+ Label &label = address[_meta.indexed[n]];
+ size_t size = _type.dimensions()[_meta.indexed[n]].size;
+ for (label.index = 0; label.index < size; ++label.index) {
+ convert(block, address, n + 1, cells_out);
}
- subconvert(address, 0, cells_out);
+ } else {
+ cells_out.emplace_back(address, block[offset_of(address)]);
}
- };
- using BlockMap = std::map<Address,Block>;
- ValueType _type;
- IndexList _mapped;
- IndexList _indexed;
- size_t _block_size;
- BlockMap _blocks;
+ }
+
public:
- explicit Builder(const ValueType &type);
- ~Builder();
+ explicit Builder(const ValueType &type)
+ : _type(type),
+ _meta(type),
+ _blocks()
+ {
+ assert_type(_type);
+ if (_meta.mapped.empty()) {
+ _blocks.emplace(Address(), Block(_meta.block_size, 0.0));
+ }
+ }
+ ~Builder() {}
void set(const Address &address, double value) {
assert_address(address, _type);
- Address block_key = select(address, _mapped);
+ Address block_key = select(address, _meta.mapped);
auto pos = _blocks.find(block_key);
if (pos == _blocks.end()) {
- pos = _blocks.emplace(block_key, Block(_type, _indexed, _block_size)).first;
+ pos = _blocks.emplace(block_key, Block(_meta.block_size, 0.0)).first;
}
- pos->second.set(address, value);
+ pos->second[offset_of(address)] = value;
}
void set(const TensorSpec::Address &label_map, double value) {
Address address;
@@ -163,36 +187,17 @@ public:
}
std::unique_ptr<SimpleTensor> build() {
Cells cells;
+ Address address(_type.dimensions().size(), Label(size_t(0)));
for (const auto &entry: _blocks) {
- entry.second.convert(entry.first, _mapped, cells);
+ for (size_t i = 0; i < _meta.mapped.size(); ++i) {
+ address[_meta.mapped[i]] = entry.first[i];
+ }
+ convert(entry.second, address, 0, cells);
}
return std::make_unique<SimpleTensor>(_type, std::move(cells));
}
};
-Builder::Builder(const ValueType &type)
- : _type(type),
- _mapped(),
- _indexed(),
- _block_size(1),
- _blocks()
-{
- assert_type(_type);
- for (size_t i = 0; i < type.dimensions().size(); ++i) {
- const auto &dimension = _type.dimensions()[i];
- if (dimension.is_mapped()) {
- _mapped.push_back(i);
- } else {
- _block_size *= dimension.size;
- _indexed.push_back(i);
- }
- }
- if (_mapped.empty()) {
- _blocks.emplace(Address(), Block(_type, _indexed, _block_size));
- }
-}
-Builder::~Builder() { }
-
/**
* Helper class used to calculate which dimensions are shared between
* types and which are not. Also calculates how address elements from
@@ -208,57 +213,53 @@ struct TypeAnalyzer {
IndexList combine;
size_t ignored_a;
size_t ignored_b;
- TypeAnalyzer(const ValueType &lhs, const ValueType &rhs, const vespalib::string &ignore = "");
- ~TypeAnalyzer();
-};
-
-TypeAnalyzer::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)
-{
- 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)) {
- if (b[b_idx].name != ignore) {
- only_b.push_back(b_idx);
- combine.push_back(a.size() + b_idx);
+ 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)
+ {
+ 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)) {
+ 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)) {
+ if (a[a_idx].name != ignore) {
+ overlap_a.push_back(a_idx);
+ overlap_b.push_back(b_idx);
+ combine.push_back(a_idx);
+ } else {
+ ignored_a = a_idx;
+ ignored_b = b_idx;
+ }
+ ++b_idx;
} else {
- ignored_b = b_idx;
+ if (a[a_idx].name != ignore) {
+ only_a.push_back(a_idx);
+ combine.push_back(a_idx);
+ } else {
+ ignored_a = a_idx;
+ }
}
- ++b_idx;
}
- if ((b_idx < b.size()) && (b[b_idx].name == a[a_idx].name)) {
- if (a[a_idx].name != ignore) {
- overlap_a.push_back(a_idx);
- overlap_b.push_back(b_idx);
- combine.push_back(a_idx);
+ while (b_idx < b.size()) {
+ if (b[b_idx].name != ignore) {
+ only_b.push_back(b_idx);
+ combine.push_back(a.size() + b_idx);
} else {
- ignored_a = a_idx;
ignored_b = b_idx;
}
++b_idx;
- } else {
- 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()) {
- 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;
- }
-}
-
-TypeAnalyzer::~TypeAnalyzer() { }
+ ~TypeAnalyzer() {}
+};
/**
* A view is a total ordering of cells from a SimpleTensor according
@@ -325,7 +326,7 @@ public:
}
std::sort(_refs.begin(), _refs.end(), _less);
}
- ~View();
+ ~View() {}
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()); }
@@ -333,8 +334,6 @@ public:
EqualRange next_range(const EqualRange &prev) const { return make_range(prev.end()); }
};
-View::~View() { }
-
/**
* Helper class used to find matching EqualRanges from two different
* SimpleTensor Views.
@@ -355,7 +354,7 @@ public:
{
assert(a_selector.size() == b_selector.size());
}
- ~CrossCompare();
+ ~CrossCompare() {}
Result compare(const Cell &a, const Cell &b) const {
for (size_t i = 0; i < a_selector.size(); ++i) {
if (a.address[a_selector[i]] != b.address[b_selector[i]]) {
@@ -405,7 +404,7 @@ public:
{
find_match();
}
- ~ViewMatcher();
+ ~ViewMatcher() {}
bool valid() const { return (has_a() && has_b()); }
const EqualRange &get_a() const { return _a_range; }
const EqualRange &get_b() const { return _b_range; }
@@ -416,9 +415,100 @@ public:
}
};
-ViewMatcher::~ViewMatcher() { }
+struct Format {
+ bool is_sparse;
+ bool is_dense;
+ uint8_t tag;
+ explicit Format(const TypeMeta &meta)
+ : is_sparse(meta.mapped.size() > 0),
+ is_dense((meta.indexed.size() > 0) || !is_sparse),
+ tag((is_sparse ? 0x1 : 0) | (is_dense ? 0x2 : 0)) {}
+ explicit Format(uint8_t tag_in)
+ : is_sparse((tag_in & 0x1) != 0),
+ is_dense((tag_in & 0x2) != 0),
+ tag(tag_in) {}
+ ~Format() {}
+};
+
+void encode_type(nbostream &output, const Format &format, const ValueType &type, const TypeMeta &meta) {
+ if (format.is_sparse) {
+ output.putInt1_4Bytes(meta.mapped.size());
+ for (size_t idx: meta.mapped) {
+ output.writeSmallString(type.dimensions()[idx].name);
+ }
+ }
+ if (format.is_dense) {
+ output.putInt1_4Bytes(meta.indexed.size());
+ for (size_t idx: meta.indexed) {
+ output.writeSmallString(type.dimensions()[idx].name);
+ output.putInt1_4Bytes(type.dimensions()[idx].size);
+ }
+ }
+}
+
+void maybe_encode_num_blocks(nbostream &output, const TypeMeta &meta, size_t num_blocks) {
+ if ((meta.mapped.size() > 0)) {
+ output.putInt1_4Bytes(num_blocks);
+ }
+}
+
+void encode_mapped_labels(nbostream &output, const TypeMeta &meta, const Address &addr) {
+ for (size_t idx: meta.mapped) {
+ output.writeSmallString(addr[idx].name);
+ }
+}
+
+ValueType decode_type(nbostream &input, const Format &format) {
+ std::vector<ValueType::Dimension> dim_list;
+ if (format.is_sparse) {
+ size_t cnt = input.getInt1_4Bytes();
+ for (size_t i = 0; i < cnt; ++i) {
+ vespalib::string name;
+ input.readSmallString(name);
+ dim_list.emplace_back(name);
+ }
+ }
+ if (format.is_dense) {
+ size_t cnt = input.getInt1_4Bytes();
+ for (size_t i = 0; i < cnt; ++i) {
+ vespalib::string name;
+ input.readSmallString(name);
+ dim_list.emplace_back(name, input.getInt1_4Bytes());
+ }
+ }
+ return (dim_list.empty()
+ ? ValueType::double_type()
+ : ValueType::tensor_type(std::move(dim_list)));
+}
+
+size_t maybe_decode_num_blocks(nbostream &input, const TypeMeta &meta, const Format &format) {
+ if ((meta.mapped.size() > 0) || !format.is_dense) {
+ return input.getInt1_4Bytes();
+ }
+ return 1;
+}
-ViewMatcher::CrossCompare::~CrossCompare() { }
+void decode_mapped_labels(nbostream &input, const TypeMeta &meta, Address &addr) {
+ for (size_t idx: meta.mapped) {
+ vespalib::string name;
+ input.readSmallString(name);
+ addr[idx] = Label(name);
+ }
+}
+
+void decode_cells(nbostream &input, const ValueType &type, const TypeMeta meta,
+ Address &address, size_t n, Builder &builder)
+{
+ if (n < meta.indexed.size()) {
+ Label &label = address[meta.indexed[n]];
+ size_t size = type.dimensions()[meta.indexed[n]].size;
+ for (label.index = 0; label.index < size; ++label.index) {
+ decode_cells(input, type, meta, address, n + 1, builder);
+ }
+ } else {
+ builder.set(address, input.readValue<double>());
+ }
+}
} // namespace vespalib::eval::<unnamed>
@@ -594,5 +684,39 @@ SimpleTensor::concat(const SimpleTensor &a, const SimpleTensor &b, const vespali
return builder.build();
}
+void
+SimpleTensor::encode(const SimpleTensor &tensor, nbostream &output)
+{
+ TypeMeta meta(tensor.type());
+ Format format(meta);
+ output << format.tag;
+ encode_type(output, format, tensor.type(), meta);
+ maybe_encode_num_blocks(output, meta, tensor.cells().size() / meta.block_size);
+ View view(tensor, meta.mapped);
+ for (auto block = view.first_range(); !block.empty(); block = view.next_range(block)) {
+ encode_mapped_labels(output, meta, block.begin()->get().address);
+ View subview(block, meta.indexed);
+ for (auto cell = subview.first_range(); !cell.empty(); cell = subview.next_range(cell)) {
+ output << cell.begin()->get().value;
+ }
+ }
+}
+
+std::unique_ptr<SimpleTensor>
+SimpleTensor::decode(nbostream &input)
+{
+ Format format(input.readValue<uint8_t>());
+ ValueType type = decode_type(input, format);
+ TypeMeta meta(type);
+ Builder builder(type);
+ size_t num_blocks = maybe_decode_num_blocks(input, meta, format);
+ Address address(type.dimensions().size(), Label(size_t(0)));
+ for (size_t i = 0; i < num_blocks; ++i) {
+ decode_mapped_labels(input, meta, address);
+ decode_cells(input, type, meta, address, 0, builder);
+ }
+ return builder.build();
+}
+
} // namespace vespalib::eval
} // namespace vespalib
diff --git a/eval/src/vespa/eval/eval/simple_tensor.h b/eval/src/vespa/eval/eval/simple_tensor.h
index a42e39d23a8..51b34813456 100644
--- a/eval/src/vespa/eval/eval/simple_tensor.h
+++ b/eval/src/vespa/eval/eval/simple_tensor.h
@@ -13,6 +13,9 @@
#include <functional>
namespace vespalib {
+
+class nbostream;
+
namespace eval {
struct UnaryOperation;
@@ -84,6 +87,8 @@ public:
static bool equal(const SimpleTensor &a, const SimpleTensor &b);
static std::unique_ptr<SimpleTensor> join(const SimpleTensor &a, const SimpleTensor &b, const std::function<double(double,double)> &function);
static std::unique_ptr<SimpleTensor> concat(const SimpleTensor &a, const SimpleTensor &b, const vespalib::string &dimension);
+ static void encode(const SimpleTensor &tensor, nbostream &output);
+ static std::unique_ptr<SimpleTensor> decode(nbostream &input);
};
} // namespace vespalib::eval
diff --git a/eval/src/vespa/eval/eval/simple_tensor_engine.cpp b/eval/src/vespa/eval/eval/simple_tensor_engine.cpp
index 9e4e7993cde..216df3825e4 100644
--- a/eval/src/vespa/eval/eval/simple_tensor_engine.cpp
+++ b/eval/src/vespa/eval/eval/simple_tensor_engine.cpp
@@ -139,6 +139,20 @@ SimpleTensorEngine::apply(const BinaryOperation &op, const eval::Tensor &a, cons
//-----------------------------------------------------------------------------
+void
+SimpleTensorEngine::encode(const Value &value, nbostream &output, Stash &stash) const
+{
+ SimpleTensor::encode(to_simple(value, stash), output);
+}
+
+const Value &
+SimpleTensorEngine::decode(nbostream &input, Stash &stash) const
+{
+ return to_value(SimpleTensor::decode(input), stash);
+}
+
+//-----------------------------------------------------------------------------
+
const Value &
SimpleTensorEngine::map(const Value &a, const std::function<double(double)> &function, Stash &stash) const
{
diff --git a/eval/src/vespa/eval/eval/simple_tensor_engine.h b/eval/src/vespa/eval/eval/simple_tensor_engine.h
index 95c756ce547..6e2b4a56ce4 100644
--- a/eval/src/vespa/eval/eval/simple_tensor_engine.h
+++ b/eval/src/vespa/eval/eval/simple_tensor_engine.h
@@ -29,6 +29,8 @@ public:
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;
+ void encode(const Value &value, nbostream &output, Stash &stash) const override;
+ const Value &decode(nbostream &input, Stash &stash) const override;
const Value &map(const Value &a, const std::function<double(double)> &function, Stash &stash) const override;
const Value &join(const Value &a, const Value &b, const std::function<double(double,double)> &function, Stash &stash) const override;
const Value &reduce(const Value &a, Aggr aggr, const std::vector<vespalib::string> &dimensions, Stash &stash) const override;
diff --git a/eval/src/vespa/eval/eval/tensor_engine.h b/eval/src/vespa/eval/eval/tensor_engine.h
index 80e463e7989..4b1d8fe4684 100644
--- a/eval/src/vespa/eval/eval/tensor_engine.h
+++ b/eval/src/vespa/eval/eval/tensor_engine.h
@@ -13,6 +13,7 @@
namespace vespalib {
class Stash;
+class nbostream;
namespace eval {
@@ -54,6 +55,8 @@ struct TensorEngine
virtual const Value &apply(const BinaryOperation &op, const Tensor &a, const Tensor &b, Stash &stash) const = 0;
// havardpe: new API, WIP
+ virtual void encode(const Value &value, nbostream &output, Stash &stash) const = 0;
+ virtual const Value &decode(nbostream &input, Stash &stash) const = 0;
virtual const Value &map(const Value &a, const std::function<double(double)> &function, Stash &stash) const = 0;
virtual const Value &join(const Value &a, const Value &b, const std::function<double(double,double)> &function, Stash &stash) const = 0;
virtual const Value &reduce(const Value &a, Aggr aggr, const std::vector<vespalib::string> &dimensions, Stash &stash) const = 0;
diff --git a/eval/src/vespa/eval/eval/test/tensor_conformance.cpp b/eval/src/vespa/eval/eval/test/tensor_conformance.cpp
index 2c18b513730..74dfeaebe31 100644
--- a/eval/src/vespa/eval/eval/test/tensor_conformance.cpp
+++ b/eval/src/vespa/eval/eval/test/tensor_conformance.cpp
@@ -10,6 +10,7 @@
#include <vespa/eval/eval/tensor_function.h>
#include <vespa/eval/eval/interpreted_function.h>
#include <vespa/eval/eval/aggr.h>
+#include <vespa/vespalib/objects/nbostream.h>
namespace vespalib {
namespace eval {
@@ -1219,6 +1220,39 @@ struct TestContext {
//-------------------------------------------------------------------------
+ void verify_encode_decode(const TensorSpec &spec,
+ const TensorEngine &encode_engine,
+ const TensorEngine &decode_engine)
+ {
+ Stash stash;
+ nbostream data;
+ encode_engine.encode(make_value(encode_engine, spec, stash), data, stash);
+ TEST_DO(verify_result(Eval::Result(decode_engine.decode(data, stash)), spec));
+ }
+
+ void verify_encode_decode(const TensorSpec &spec) {
+ TEST_DO(verify_encode_decode(spec, engine, ref_engine));
+ if (&engine != &ref_engine) {
+ TEST_DO(verify_encode_decode(spec, ref_engine, engine));
+ }
+ }
+
+ void test_binary_format() {
+ TEST_DO(verify_encode_decode(spec(42)));
+ TEST_DO(verify_encode_decode(spec({x(3)}, N())));
+ TEST_DO(verify_encode_decode(spec({x(3),y(5)}, N())));
+ TEST_DO(verify_encode_decode(spec({x(3),y(5),z(7)}, N())));
+ TEST_DO(verify_encode_decode(spec({x({"a","b","c"})}, N())));
+ TEST_DO(verify_encode_decode(spec({x({"a","b","c"}),y({"foo","bar"})}, N())));
+ TEST_DO(verify_encode_decode(spec({x({"a","b","c"}),y({"foo","bar"}),z({"i","j","k","l"})}, N())));
+ if (mixed(2)) {
+ TEST_DO(verify_encode_decode(spec({x(3),y({"foo", "bar"}),z(7)}, N())));
+ TEST_DO(verify_encode_decode(spec({x({"a","b","c"}),y(5),z({"i","j","k","l"})}, N())));
+ }
+ }
+
+ //-------------------------------------------------------------------------
+
void run_tests() {
TEST_DO(test_tensor_create_type());
TEST_DO(test_tensor_equality());
@@ -1230,6 +1264,7 @@ struct TestContext {
TEST_DO(test_concat());
TEST_DO(test_rename());
TEST_DO(test_tensor_lambda());
+ TEST_DO(test_binary_format());
}
};
diff --git a/eval/src/vespa/eval/tensor/default_tensor_engine.cpp b/eval/src/vespa/eval/tensor/default_tensor_engine.cpp
index 39b900c3b30..571fe8a293e 100644
--- a/eval/src/vespa/eval/tensor/default_tensor_engine.cpp
+++ b/eval/src/vespa/eval/tensor/default_tensor_engine.cpp
@@ -7,6 +7,8 @@
#include <vespa/eval/eval/operation_visitor.h>
#include <vespa/eval/eval/simple_tensor_engine.h>
#include "tensor.h"
+#include "serialization/typed_binary_format.h"
+#include "dense/dense_tensor.h"
#include "dense/dense_tensor_builder.h"
#include "dense/dense_tensor_function_compiler.h"
#include "default_tensor.h"
@@ -15,11 +17,31 @@ namespace vespalib {
namespace tensor {
using Value = eval::Value;
+using ValueType = eval::ValueType;
using ErrorValue = eval::ErrorValue;
using DoubleValue = eval::DoubleValue;
using TensorValue = eval::TensorValue;
using TensorSpec = eval::TensorSpec;
+namespace {
+
+const Value &to_value(std::unique_ptr<Tensor> tensor, Stash &stash) {
+ if (tensor->getType().is_tensor()) {
+ return stash.create<TensorValue>(std::move(tensor));
+ }
+ return stash.create<DoubleValue>(tensor->sum());
+}
+
+const Tensor &to_tensor(const Value &value, Stash &stash) {
+ if (auto tensor = value.as_tensor()) {
+ return static_cast<const Tensor &>(*tensor);
+ }
+ return stash.create<DenseTensor>(ValueType::double_type(),
+ std::vector<double>({value.as_double()}));
+}
+
+} // namespace vespalib::tensor::<unnamed>
+
const DefaultTensorEngine DefaultTensorEngine::_engine;
eval::ValueType
@@ -142,13 +164,7 @@ DefaultTensorEngine::reduce(const Tensor &tensor, const BinaryOperation &op, con
result = my_tensor.reduce(op, dimensions);
}
if (result) {
- eval::ValueType result_type(result->getType());
- if (result_type.is_tensor()) {
- return stash.create<TensorValue>(std::move(result));
- }
- if (result_type.is_double()) {
- return stash.create<eval::DoubleValue>(result->sum());
- }
+ return to_value(std::move(result), stash);
}
return stash.create<ErrorValue>();
}
@@ -165,11 +181,7 @@ DefaultTensorEngine::map(const UnaryOperation &op, const Tensor &a, Stash &stash
assert(&a.engine() == this);
const tensor::Tensor &my_a = static_cast<const tensor::Tensor &>(a);
CellFunctionAdapter cell_function(op);
- auto result = my_a.apply(cell_function);
- if (result->getType().is_double()) {
- return stash.create<DoubleValue>(result->sum());
- }
- return stash.create<TensorValue>(std::move(result));
+ return to_value(my_a.apply(cell_function), stash);
}
struct TensorOperationOverride : eval::DefaultOperationVisitor {
@@ -221,10 +233,7 @@ DefaultTensorEngine::apply(const BinaryOperation &op, const Tensor &a, const Ten
TensorOperationOverride tensor_override(my_a, my_b);
op.accept(tensor_override);
if (tensor_override.result) {
- if (tensor_override.result->getType().is_double()) {
- return stash.create<DoubleValue>(tensor_override.result->sum());
- }
- return stash.create<TensorValue>(std::move(tensor_override.result));
+ return to_value(std::move(tensor_override.result), stash);
} else {
return stash.create<ErrorValue>();
}
@@ -259,6 +268,20 @@ const Value &to_default(const Value &value, Stash &stash) {
//-----------------------------------------------------------------------------
+void
+DefaultTensorEngine::encode(const Value &value, nbostream &output, Stash &stash) const
+{
+ TypedBinaryFormat::serialize(output, to_tensor(value, stash));
+}
+
+const Value &
+DefaultTensorEngine::decode(nbostream &input, Stash &stash) const
+{
+ return to_value(TypedBinaryFormat::deserialize(input), stash);
+}
+
+//-----------------------------------------------------------------------------
+
const Value &
DefaultTensorEngine::map(const Value &a, const std::function<double(double)> &function, Stash &stash) const
{
diff --git a/eval/src/vespa/eval/tensor/default_tensor_engine.h b/eval/src/vespa/eval/tensor/default_tensor_engine.h
index a61fb9d81fc..c3bcf7ab918 100644
--- a/eval/src/vespa/eval/tensor/default_tensor_engine.h
+++ b/eval/src/vespa/eval/tensor/default_tensor_engine.h
@@ -31,6 +31,8 @@ public:
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;
+ void encode(const Value &value, nbostream &output, Stash &stash) const override;
+ const Value &decode(nbostream &input, Stash &stash) const override;
const Value &map(const Value &a, const std::function<double(double)> &function, Stash &stash) const override;
const Value &join(const Value &a, const Value &b, const std::function<double(double,double)> &function, Stash &stash) const override;
const Value &reduce(const Value &a, Aggr aggr, const std::vector<vespalib::string> &dimensions, Stash &stash) const override;