aboutsummaryrefslogtreecommitdiffstats
path: root/vespalib
diff options
context:
space:
mode:
authorGeir Storli <geirstorli@yahoo.no>2016-10-18 08:46:35 +0200
committerGitHub <noreply@github.com>2016-10-18 08:46:35 +0200
commit0c754d03ec0f347b7b5ee1782c3c8c5e92fe4283 (patch)
treeeda51b2d4b9ca991271fc527fb8dc860df30cec5 /vespalib
parent5ef0ed10c25d43949251c1090ee8427f80368c89 (diff)
parentc52d6e557080bb8367d8b74fcd457cbe934c9180 (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')
-rw-r--r--vespalib/src/vespa/vespalib/tensor/dense/CMakeLists.txt2
-rw-r--r--vespalib/src/vespa/vespalib/tensor/dense/dense_tensor.cpp230
-rw-r--r--vespalib/src/vespa/vespalib/tensor/dense/dense_tensor.h26
-rw-r--r--vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_address_combiner.h8
-rw-r--r--vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_apply.h5
-rw-r--r--vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_apply.hpp21
-rw-r--r--vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_cells_iterator.cpp25
-rw-r--r--vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_cells_iterator.h40
-rw-r--r--vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_reduce.h2
-rw-r--r--vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_reduce.hpp12
-rw-r--r--vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_view.cpp360
-rw-r--r--vespalib/src/vespa/vespalib/tensor/dense/dense_tensor_view.h61
-rw-r--r--vespalib/src/vespa/vespalib/util/array.h16
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,