253 lines
9.3 KiB
Plaintext
253 lines
9.3 KiB
Plaintext
@q $Id: tensor.hweb 741 2006-05-09 11:12:46Z kamenik $ @>
|
|
@q Copyright 2004, Ondra Kamenik @>
|
|
|
|
@*2 Tensor concept. Start of {\tt tensor.h} file.
|
|
|
|
Here we define a tensor class. Tensor is a mathematical object
|
|
corresponding to a $(n+1)$-dimensional array. An element of such array
|
|
is denoted $[B]_{\alpha_1\ldots\alpha_n}^\beta$, where $\beta$ is a
|
|
special index and $\alpha_1\ldots\alpha_n$ are other indices. The
|
|
class |Tensor| and its subclasses view such array as a 2D matrix,
|
|
where $\beta$ corresponds to one dimension, and
|
|
$\alpha_1\ldots\alpha_2$ unfold to the other dimension. Whether
|
|
$\beta$ correspond to rows or columns is decided by tensor subclasses,
|
|
however, most of our tensors will have rows indexed by $\beta$, and
|
|
$\alpha_1\ldots\alpha_n$ will unfold column-wise.
|
|
|
|
There might be some symmetries in the tensor data. For instance, if
|
|
$\alpha_1$ is interchanged with $\alpha_3$ and the both elements equal
|
|
for all possible $\alpha_i$, and $\beta$, then there is a symmetry
|
|
of $\alpha_1$ and $\alpha_3$.
|
|
|
|
For any symmetry, there are basically two possible storages of the
|
|
data. The first is unfolded storage, which stores all elements
|
|
regardless the symmetry. The other storage type is folded, which
|
|
stores only elements which do not repeat. We declare abstract classes
|
|
for unfolded tensor, and folded tensor.
|
|
|
|
Also, here we also define a concept of tensor index which is the
|
|
$n$-tuple $\alpha_1\ldots\alpha_n$. It is an iterator, which iterates
|
|
in dependence of symmetry and storage of the underlying tensor.
|
|
|
|
Although we do not decide about possible symmetries at this point, it
|
|
is worth noting that we implement two kinds of symmetries. The first
|
|
one is a full symmetry where all indices are interchangeable. The
|
|
second one is a generalization of the first. We define tensor of a
|
|
symmetry, where there are a few groups of indices interchangeable
|
|
within a group and not across. Moreover, the groups are required to be
|
|
consequent partitions of the index $n$-tuple. This is, we do not allow
|
|
$\alpha_1$ be interchangeable with $\alpha_3$ and not with $\alpha_2$
|
|
at the same time.
|
|
|
|
However, some intermediate results are, in fact, tensors of a symmetry
|
|
not fitting to our concept. We develop the tensor abstraction for it,
|
|
but these objects are not used very often. They have limited usage
|
|
due to their specialized constructor.
|
|
|
|
@c
|
|
|
|
#ifndef TENSOR_H
|
|
#define TENSOR_H
|
|
|
|
#include "int_sequence.h"
|
|
#include "twod_matrix.h"
|
|
|
|
@<index class definition@>;
|
|
@<|Tensor| class declaration@>;
|
|
@<|UTensor| class declaration@>;
|
|
@<|FTensor| class declaration@>;
|
|
|
|
#endif
|
|
|
|
@ The index represents $n$-tuple $\alpha_1\ldots\alpha_n$. Since its
|
|
movement is dependent on the underlying tensor (with storage and
|
|
symmetry), we maintain a pointer to that tensor, we maintain the
|
|
$n$-tuple (or coordinates) as |IntSequence| and also we maintain the
|
|
offset number (column, or row) of the index in the tensor. The pointer
|
|
is const, since we do not need to change data through the index.
|
|
|
|
Here we require the |tensor| to implement |increment| and |decrement|
|
|
methods, which calculate following and preceding $n$-tuple. Also, we
|
|
need to calculate offset number from the given coordinates, so the
|
|
tensor must implement method |getOffset|. This method is used only in
|
|
construction of the index from the given coordinates. As the index is
|
|
created, the offset is automatically incremented, and decremented
|
|
together with index. The|getOffset| method can be relatively
|
|
computationally complex. This must be kept in mind. Also we generally
|
|
suppose that n-tuple of all zeros is the first offset (first columns
|
|
or row).
|
|
|
|
What follows is a definition of index class, the only
|
|
interesting point is |operator==| which decides only according to
|
|
offset, not according to the coordinates. This is useful since there
|
|
can be more than one of coordinate representations of past-the-end
|
|
index.
|
|
|
|
@s _Tptr int
|
|
@s _Self int
|
|
|
|
@<index class definition@>=
|
|
template<class _Tptr> class _index {
|
|
typedef _index<_Tptr> _Self;
|
|
_Tptr tensor;
|
|
int offset;
|
|
IntSequence coor;
|
|
public:@;
|
|
_index(_Tptr t, int n)
|
|
: tensor(t), offset(0), coor(n, 0)@+ {}
|
|
_index(_Tptr t, const IntSequence& cr, int c)
|
|
: tensor(t), offset(c), coor(cr)@+ {}
|
|
_index(_Tptr t, const IntSequence& cr)
|
|
: tensor(t), offset(tensor->getOffset(cr)), coor(cr)@+ {}
|
|
_index(const _index& ind)
|
|
: tensor(ind.tensor), offset(ind.offset), coor(ind.coor)@+ {}
|
|
const _Self& operator=(const _Self& in)
|
|
{@+ tensor = in.tensor;@+ offset = in.offset;@+ coor = in.coor;
|
|
return *this;@+}
|
|
_Self& operator++()
|
|
{@+ tensor->increment(coor);@+ offset++;@+ return *this;@+}
|
|
_Self& operator--()
|
|
{@+ tensor->decrement(coor);@+ offset--;@+ return *this;@+}
|
|
int operator*() const
|
|
{@+ return offset;@+}
|
|
bool operator==(const _index& n) const
|
|
{@+ return offset == n.offset;@+}
|
|
bool operator!=(const _index& n) const
|
|
{@+ return offset != n.offset;@+}
|
|
const IntSequence& getCoor() const
|
|
{@+ return coor;@+}
|
|
void print() const
|
|
{@+ printf("%4d: ", offset);@+ coor.print();@+}
|
|
};
|
|
|
|
@ Here is the |Tensor| class, which is nothing else than a simple subclass
|
|
of |TwoDMatrix|. The unique semantically new member is |dim| which is tensor
|
|
dimension (length of $\alpha_1\ldots\alpha_n$). We also declare
|
|
|increment|, |decrement| and |getOffset| methods as pure virtual.
|
|
|
|
We also add members for index begin and index end. This is useful,
|
|
since |begin| and |end| methods do not return instance but only
|
|
references, which prevent making additional copy of index (for example
|
|
in for cycles as |in != end()| which would do a copy of index for each
|
|
cycle). The index begin |in_beg| is constructed as a sequence of all
|
|
zeros, and |in_end| is constructed from the sequence |last| passed to
|
|
the constructor, since it depends on subclasses. Also we have to say,
|
|
along what coordinate is the multidimensional index. This is used only
|
|
for initialization of |in_end|.
|
|
|
|
Also, we declare static auxiliary functions for $\pmatrix{n\cr k}$
|
|
which is |noverk| and $a^b$, which is |power|.
|
|
|
|
@s indor int
|
|
|
|
@<|Tensor| class declaration@>=
|
|
class Tensor : public TwoDMatrix {
|
|
public:@;
|
|
enum indor {along_row, along_col};
|
|
typedef _index<const Tensor*> index;
|
|
protected:@;
|
|
const index in_beg;
|
|
const index in_end;
|
|
int dim;
|
|
public:@;
|
|
Tensor(indor io, const IntSequence& last, int r, int c, int d)
|
|
: TwoDMatrix(r, c),
|
|
in_beg(this, d),
|
|
in_end(this, last, (io == along_row)? r:c),
|
|
dim(d)@+ {}
|
|
Tensor(indor io, const IntSequence& first, const IntSequence& last,
|
|
int r, int c, int d)
|
|
: TwoDMatrix(r, c),
|
|
in_beg(this, first, 0),
|
|
in_end(this, last, (io == along_row)? r:c),
|
|
dim(d)@+ {}
|
|
Tensor(int first_row, int num, Tensor& t)
|
|
: TwoDMatrix(first_row, num, t),
|
|
in_beg(t.in_beg),
|
|
in_end(t.in_end),
|
|
dim(t.dim)@+ {}
|
|
Tensor(const Tensor& t)
|
|
: TwoDMatrix(t),
|
|
in_beg(this, t.in_beg.getCoor(), *(t.in_beg)),
|
|
in_end(this, t.in_end.getCoor(), *(t.in_end)),
|
|
dim(t.dim)@+ {}
|
|
virtual ~Tensor()@+ {}
|
|
virtual void increment(IntSequence& v) const =0;
|
|
virtual void decrement(IntSequence& v) const =0;
|
|
virtual int getOffset(const IntSequence& v) const =0;
|
|
int dimen() const
|
|
{@+ return dim;@+}
|
|
|
|
const index& begin() const
|
|
{@+ return in_beg;@+}
|
|
const index& end() const
|
|
{@+ return in_end;@+}
|
|
|
|
static int noverk(int n, int k);
|
|
static int power(int a, int b);
|
|
static int noverseq(const IntSequence& s)
|
|
{
|
|
IntSequence seq(s);
|
|
return noverseq_ip((IntSequence&)s);
|
|
}
|
|
private:@;
|
|
static int noverseq_ip(IntSequence& s);
|
|
};
|
|
|
|
@ Here is an abstraction for unfolded tensor. We provide a pure
|
|
virtual method |fold| which returns a new instance of folded tensor of
|
|
the same symmetry. Also we provide static methods for incrementing and
|
|
decrementing an index with full symmetry and general symmetry as
|
|
defined above.
|
|
|
|
@<|UTensor| class declaration@>=
|
|
class FTensor;
|
|
class UTensor : public Tensor {
|
|
public:@;
|
|
UTensor(indor io, const IntSequence& last, int r, int c, int d)
|
|
: Tensor(io, last, r, c, d)@+ {}
|
|
UTensor(const UTensor& ut)
|
|
: Tensor(ut)@+ {}
|
|
UTensor(int first_row, int num, UTensor& t)
|
|
: Tensor(first_row, num, t)@+ {}
|
|
virtual ~UTensor()@+ {}
|
|
virtual FTensor& fold() const =0;
|
|
|
|
static void increment(IntSequence& v, int nv);
|
|
static void decrement(IntSequence& v, int nv);
|
|
static void increment(IntSequence& v, const IntSequence& nvmx);
|
|
static void decrement(IntSequence& v, const IntSequence& nvmx);
|
|
static int getOffset(const IntSequence& v, int nv);
|
|
static int getOffset(const IntSequence& v, const IntSequence& nvmx);
|
|
};
|
|
|
|
@ This is an abstraction for folded tensor. It only provides a method
|
|
|unfold|, which returns the unfolded version of the same symmetry, and
|
|
static methods for decrementing indices.
|
|
|
|
We also provide static methods for decrementing the |IntSequence| in
|
|
folded fashion and also calculating an offset for a given
|
|
|IntSequence|. However, this is relatively complex calculation, so
|
|
this should be avoided if possible.
|
|
|
|
@<|FTensor| class declaration@>=
|
|
class FTensor : public Tensor {
|
|
public:@;
|
|
FTensor(indor io, const IntSequence& last, int r, int c, int d)
|
|
: Tensor(io, last, r, c, d)@+ {}
|
|
FTensor(const FTensor& ft)
|
|
: Tensor(ft)@+ {}
|
|
FTensor(int first_row, int num, FTensor& t)
|
|
: Tensor(first_row, num, t)@+ {}
|
|
virtual ~FTensor()@+ {}
|
|
virtual UTensor& unfold() const =0;
|
|
|
|
static void decrement(IntSequence& v, int nv);
|
|
static int getOffset(const IntSequence& v, int nv)
|
|
{@+IntSequence vtmp(v);@+ return getOffsetRecurse(vtmp, nv);@+}
|
|
private:@;
|
|
static int getOffsetRecurse(IntSequence& v, int nv);
|
|
};
|
|
|
|
@ End of {\tt tensor.h} file.
|