Commit 436e1430 authored by Ben Huber's avatar Ben Huber

tensor tutorial (creation of tensors)

parent af2d3b59
Pipeline #706 failed with stages
in 4 minutes and 39 seconds
......@@ -23,7 +23,7 @@ using the `xerus` library though, it might be worthwile to go even further.
The most significant change in runtime speed gains due to compiler settings at this point will come from link-time optimizations
(for `c++`projects using `xerus`).
To make use of them you will need a sufficiently recent versions of the `g++` compiler and `ar` archiver. After compiling the
`libxerus.so` object with the `USE_LTO = TRUE` flag you can then enable `flto` in your own compilation process. The optimizations
`libxerus.so` object with the `USE_LTO = TRUE` flag you can then enable `-flto` in your own compilation process. The optimizations
that will be used then extending more than a single compilation unit and might thus use significant system resources during
compilation.
......
# The Tensor Class
The basic building stone of this library and all Tensor Network methods is the Tensor, represented in the class `xerus::Tensor`.
To simplify the work with these objects, `xerus` contains a number of helper functions that allow the quick creation and
modification of sparse and dense tensors. In the following we will list the most important ones but advise you to also read
the tutorials on [indices and equations]() and [decompositions]() to have the full toolset with which to work on individual
tensors.
## Creation of Tensors
The most basic tensors can be created with the empty constructor
~~~.cpp
A = xerus::Tensor()
~~~
it is of degree 0 and represents the single number 0. Similarly the constructors that take either the degree or a vector of
dimensions as input create (sparse) tensors that are equal to 0 everywhere
~~~.cpp
// creates a 1x1x1 tensor with entry 0
B = xerus::Tensor(3);
// creates a sparse 2x2x2 tensor without any entries
C = xerus::Tensor({2,2,2});
~~~
The latter of these can be forced to create a dense tensor instead which can either be initialized to 0 or uninitialized
~~~.cpp
// creates a dense 2x2x2 tensor with all entries set to 0
D = xerus::Tensor({2,2,2}, xerus::Tensor::Representation::Dense);
// creates a dense 2x2x2 tensor with uninitialized entries
E = xerus::Tensor({2,2,2}, xerus::Tensor::Representation::Dense, xerus::Tensor::Initialisation::None);
~~~
Other common tensors (apart from the 0 tensor) are available through named constructors:
~~~.cpp
// a 2x3x4 tensor with all entries = 1
xerus::Tensor::ones({2,3,4});
// an (3x4) x (3x4) identity operator
xerus::Tensor::identity({3,4,3,4});
// a 3x4x3x4 tensor with superdiagonal = 1 (where all 4 indices coincide) and = 0 otherwise
xerus::Tensor::kronecker({3,4,3,4});
// a 2x2x2 tensor with a 1 in position {1,1,1} and 0 everywhere else
xerus::Tensor::dirac({2,2,2}, {1,1,1});
// a 4x4x4 tensor with i.i.d. Gaussian random values
xerus::Tensor::random({4,4,4});
// a (4x4) x (4x4) random orthogonal operator drawn according to the Haar measure
xerus::Tensor::random_orthogonal({4,4},{4,4});
// a 4x4x4 sparse tensor with 10 random entries in uniformly distributed random positions
xerus::Tensor::random({4,4,4}, 10);
~~~
If the entries of the tensor should be calculated externally, it is possible to either pass the raw data directly (as
`std::unique_ptr<double>` or `std::shared_ptr<double>`) or use a callback / lambda function to populate the entries:
~~~.cpp
std::unique_ptr<double> ptr = foo();
// transfer ownership of the data to the Tensor object of size 2x2x2
// NOTE: make sure that the dimensions are correct as there is no way for xerus to check this!
F = xerus::Tensor({2,2,2}, ptr);
// create a dense 2x2x2 tensor with every entry populated by a callback (lambda) function
G = xerus::Tensor({2,2,2}, [](const std::vector<size_t> &_idx) -> double {
// somehow derive the entry from the index positions in _idx
double result = double( _idx[0] * _idx[1] * _idx[2] );
return result;
});
// create a sparse 10x10x10 tensor with 5 entries determined by a callback (lambda) function
H = xerus::Tensor({10,10,10}, 5, [](size_t num, size_t max) -> std::pair<size_t, double> {
// insert number 1/5, 2/5, 3/5, 4/5, 5/5
// at positions 0 (= {0,0,0}), 17 (= {0,1,7}), 34 (= {0,3,4}) ... respectively
return std::pair<size_t,double>(num*17, double(num)/double(max));
});
~~~
## Used Representation
## Operators and Modifications
## Advanced Use and Ownership of Data
......@@ -50,10 +50,10 @@ namespace xerus {
* @param _lhsTrans Flags whether the LHS should be transposed (in the matrifications sense).
* @param _rhs right hand side of the contraction.
* @param _rhsTrans Flags whether the RHS should be transposed (in the matrifications sense).
* @param _numIndices number of indices that shall be contracted.
* @param _numModes number of modes that shall be contracted.
*/
void contract(Tensor& _result, const Tensor& _lhs, const bool _lhsTrans, const Tensor& _rhs, const bool _rhsTrans, const size_t _numIndices);
Tensor contract(const Tensor& _lhs, const bool _lhsTrans, const Tensor& _rhs, const bool _rhsTrans, const size_t _numIndices);
void contract(Tensor& _result, const Tensor& _lhs, const bool _lhsTrans, const Tensor& _rhs, const bool _rhsTrans, const size_t _numModes);
Tensor contract(const Tensor& _lhs, const bool _lhsTrans, const Tensor& _rhs, const bool _rhsTrans, const size_t _numModes);
......@@ -151,7 +151,7 @@ namespace xerus {
*/
template<XERUS_ADD_MOVE(Vec, DimensionTuple), XERUS_ADD_MOVE(SPtr, std::shared_ptr<value_t>)>
explicit Tensor(Vec&& _dimensions, SPtr&& _data)
: dimensions(std::forward<Vec>(_dimensions)), size(misc::product(dimensions)), representation(Representation::Sparse), denseData(std::forward<SPtr>(_data)) { }
: dimensions(std::forward<Vec>(_dimensions)), size(misc::product(dimensions)), representation(Representation::Dense), denseData(std::forward<SPtr>(_data)) { }
/**
......@@ -198,7 +198,7 @@ namespace xerus {
* @param _N the number of non-zero entries to be created.
* @param _f the function to be used to create each non zero entry.
*/
Tensor(DimensionTuple _dimensions, const size_t _N, const std::function<std::pair<size_t, value_t>(const size_t, const size_t)>& _f);
Tensor(DimensionTuple _dimensions, const size_t _N, const std::function<std::pair<size_t, value_t>(size_t, size_t)>& _f);
/**
......@@ -857,14 +857,14 @@ namespace xerus {
* @param _result Output for the result of the contraction.
* @param _lhs left hand side of the contraction.
* @param _rhs right hand side of the contraction.
* @param _numIndices number of indices that shall be contracted.
* @param _numModes number of indices that shall be contracted.
*/
XERUS_force_inline void contract(Tensor& _result, const Tensor& _lhs, const Tensor& _rhs, const size_t _numIndices) {
contract(_result, _lhs, false, _rhs, false, _numIndices);
XERUS_force_inline void contract(Tensor& _result, const Tensor& _lhs, const Tensor& _rhs, const size_t _numModes) {
contract(_result, _lhs, false, _rhs, false, _numModes);
}
XERUS_force_inline Tensor contract(const Tensor& _lhs, const Tensor& _rhs, const size_t _numIndices) {
return contract(_lhs, false, _rhs, false, _numIndices);
XERUS_force_inline Tensor contract(const Tensor& _lhs, const Tensor& _rhs, const size_t _numModes) {
return contract(_lhs, false, _rhs, false, _numModes);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - Basic arithmetics - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
......
......@@ -1239,25 +1239,25 @@ namespace xerus {
/*- - - - - - - - - - - - - - - - - - - - - - - - - - External functions - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void contract(Tensor& _result, const Tensor& _lhs, const bool _lhsTrans, const Tensor& _rhs, const bool _rhsTrans, const size_t _numIndices) {
REQUIRE(_numIndices <= _lhs.degree() && _numIndices <= _rhs.degree(), "Cannot contract more indices than both tensors have. we have: "
<< _lhs.degree() << " and " << _rhs.degree() << " but want to contract: " << _numIndices);
void contract(Tensor& _result, const Tensor& _lhs, const bool _lhsTrans, const Tensor& _rhs, const bool _rhsTrans, const size_t _numModes) {
REQUIRE(_numModes <= _lhs.degree() && _numModes <= _rhs.degree(), "Cannot contract more indices than both tensors have. we have: "
<< _lhs.degree() << " and " << _rhs.degree() << " but want to contract: " << _numModes);
const size_t lhsRemainOrder = _lhs.degree() - _numIndices;
const size_t lhsRemainStart = _lhsTrans ? _numIndices : 0;
const size_t lhsRemainOrder = _lhs.degree() - _numModes;
const size_t lhsRemainStart = _lhsTrans ? _numModes : 0;
const size_t lhsContractStart = _lhsTrans ? 0 : lhsRemainOrder;
const size_t lhsRemainEnd = lhsRemainStart + lhsRemainOrder;
const size_t rhsRemainOrder = _rhs.degree() - _numIndices;
const size_t rhsRemainStart = _rhsTrans ? 0 : _numIndices;
const size_t rhsRemainOrder = _rhs.degree() - _numModes;
const size_t rhsRemainStart = _rhsTrans ? 0 : _numModes;
IF_CHECK(const size_t rhsContractStart = _rhsTrans ? rhsRemainOrder : 0;)
const size_t rhsRemainEnd = rhsRemainStart + rhsRemainOrder;
REQUIRE(std::equal(_lhs.dimensions.begin() + lhsContractStart, _lhs.dimensions.begin() + lhsContractStart + _numIndices, _rhs.dimensions.begin() + rhsContractStart),
"Dimensions of the be contracted indices do not coincide. " <<_lhs.dimensions << " ("<<_lhsTrans<<") and " << _rhs.dimensions << " ("<<_rhsTrans<<") with " << _numIndices);
REQUIRE(std::equal(_lhs.dimensions.begin() + lhsContractStart, _lhs.dimensions.begin() + lhsContractStart + _numModes, _rhs.dimensions.begin() + rhsContractStart),
"Dimensions of the be contracted indices do not coincide. " <<_lhs.dimensions << " ("<<_lhsTrans<<") and " << _rhs.dimensions << " ("<<_rhsTrans<<") with " << _numModes);
const size_t leftDim = misc::product(_lhs.dimensions, lhsRemainStart, lhsRemainEnd);
const size_t midDim = misc::product(_lhs.dimensions, lhsContractStart, lhsContractStart+_numIndices);
const size_t midDim = misc::product(_lhs.dimensions, lhsContractStart, lhsContractStart+_numModes);
const size_t rightDim = misc::product(_rhs.dimensions, rhsRemainStart, rhsRemainEnd);
......@@ -1341,9 +1341,9 @@ namespace xerus {
}
}
Tensor contract(const Tensor& _lhs, const bool _lhsTrans, const Tensor& _rhs, const bool _rhsTrans, const size_t _numIndices) {
Tensor contract(const Tensor& _lhs, const bool _lhsTrans, const Tensor& _rhs, const bool _rhsTrans, const size_t _numModes) {
Tensor result;
contract(result, _lhs, _lhsTrans, _rhs, _rhsTrans, _numIndices);
contract(result, _lhs, _lhsTrans, _rhs, _rhsTrans, _numModes);
return result;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment