@q $Id: gs_tensor.hweb 741 2006-05-09 11:12:46Z kamenik $ @>
@q Copyright 2004, Ondra Kamenik @>

@*2 General symmetry tensor. Start of {\tt gs\_tensor.h} file.

Here we define tensors for general symmetry. All tensors from here are
identifying the multidimensional index with columns. Thus all
symmetries regard to columns. The general symmetry here is not the most
general. It captures all symmetries of indices which are given by
continuous partitioning of indices. Two items are symmetric if they
belong to the same group. The continuity implies that if two items
belong to one group, then all items between them belong to that
group. This continuous partitioning of indices is described by
|Symmetry| class.

The dimension of the tensors here are described (besides the symmetry)
also by number of variables for each group. This is dealt in the class
for tensor dimensions defined also here.

@c
#ifndef GS_TENSOR_H
#define GS_TENSOR_H

#include "tensor.h"
#include "fs_tensor.h"
#include "symmetry.h"
#include "rfs_tensor.h"

class FGSTensor;
class UGSTensor;
class FSSparseTensor;

@<|TensorDimens| class declaration@>;
@<|FGSTensor| class declaration@>;
@<|UGSTensor| class declaration@>;

#endif

@ This class encapsulates symmetry information for the general
symmetry tensor. It maintains a vector of variable numbers |nvs|, and
symmetry |sym|. For example, let the symmetry be $y^2u^3$, and
variable numbers be 10 for $y$, and 5 for $u$. Then the |nvs| is
$(10,5)$, and |sym| is $(2,3)$. Also it maintains |nvmax| unfolded |nvs| with
respect to the symmetry, this is $(10,10,5,5,5)$.

The constructors of |TensorDimens| are clear and pretty intuitive but
the constructor which is used for slicing fully symmetric tensor. It
constructs the dimensions from the partitioning of variables of fully
symmetric tensor. Let the partitioning be, for instance, $(a,b,c,d)$,
where $(n_a,n_b,n_c,n_d)$ are lengths of the partitions. Let one want
to get a slice only of the part of the fully symmetric tensor
corresponding to indices of the form $b^2d^3$. This corresponds to the
symmetry $a^0b^2c^0d^3$. So, the dimension of the slice would be also
$(n_a,n_b,n_c,n_d)$ for number of variables and $(0,2,0,3)$ for the
symmetry. So we provide the constructor which takes sizes of
partitions $(n_a,n_b,n_c,n_d)$ as |IntSequence|, and indices of picked
partitions, in our case $(1,1,3,3,3)$, as |IntSequence|.

The class is able to calculate number of offsets (columns or rows depending
what matrix coordinate we describe) in unfolded and folded tensors
with the given symmetry.

@s TensorDimens int

@<|TensorDimens| class declaration@>=
class TensorDimens {
protected:@;
	IntSequence nvs;
	Symmetry sym;
	IntSequence nvmax;
public:@;
	TensorDimens(const Symmetry& s, const IntSequence& nvars)
		: nvs(nvars), sym(s), nvmax(sym, nvs)@+ {}
	TensorDimens(int nvar, int dimen)
		: nvs(1), sym(dimen), nvmax(dimen, nvar)
		{@+ nvs[0] = nvar;@+}
	TensorDimens(const TensorDimens& td)
		: nvs(td.nvs), sym(td.sym), nvmax(td.nvmax)@+ {}
	virtual ~TensorDimens()@+ {}
	TensorDimens(const IntSequence& ss, const IntSequence& coor);
	const TensorDimens& operator=(const TensorDimens& td)
		{@+ nvs = td.nvs;@+ sym = td.sym;@+ nvmax = td.nvmax;@+ return *this;@+}
	bool operator==(const TensorDimens& td) const
		{@+ return nvs == td.nvs && sym == td.sym;@+}
	bool operator!=(const TensorDimens& td) const
		{@+ return !operator==(td);@+}

	int dimen() const
		{@+ return sym.dimen();@+}
	int getNVX(int i) const
		{@+ return nvmax[i];@+}
	const IntSequence& getNVS() const
		{ @+ return nvs;@+}
	const IntSequence& getNVX() const
		{@+ return nvmax;@+}
	const Symmetry& getSym() const
		{@+ return sym;@+}

	int calcUnfoldMaxOffset() const;
	int calcFoldMaxOffset() const;
	int calcFoldOffset(const IntSequence& v) const;
	void decrement(IntSequence& v) const; 
};

@ Here is a class for folded general symmetry tensor. It only contains
tensor dimensions, it defines types for indices, implement virtual
methods of super class |FTensor|.

We add a method |contractAndAdd| which performs a contraction of one
variable in the tensor. This is, for instance
$$\left[r_{x^iz^k}\right]_{\alpha_1\ldots\alpha_i\gamma_1\ldots\gamma_k}=
\left[t_{x^iy^jz^k}\right]_{\alpha_1\ldots\alpha_i\beta_1\ldots\beta_j\gamma_1\ldots\gamma_k}
\left[c\right]^{\beta_1\ldots\beta_j}
$$

Also we add |getOffset| which should be used with care.

@<|FGSTensor| class declaration@>=
class GSSparseTensor;
class FGSTensor : public FTensor {
	friend class UGSTensor;

	const TensorDimens tdims;
public:@;
	@<|FGSTensor| constructor declarations@>;
	virtual ~FGSTensor()@+ {}

	void increment(IntSequence& v) const;
	void decrement(IntSequence& v) const
		{@+ tdims.decrement(v);@+}
	UTensor& unfold() const;
	const TensorDimens& getDims() const
		{@+ return tdims;@+}
	const Symmetry& getSym() const
		{@+ return getDims().getSym();@+}

	void contractAndAdd(int i, FGSTensor& out,
						const FRSingleTensor& col) const;
	int getOffset(const IntSequence& v) const
		{@+ return tdims.calcFoldOffset(v);@+}
};

@ These are standard constructors followed by two slicing. The first
constructs a slice from the sparse, the second from the dense (both
fully symmetric). Next constructor is just a conversion from
|GSSParseTensor|. The last constructor allows for in-place conversion
from |FFSTensor| to |FGSTensor|.

@<|FGSTensor| constructor declarations@>=
	FGSTensor(int r, const TensorDimens& td)
		: FTensor(along_col, td.getNVX(), r,
				  td.calcFoldMaxOffset(), td.dimen()), tdims(td)@+ {}
	FGSTensor(const FGSTensor& ft)
		: FTensor(ft), tdims(ft.tdims)@+ {}
	FGSTensor(const UGSTensor& ut);
	FGSTensor(int first_row, int num, FGSTensor& t)
		: FTensor(first_row, num, t), tdims(t.tdims)@+ {}
	FGSTensor(const FSSparseTensor& t, const IntSequence& ss,
			  const IntSequence& coor, const TensorDimens& td);
	FGSTensor(const FFSTensor& t, const IntSequence& ss,
			  const IntSequence& coor, const TensorDimens& td);
	FGSTensor(const GSSparseTensor& sp);
	FGSTensor(FFSTensor& t)
		: FTensor(0, t.nrows(), t), tdims(t.nvar(), t.dimen())@+ {}


@ Besides similar things that has |FGSTensor|, we have here also
method |unfoldData|, and helper method |getFirstIndexOf|
which corresponds to sorting coordinates in fully symmetric case (here
the action is more complicated, so we put it to the method).

@<|UGSTensor| class declaration@>=
class UGSTensor : public UTensor {
	friend class FGSTensor;

	const TensorDimens tdims;
public:@;
	@<|UGSTensor| constructor declarations@>;
	virtual ~UGSTensor()@+ {}

	void increment(IntSequence& v) const;
	void decrement(IntSequence& v) const;
	FTensor& fold() const;
	const TensorDimens& getDims() const
		{@+ return tdims;@+}
	const Symmetry& getSym() const
		{@+ return getDims().getSym();@+}

	void contractAndAdd(int i, UGSTensor& out,
						const URSingleTensor& col) const;
	int getOffset(const IntSequence& v) const;
private:@;
	void unfoldData();
public:@;
	index getFirstIndexOf(const index& in) const;
};


@ These are standard constructors. The last two constructors are
slicing. The first makes a slice from fully symmetric sparse, the
second from fully symmetric dense unfolded tensor. The last
constructor allows for in-place conversion from |UFSTensor| to
|UGSTensor|.

@<|UGSTensor| constructor declarations@>=
	UGSTensor(int r, const TensorDimens& td)
		: UTensor(along_col, td.getNVX(), r,
				  td.calcUnfoldMaxOffset(), td.dimen()), tdims(td)@+ {}
	UGSTensor(const UGSTensor& ut)
		: UTensor(ut), tdims(ut.tdims)@+ {}
	UGSTensor(const FGSTensor& ft);
	UGSTensor(int first_row, int num, UGSTensor& t)
		: UTensor(first_row,  num, t), tdims(t.tdims)@+ {}
	UGSTensor(const FSSparseTensor& t, const IntSequence& ss,
			  const IntSequence& coor, const TensorDimens& td);
	UGSTensor(const UFSTensor& t, const IntSequence& ss,
			  const IntSequence& coor, const TensorDimens& td);
	UGSTensor(UFSTensor& t)
		: UTensor(0, t.nrows(), t), tdims(t.nvar(), t.dimen())@+ {}


@ End of {\tt gs\_tensor.h} file.
