diff options
author | Geir Storli <geirstorli@yahoo.no> | 2016-10-18 08:46:35 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-10-18 08:46:35 +0200 |
commit | 0c754d03ec0f347b7b5ee1782c3c8c5e92fe4283 (patch) | |
tree | eda51b2d4b9ca991271fc527fb8dc860df30cec5 /vespalib | |
parent | 5ef0ed10c25d43949251c1090ee8427f80368c89 (diff) | |
parent | c52d6e557080bb8367d8b74fcd457cbe934c9180 (diff) |
Merge pull request #895 from yahoo/toregge/add-dense-tensor-view
Add dense tensor view, a view to a dense tensor without ownership of
Diffstat (limited to 'vespalib')
13 files changed, 560 insertions, 248 deletions
diff --git a/vespalib/src/vespa/vespalib/tensor/dense/CMakeLists.txt b/vespalib/src/vespa/vespalib/tensor/dense/CMakeLists.txt index c965eb6609c..b7ca224fb90 100644 --- a/vespalib/src/vespa/vespalib/tensor/dense/CMakeLists.txt +++ b/vespalib/src/vespa/vespalib/tensor/dense/CMakeLists.txt @@ -5,5 +5,7 @@ vespa_add_library(vespalib_vespalib_tensor_dense OBJECT dense_tensor.cpp dense_tensor_address_combiner.cpp dense_tensor_builder.cpp + dense_tensor_cells_iterator.cpp + dense_tensor_view.cpp DEPENDS ) diff --git a/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor.cpp b/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor.cpp index b8cb0838fee..88e9d304580 100644 --- a/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor.cpp +++ b/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor.cpp @@ -2,6 +2,7 @@ #include <vespa/fastos/fastos.h> #include "dense_tensor.h" +#include "dense_tensor_view.h" #include "dense_tensor_apply.hpp" #include "dense_tensor_reduce.hpp" #include <vespa/vespalib/util/stringfmt.h> @@ -19,23 +20,6 @@ namespace tensor { namespace { -string -dimensionsAsString(const eval::ValueType &type) -{ - std::ostringstream oss; - bool first = true; - oss << "["; - for (const auto &dim : type.dimensions()) { - if (!first) { - oss << ","; - } - first = false; - oss << dim.name << ":" << dim.size; - } - oss << "]"; - return oss.str(); -} - size_t calcCellsSize(const eval::ValueType &type) { @@ -46,7 +30,6 @@ calcCellsSize(const eval::ValueType &type) return cellsSize; } - void checkCellsSize(const DenseTensor &arg) { @@ -60,63 +43,6 @@ checkCellsSize(const DenseTensor &arg) } } -void -checkDimensions(const DenseTensor &lhs, const DenseTensor &rhs, - vespalib::stringref operation) -{ - if (lhs.type() != rhs.type()) { - throw IllegalStateException(make_string("mismatching dimensions for " - "dense tensor %s, " - "lhs dimensions = '%s', " - "rhs dimensions = '%s'", - operation.c_str(), - dimensionsAsString(lhs.type()).c_str(), - dimensionsAsString(rhs.type()).c_str())); - } - checkCellsSize(lhs); - checkCellsSize(rhs); -} - - -/* - * Join the cells of two tensors. - * - * The given function is used to calculate the resulting cell value - * for overlapping cells. - */ -template <typename Function> -Tensor::UP -joinDenseTensors(const DenseTensor &lhs, const DenseTensor &rhs, - Function &&func) -{ - DenseTensor::Cells cells; - cells.reserve(lhs.cells().size()); - auto rhsCellItr = rhs.cells().cbegin(); - for (const auto &lhsCell : lhs.cells()) { - cells.push_back(func(lhsCell, *rhsCellItr)); - ++rhsCellItr; - } - assert(rhsCellItr == rhs.cells().cend()); - return std::make_unique<DenseTensor>(lhs.type(), - std::move(cells)); -} - -} - - -void -DenseTensor::CellsIterator::next() -{ - ++_cellIdx; - if (valid()) { - for (int64_t i = (_address.size() - 1); i >= 0; --i) { - _address[i] = (_address[i] + 1) % _type.dimensions()[i].size; - if (_address[i] != 0) { - // Outer dimension labels can only be increased when this label wraps around. - break; - } - } - } } DenseTensor::DenseTensor() @@ -166,224 +92,104 @@ DenseTensor::getType() const double DenseTensor::sum() const { - double result = 0.0; - for (const auto &cell : _cells) { - result += cell; - } - return result; + return DenseTensorView(*this).sum(); } Tensor::UP DenseTensor::add(const Tensor &arg) const { - const DenseTensor *rhs = dynamic_cast<const DenseTensor *>(&arg); - if (!rhs) { - return Tensor::UP(); - } - return dense::apply(*this, *rhs, - [](double lhsValue, double rhsValue) - { return lhsValue + rhsValue; }); + return DenseTensorView(*this).add(arg); } Tensor::UP DenseTensor::subtract(const Tensor &arg) const { - const DenseTensor *rhs = dynamic_cast<const DenseTensor *>(&arg); - if (!rhs) { - return Tensor::UP(); - } - return dense::apply(*this, *rhs, - [](double lhsValue, double rhsValue) - { return lhsValue - rhsValue; }); + return DenseTensorView(*this).subtract(arg); } Tensor::UP DenseTensor::multiply(const Tensor &arg) const { - const DenseTensor *rhs = dynamic_cast<const DenseTensor *>(&arg); - if (!rhs) { - return Tensor::UP(); - } - return dense::apply(*this, *rhs, [](double lhsValue, double rhsValue) - { return lhsValue * rhsValue; }); + return DenseTensorView(*this).multiply(arg); } Tensor::UP DenseTensor::min(const Tensor &arg) const { - const DenseTensor *rhs = dynamic_cast<const DenseTensor *>(&arg); - if (!rhs) { - return Tensor::UP(); - } - return dense::apply(*this, *rhs, - [](double lhsValue, double rhsValue) - { return std::min(lhsValue, rhsValue); }); + return DenseTensorView(*this).min(arg); } Tensor::UP DenseTensor::max(const Tensor &arg) const { - const DenseTensor *rhs = dynamic_cast<const DenseTensor *>(&arg); - if (!rhs) { - return Tensor::UP(); - } - return dense::apply(*this, *rhs, - [](double lhsValue, double rhsValue) - { return std::max(lhsValue, rhsValue); }); + return DenseTensorView(*this).max(arg); } Tensor::UP DenseTensor::match(const Tensor &arg) const { - const DenseTensor *rhs = dynamic_cast<const DenseTensor *>(&arg); - if (!rhs) { - return Tensor::UP(); - } - checkDimensions(*this, *rhs, "match"); - return joinDenseTensors(*this, *rhs, - [](double lhsValue, double rhsValue) - { return (lhsValue * rhsValue); }); + return DenseTensorView(*this).match(arg); } Tensor::UP DenseTensor::apply(const CellFunction &func) const { - Cells newCells(_cells.size()); - auto itr = newCells.begin(); - for (const auto &cell : _cells) { - *itr = func.apply(cell); - ++itr; - } - assert(itr == newCells.end()); - return std::make_unique<DenseTensor>(_type, - std::move(newCells)); + return DenseTensorView(*this).apply(func); } Tensor::UP DenseTensor::sum(const vespalib::string &dimension) const { - return dense::reduce(*this, { dimension }, - [](double lhsValue, double rhsValue) - { return lhsValue + rhsValue; }); + return DenseTensorView(*this).sum(dimension); } bool DenseTensor::equals(const Tensor &arg) const { - const DenseTensor *rhs = dynamic_cast<const DenseTensor *>(&arg); - if (!rhs) { - return false; - } - return *this == *rhs; + return DenseTensorView(*this).equals(arg); } vespalib::string DenseTensor::toString() const { - std::ostringstream stream; - stream << *this; - return stream.str(); + return DenseTensorView(*this).toString(); } Tensor::UP DenseTensor::clone() const { - return std::make_unique<DenseTensor>(_type, _cells); -} - -namespace { - -void -buildAddress(const DenseTensor::CellsIterator &itr, TensorSpec::Address &address) -{ - auto addressItr = itr.address().begin(); - for (const auto &dim : itr.type().dimensions()) { - address.emplace(std::make_pair(dim.name, TensorSpec::Label(*addressItr++))); - } - assert(addressItr == itr.address().end()); -} - + return DenseTensorView(*this).clone(); } TensorSpec DenseTensor::toSpec() const { - TensorSpec result(getType().to_spec()); - TensorSpec::Address address; - for (CellsIterator itr(_type, _cells); itr.valid(); itr.next()) { - buildAddress(itr, address); - result.add(address, itr.cell()); - address.clear(); - } - return result; + return DenseTensorView(*this).toSpec(); } void DenseTensor::print(std::ostream &out) const { - // TODO (geirst): print on common format. - out << "[ "; - bool first = true; - for (const auto &dim : _type.dimensions()) { - if (!first) { - out << ", "; - } - out << dim.name << ":" << dim.size; - first = false; - } - out << " ] { "; - first = true; - for (const auto &cell : cells()) { - if (!first) { - out << ", "; - } - out << cell; - first = false; - } - out << " }"; + return DenseTensorView(*this).print(out); } void DenseTensor::accept(TensorVisitor &visitor) const { - DenseTensor::CellsIterator iterator(_type, _cells); - TensorAddressBuilder addressBuilder; - TensorAddress address; - vespalib::string label; - while (iterator.valid()) { - addressBuilder.clear(); - auto rawIndex = iterator.address().begin(); - for (const auto &dimension : _type.dimensions()) { - label = vespalib::make_string("%zu", *rawIndex); - addressBuilder.add(dimension.name, label); - ++rawIndex; - } - address = addressBuilder.build(); - visitor.visit(address, iterator.cell()); - iterator.next(); - } + return DenseTensorView(*this).accept(visitor); } Tensor::UP DenseTensor::apply(const eval::BinaryOperation &op, const Tensor &arg) const { - const DenseTensor *rhs = dynamic_cast<const DenseTensor *>(&arg); - if (!rhs) { - return Tensor::UP(); - } - return dense::apply(*this, *rhs, - [&op](double lhsValue, double rhsValue) - { return op.eval(lhsValue, rhsValue); }); + return DenseTensorView(*this).apply(op, arg); } Tensor::UP DenseTensor::reduce(const eval::BinaryOperation &op, const std::vector<vespalib::string> &dimensions) const { - return dense::reduce(*this, - (dimensions.empty() ? _type.dimension_names() : dimensions), - [&op](double lhsValue, double rhsValue) - { return op.eval(lhsValue, rhsValue); }); + return DenseTensorView(*this).reduce(op, dimensions); } } // namespace vespalib::tensor diff --git a/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor.h b/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor.h index 0a253f398b2..46345dcf202 100644 --- a/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor.h +++ b/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor.h @@ -5,6 +5,7 @@ #include <vespa/vespalib/tensor/tensor.h> #include <vespa/vespalib/tensor/types.h> #include <vespa/vespalib/eval/value_type.h> +#include "dense_tensor_cells_iterator.h" namespace vespalib { namespace tensor { @@ -18,30 +19,7 @@ class DenseTensor : public Tensor public: typedef std::unique_ptr<DenseTensor> UP; using Cells = std::vector<double>; - - class CellsIterator - { - private: - const eval::ValueType &_type; - const Cells &_cells; - size_t _cellIdx; - std::vector<size_t> _address; - - public: - CellsIterator(const eval::ValueType &type_in, - const Cells &cells) - : _type(type_in), - _cells(cells), - _cellIdx(0), - _address(type_in.dimensions().size(), 0) - {} - bool valid() const { return _cellIdx < _cells.size(); } - void next(); - double cell() const { return _cells[_cellIdx]; } - const std::vector<size_t> &address() const { return _address; } - const eval::ValueType &type() const { return _type; } - }; - + using CellsIterator = DenseTensorCellsIterator; private: eval::ValueType _type; diff --git a/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_address_combiner.h b/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_address_combiner.h index 89168e038bc..75336da311b 100644 --- a/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_address_combiner.h +++ b/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_address_combiner.h @@ -2,11 +2,15 @@ #pragma once -#include <vespa/vespalib/tensor/dense/dense_tensor.h> +#include <vespa/vespalib/tensor/tensor.h> +#include <vespa/vespalib/tensor/types.h> +#include <vespa/vespalib/eval/value_type.h> +#include "dense_tensor_cells_iterator.h" namespace vespalib { namespace tensor { + /** * Combines two dense tensor addresses to a new tensor address. * The resulting dimensions is the union of the input dimensions and @@ -24,7 +28,7 @@ private: BOTH }; - using CellsIterator = DenseTensor::CellsIterator; + using CellsIterator = DenseTensorCellsIterator; std::vector<AddressOp> _ops; Address _combinedAddress; diff --git a/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_apply.h b/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_apply.h index 307e1db43d3..d2411cebbd9 100644 --- a/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_apply.h +++ b/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_apply.h @@ -17,7 +17,10 @@ namespace dense { */ template <typename Function> std::unique_ptr<Tensor> -apply(const DenseTensor &lhs, const DenseTensor &rhs, Function &&func); +apply(const DenseTensorView &lhs, const Tensor &rhs, Function &&func); +template <typename Function> +std::unique_ptr<Tensor> +apply(const DenseTensorView &lhs, const DenseTensorView &rhs, Function &&func); } // namespace vespalib::tensor::dense } // namespace vespalib::tensor diff --git a/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_apply.hpp b/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_apply.hpp index 270539f72d8..73a737e6ff3 100644 --- a/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_apply.hpp +++ b/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_apply.hpp @@ -12,12 +12,12 @@ namespace dense { template <typename Function> std::unique_ptr<Tensor> -apply(const DenseTensor &lhs, const DenseTensor &rhs, Function &&func) +apply(const DenseTensorView &lhs, const DenseTensorView &rhs, Function &&func) { DenseTensorAddressCombiner combiner(lhs.type(), rhs.type()); DirectDenseTensorBuilder builder(DenseTensorAddressCombiner::combineDimensions(lhs.type(), rhs.type())); - for (DenseTensor::CellsIterator lhsItr = lhs.cellsIterator(); lhsItr.valid(); lhsItr.next()) { - for (DenseTensor::CellsIterator rhsItr = rhs.cellsIterator(); rhsItr.valid(); rhsItr.next()) { + for (DenseTensorCellsIterator lhsItr = lhs.cellsIterator(); lhsItr.valid(); lhsItr.next()) { + for (DenseTensorCellsIterator rhsItr = rhs.cellsIterator(); rhsItr.valid(); rhsItr.next()) { bool combineSuccess = combiner.combine(lhsItr, rhsItr); if (combineSuccess) { builder.insertCell(combiner.address(), func(lhsItr.cell(), rhsItr.cell())); @@ -27,6 +27,21 @@ apply(const DenseTensor &lhs, const DenseTensor &rhs, Function &&func) return builder.build(); } +template <typename Function> +std::unique_ptr<Tensor> +apply(const DenseTensorView &lhs, const Tensor &rhs, Function &&func) +{ + const DenseTensorView *view = dynamic_cast<const DenseTensorView *>(&rhs); + if (view) { + return apply(lhs, *view, func); + } + const DenseTensor *dense = dynamic_cast<const DenseTensor *>(&rhs); + if (dense) { + return apply(lhs, DenseTensorView(*dense), func); + } + return Tensor::UP(); +} + } // namespace vespalib::tensor::dense } // namespace vespalib::tensor } // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_cells_iterator.cpp b/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_cells_iterator.cpp new file mode 100644 index 00000000000..84311e47e5a --- /dev/null +++ b/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_cells_iterator.cpp @@ -0,0 +1,25 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/fastos/fastos.h> +#include "dense_tensor_cells_iterator.h" + +namespace vespalib { +namespace tensor { + +void +DenseTensorCellsIterator::next() +{ + ++_cellIdx; + if (valid()) { + for (int64_t i = (_address.size() - 1); i >= 0; --i) { + _address[i] = (_address[i] + 1) % _type.dimensions()[i].size; + if (_address[i] != 0) { + // Outer dimension labels can only be increased when this label wraps around. + break; + } + } + } +} + +} // namespace vespalib::tensor +} // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_cells_iterator.h b/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_cells_iterator.h new file mode 100644 index 00000000000..446db249e02 --- /dev/null +++ b/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_cells_iterator.h @@ -0,0 +1,40 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#pragma once + +#include <vespa/vespalib/tensor/tensor.h> +#include <vespa/vespalib/tensor/types.h> +#include <vespa/vespalib/eval/value_type.h> +#include <vespa/vespalib/tensor/tensor.h> + +namespace vespalib { +namespace tensor { + +/** + * Utility class to iterate over cells in a dense tensor. + */ +class DenseTensorCellsIterator +{ +private: + using CellsRef = vespalib::ConstArrayRef<double>; + const eval::ValueType &_type; + CellsRef _cells; + size_t _cellIdx; + std::vector<size_t> _address; + +public: + DenseTensorCellsIterator(const eval::ValueType &type_in, CellsRef cells) + : _type(type_in), + _cells(cells), + _cellIdx(0), + _address(type_in.dimensions().size(), 0) + {} + bool valid() const { return _cellIdx < _cells.size(); } + void next(); + double cell() const { return _cells[_cellIdx]; } + const std::vector<size_t> &address() const { return _address; } + const eval::ValueType &type() const { return _type; } +}; + +} // namespace vespalib::tensor +} // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_reduce.h b/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_reduce.h index ce3bf308fd3..58dccf9dd0b 100644 --- a/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_reduce.h +++ b/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_reduce.h @@ -14,7 +14,7 @@ namespace dense { */ template<typename Function> std::unique_ptr<Tensor> -reduce(const DenseTensor &tensor, const std::vector<vespalib::string> &dimensions, Function &&func); +reduce(const DenseTensorView &tensor, const std::vector<vespalib::string> &dimensions, Function &&func); } // namespace dense } // namespace tensor diff --git a/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_reduce.hpp b/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_reduce.hpp index b072b7ef206..31d77441f75 100644 --- a/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_reduce.hpp +++ b/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_reduce.hpp @@ -7,7 +7,8 @@ namespace vespalib { namespace tensor { namespace dense { -using Cells = DenseTensor::Cells; +using Cells = DenseTensorView::Cells; +using CellsRef = DenseTensorView::CellsRef; namespace { @@ -65,7 +66,7 @@ public: template <typename Function> DenseTensor::UP - reduceCells(const Cells &cellsIn, Function &&func) { + reduceCells(CellsRef cellsIn, Function &&func) { auto itr_in = cellsIn.cbegin(); auto itr_out = _cellsResult.begin(); for (size_t outerDim = 0; outerDim < _outerDimSize; ++outerDim) { @@ -92,7 +93,7 @@ public: template <typename Function> DenseTensor::UP -reduce(const DenseTensor &tensor, const vespalib::string &dimensionToRemove, Function &&func) +reduce(const DenseTensorView &tensor, const vespalib::string &dimensionToRemove, Function &&func) { DimensionReducer reducer(tensor.type(), dimensionToRemove); return reducer.reduceCells(tensor.cells(), func); @@ -102,14 +103,15 @@ reduce(const DenseTensor &tensor, const vespalib::string &dimensionToRemove, Fun template <typename Function> std::unique_ptr<Tensor> -reduce(const DenseTensor &tensor, const std::vector<vespalib::string> &dimensions, Function &&func) +reduce(const DenseTensorView &tensor, const std::vector<vespalib::string> &dimensions, Function &&func) { if (dimensions.size() == 1) { return reduce(tensor, dimensions[0], func); } else if (dimensions.size() > 0) { DenseTensor::UP result = reduce(tensor, dimensions[0], func); for (size_t i = 1; i < dimensions.size(); ++i) { - DenseTensor::UP tmpResult = reduce(*result, dimensions[i], func); + DenseTensor::UP tmpResult = reduce(DenseTensorView(*result), + dimensions[i], func); result = std::move(tmpResult); } return result; diff --git a/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_view.cpp b/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_view.cpp new file mode 100644 index 00000000000..70070ad2fe9 --- /dev/null +++ b/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_view.cpp @@ -0,0 +1,360 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/fastos/fastos.h> +#include "dense_tensor_view.h" +#include "dense_tensor_apply.hpp" +#include "dense_tensor_reduce.hpp" +#include <vespa/vespalib/util/stringfmt.h> +#include <vespa/vespalib/util/exceptions.h> +#include <vespa/vespalib/stllike/asciistream.h> +#include <vespa/vespalib/tensor/tensor_address_builder.h> +#include <vespa/vespalib/tensor/tensor_visitor.h> +#include <vespa/vespalib/eval/operation.h> +#include <sstream> + +using vespalib::eval::TensorSpec; + +namespace vespalib { +namespace tensor { + +namespace { + +string +dimensionsAsString(const eval::ValueType &type) +{ + std::ostringstream oss; + bool first = true; + oss << "["; + for (const auto &dim : type.dimensions()) { + if (!first) { + oss << ","; + } + first = false; + oss << dim.name << ":" << dim.size; + } + oss << "]"; + return oss.str(); +} + +size_t +calcCellsSize(const eval::ValueType &type) +{ + size_t cellsSize = 1; + for (const auto &dim : type.dimensions()) { + cellsSize *= dim.size; + } + return cellsSize; +} + + +void +checkCellsSize(const DenseTensorView &arg) +{ + auto cellsSize = calcCellsSize(arg.type()); + if (arg.cells().size() != cellsSize) { + throw IllegalStateException(make_string("wrong cell size, " + "expected=%zu, " + "actual=%zu", + cellsSize, + arg.cells().size())); + } +} + +void +checkDimensions(const DenseTensorView &lhs, const DenseTensorView &rhs, + vespalib::stringref operation) +{ + if (lhs.type() != rhs.type()) { + throw IllegalStateException(make_string("mismatching dimensions for " + "dense tensor %s, " + "lhs dimensions = '%s', " + "rhs dimensions = '%s'", + operation.c_str(), + dimensionsAsString(lhs.type()).c_str(), + dimensionsAsString(rhs.type()).c_str())); + } + checkCellsSize(lhs); + checkCellsSize(rhs); +} + + +/* + * Join the cells of two tensors. + * + * The given function is used to calculate the resulting cell value + * for overlapping cells. + */ +template <typename Function> +Tensor::UP +joinDenseTensors(const DenseTensorView &lhs, const DenseTensorView &rhs, + Function &&func) +{ + DenseTensor::Cells cells; + cells.reserve(lhs.cells().size()); + auto rhsCellItr = rhs.cells().cbegin(); + for (const auto &lhsCell : lhs.cells()) { + cells.push_back(func(lhsCell, *rhsCellItr)); + ++rhsCellItr; + } + assert(rhsCellItr == rhs.cells().cend()); + return std::make_unique<DenseTensor>(lhs.type(), + std::move(cells)); +} + + +template <typename Function> +Tensor::UP +joinDenseTensors(const DenseTensorView &lhs, const Tensor &rhs, + vespalib::stringref operation, + Function &&func) +{ + const DenseTensorView *view = dynamic_cast<const DenseTensorView *>(&rhs); + if (view) { + checkDimensions(lhs, *view, operation); + return joinDenseTensors(lhs, *view, func); + } + const DenseTensor *dense = dynamic_cast<const DenseTensor *>(&rhs); + if (dense) { + DenseTensorView rhsView(*dense); + checkDimensions(lhs, rhsView, operation); + return joinDenseTensors(lhs, rhsView, func); + } + return Tensor::UP(); +} + +bool sameCells(DenseTensorView::CellsRef lhs, DenseTensorView::CellsRef rhs) +{ + if (lhs.size() != rhs.size()) { + return false; + } + for (size_t i = 0; i < lhs.size(); ++i) { + if (lhs[i] != rhs[i]) { + return false; + } + } + return true; +} + +} + + +DenseTensorView::DenseTensorView(const DenseTensor &rhs) + : _type(rhs.type()), + _cells(rhs.cells()) +{ +} + + +bool +DenseTensorView::operator==(const DenseTensorView &rhs) const +{ + return (_type == rhs._type) && sameCells(_cells, rhs._cells); +} + +eval::ValueType +DenseTensorView::getType() const +{ + return _type; +} + +double +DenseTensorView::sum() const +{ + double result = 0.0; + for (const auto &cell : _cells) { + result += cell; + } + return result; +} + +Tensor::UP +DenseTensorView::add(const Tensor &arg) const +{ + return dense::apply(*this, arg, + [](double lhsValue, double rhsValue) + { return lhsValue + rhsValue; }); +} + +Tensor::UP +DenseTensorView::subtract(const Tensor &arg) const +{ + return dense::apply(*this, arg, + [](double lhsValue, double rhsValue) + { return lhsValue - rhsValue; }); +} + +Tensor::UP +DenseTensorView::multiply(const Tensor &arg) const +{ + return dense::apply(*this, arg, + [](double lhsValue, double rhsValue) + { return lhsValue * rhsValue; }); +} + +Tensor::UP +DenseTensorView::min(const Tensor &arg) const +{ + return dense::apply(*this, arg, + [](double lhsValue, double rhsValue) + { return std::min(lhsValue, rhsValue); }); +} + +Tensor::UP +DenseTensorView::max(const Tensor &arg) const +{ + return dense::apply(*this, arg, + [](double lhsValue, double rhsValue) + { return std::max(lhsValue, rhsValue); }); +} + +Tensor::UP +DenseTensorView::match(const Tensor &arg) const +{ + return joinDenseTensors(*this, arg, "match", + [](double lhsValue, double rhsValue) + { return (lhsValue * rhsValue); }); +} + +Tensor::UP +DenseTensorView::apply(const CellFunction &func) const +{ + Cells newCells(_cells.size()); + auto itr = newCells.begin(); + for (const auto &cell : _cells) { + *itr = func.apply(cell); + ++itr; + } + assert(itr == newCells.end()); + return std::make_unique<DenseTensor>(_type, std::move(newCells)); +} + +Tensor::UP +DenseTensorView::sum(const vespalib::string &dimension) const +{ + return dense::reduce(*this, { dimension }, + [](double lhsValue, double rhsValue) + { return lhsValue + rhsValue; }); +} + +bool +DenseTensorView::equals(const Tensor &arg) const +{ + const DenseTensorView *view = dynamic_cast<const DenseTensorView *>(&arg); + if (view) { + return *this == *view; + } + const DenseTensor *dense = dynamic_cast<const DenseTensor *>(&arg); + if (dense) { + return *this == DenseTensorView(*dense); + } + return false; +} + +vespalib::string +DenseTensorView::toString() const +{ + std::ostringstream stream; + stream << *this; + return stream.str(); +} + +Tensor::UP +DenseTensorView::clone() const +{ + return std::make_unique<DenseTensor>(_type, + Cells(_cells.cbegin(), _cells.cend())); +} + +namespace { + +void +buildAddress(const DenseTensorCellsIterator &itr, TensorSpec::Address &address) +{ + auto addressItr = itr.address().begin(); + for (const auto &dim : itr.type().dimensions()) { + address.emplace(std::make_pair(dim.name, TensorSpec::Label(*addressItr++))); + } + assert(addressItr == itr.address().end()); +} + +} + +TensorSpec +DenseTensorView::toSpec() const +{ + TensorSpec result(getType().to_spec()); + TensorSpec::Address address; + for (CellsIterator itr(_type, _cells); itr.valid(); itr.next()) { + buildAddress(itr, address); + result.add(address, itr.cell()); + address.clear(); + } + return result; +} + +void +DenseTensorView::print(std::ostream &out) const +{ + // TODO (geirst): print on common format. + out << "[ "; + bool first = true; + for (const auto &dim : _type.dimensions()) { + if (!first) { + out << ", "; + } + out << dim.name << ":" << dim.size; + first = false; + } + out << " ] { "; + first = true; + for (const auto &cell : cells()) { + if (!first) { + out << ", "; + } + out << cell; + first = false; + } + out << " }"; +} + +void +DenseTensorView::accept(TensorVisitor &visitor) const +{ + CellsIterator iterator(_type, _cells); + TensorAddressBuilder addressBuilder; + TensorAddress address; + vespalib::string label; + while (iterator.valid()) { + addressBuilder.clear(); + auto rawIndex = iterator.address().begin(); + for (const auto &dimension : _type.dimensions()) { + label = vespalib::make_string("%zu", *rawIndex); + addressBuilder.add(dimension.name, label); + ++rawIndex; + } + address = addressBuilder.build(); + visitor.visit(address, iterator.cell()); + iterator.next(); + } +} + +Tensor::UP +DenseTensorView::apply(const eval::BinaryOperation &op, const Tensor &arg) const +{ + return dense::apply(*this, arg, + [&op](double lhsValue, double rhsValue) + { return op.eval(lhsValue, rhsValue); }); +} + +Tensor::UP +DenseTensorView::reduce(const eval::BinaryOperation &op, + const std::vector<vespalib::string> &dimensions) const +{ + return dense::reduce(*this, + (dimensions.empty() ? _type.dimension_names() : dimensions), + [&op](double lhsValue, double rhsValue) + { return op.eval(lhsValue, rhsValue); }); +} + +} // namespace vespalib::tensor +} // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_view.h b/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_view.h new file mode 100644 index 00000000000..3d2a5eea105 --- /dev/null +++ b/vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_view.h @@ -0,0 +1,61 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#pragma once + +#include <vespa/vespalib/tensor/tensor.h> +#include <vespa/vespalib/tensor/types.h> +#include <vespa/vespalib/eval/value_type.h> +#include "dense_tensor_cells_iterator.h" + +namespace vespalib { +namespace tensor { + +class DenseTensor; + +/** + * A view to a dense tensor where all dimensions are indexed. + * Tensor cells are stored in an underlying array according to the order of the dimensions. + */ +class DenseTensorView : public Tensor +{ +public: + using Cells = std::vector<double>; + using CellsRef = ConstArrayRef<double>; + using CellsIterator = DenseTensorCellsIterator; + +private: + const eval::ValueType &_type; + CellsRef _cells; + +public: + explicit DenseTensorView(const DenseTensor &rhs); + const eval::ValueType &type() const { return _type; } + const CellsRef &cells() const { return _cells; } + bool operator==(const DenseTensorView &rhs) const; + CellsIterator cellsIterator() const { return CellsIterator(_type, _cells); } + + virtual eval::ValueType getType() const override; + virtual double sum() const override; + virtual Tensor::UP add(const Tensor &arg) const override; + virtual Tensor::UP subtract(const Tensor &arg) const override; + virtual Tensor::UP multiply(const Tensor &arg) const override; + virtual Tensor::UP min(const Tensor &arg) const override; + virtual Tensor::UP max(const Tensor &arg) const override; + virtual Tensor::UP match(const Tensor &arg) const override; + virtual Tensor::UP apply(const CellFunction &func) const override; + virtual Tensor::UP sum(const vespalib::string &dimension) const override; + virtual Tensor::UP apply(const eval::BinaryOperation &op, + const Tensor &arg) const override; + virtual Tensor::UP reduce(const eval::BinaryOperation &op, + const std::vector<vespalib::string> &dimensions) + const override; + virtual bool equals(const Tensor &arg) const override; + virtual void print(std::ostream &out) const override; + virtual vespalib::string toString() const override; + virtual Tensor::UP clone() const override; + virtual eval::TensorSpec toSpec() const override; + virtual void accept(TensorVisitor &visitor) const override; +}; + +} // namespace vespalib::tensor +} // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/util/array.h b/vespalib/src/vespa/vespalib/util/array.h index 4df60d68cdc..b010314240a 100644 --- a/vespalib/src/vespa/vespalib/util/array.h +++ b/vespalib/src/vespa/vespalib/util/array.h @@ -29,6 +29,22 @@ private: size_t _sz; }; +template <typename T> +class ConstArrayRef { +public: + ConstArrayRef(const T * v, size_t sz) : _v(v), _sz(sz) { } + ConstArrayRef(const std::vector<T> & v) : _v(&v[0]), _sz(v.size()) { } + const T & operator [] (size_t i) const { return _v[i]; } + size_t size() const { return _sz; } + const T *cbegin() const { return _v; } + const T *cend() const { return _v + _sz; } + const T *begin() const { return _v; } + const T *end() const { return _v + _sz; } +private: + const T *_v; + size_t _sz; +}; + /** * This is a small and compact implementation of a resizeable array. * It has a smaller footprint than std::vector and most important, |