diff options
author | Geir Storli <geirstorli@yahoo.no> | 2017-05-09 15:41:29 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-05-09 15:41:29 +0200 |
commit | 39c689d83745455d7105da09500eb7232fde45cd (patch) | |
tree | 669561ea7f1ef6053bbcd811288dc23c9fd60da6 /eval | |
parent | c97203ada807aca5ee4810f1b3f38249bb938263 (diff) | |
parent | 2000d4b2cf1345f7b6f3fa7e1f6a73f97daaad27 (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.cpp | 360 | ||||
-rw-r--r-- | eval/src/vespa/eval/eval/simple_tensor.h | 5 | ||||
-rw-r--r-- | eval/src/vespa/eval/eval/simple_tensor_engine.cpp | 14 | ||||
-rw-r--r-- | eval/src/vespa/eval/eval/simple_tensor_engine.h | 2 | ||||
-rw-r--r-- | eval/src/vespa/eval/eval/tensor_engine.h | 3 | ||||
-rw-r--r-- | eval/src/vespa/eval/eval/test/tensor_conformance.cpp | 35 | ||||
-rw-r--r-- | eval/src/vespa/eval/tensor/default_tensor_engine.cpp | 55 | ||||
-rw-r--r-- | eval/src/vespa/eval/tensor/default_tensor_engine.h | 2 |
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; |