297 lines
9.9 KiB
Plaintext
297 lines
9.9 KiB
Plaintext
|
@q $Id: kron_prod.hweb 2269 2008-11-23 14:33:22Z michel $ @>
|
||
|
@q Copyright 2004, Ondra Kamenik @>
|
||
|
|
||
|
@*2 Kronecker product. Start of {\tt kron\_prod.h} file.
|
||
|
|
||
|
Here we define an abstraction for a Kronecker product of a sequence of
|
||
|
matrices. This is $A_1\otimes\ldots\otimes A_n$. Obviously we do not
|
||
|
store the product in memory. First we need to represent a dimension
|
||
|
of the Kronecker product. Then we represent the Kronecker product,
|
||
|
simply it is the Kronecker product dimension with a vector of
|
||
|
references to the matrices $A_1,\ldots, A_n$.
|
||
|
|
||
|
The main task of this class is to calculate a matrix product
|
||
|
$B\cdot(A_1\otimes A_2\otimes\ldots\otimes A_n)$ which in
|
||
|
our application has much more moderate dimensions than $A_1\otimes
|
||
|
A_2\otimes\ldots\otimes A_n$. We calculate it as
|
||
|
$$B\cdot(A_1\otimes I)\cdot\ldots\cdot(I\otimes A_i\otimes
|
||
|
I)\cdot\ldots\cdot (I\otimes A_n)$$
|
||
|
where dimensions of identity matrices differ and are given by the
|
||
|
chosen order. One can naturally ask, whether there is some optimal
|
||
|
order minimizing maximum storage needed for intermediate
|
||
|
results. The optimal ordering is implemented by class |KronProdAllOptim|.
|
||
|
|
||
|
For this multiplication, we also need to represent products of type
|
||
|
$A\otimes I$, $I\otimes A\otimes I$, and $I\otimes A$.
|
||
|
|
||
|
@s KronProdDimens int
|
||
|
@s KronProd int
|
||
|
|
||
|
@c
|
||
|
|
||
|
#ifndef KRON_PROD_H
|
||
|
#define KRON_PROD_H
|
||
|
|
||
|
#include "twod_matrix.h"
|
||
|
#include "permutation.h"
|
||
|
#include "int_sequence.h"
|
||
|
|
||
|
class KronProdAll;
|
||
|
class KronProdAllOptim;
|
||
|
class KronProdIA;
|
||
|
class KronProdIAI;
|
||
|
class KronProdAI;
|
||
|
|
||
|
@<|KronProdDimens| class declaration@>;
|
||
|
@<|KronProd| class declaration@>;
|
||
|
@<|KronProdAll| class declaration@>;
|
||
|
@<|KronProdAllOptim| class declaration@>;
|
||
|
@<|KronProdIA| class declaration@>;
|
||
|
@<|KronProdAI| class declaration@>;
|
||
|
@<|KronProdIAI| class declaration@>;
|
||
|
|
||
|
#endif
|
||
|
|
||
|
@ |KronProdDimens| maintains a dimension of the Kronecker product. So,
|
||
|
it maintains two sequences, one for rows, and one for columns.
|
||
|
|
||
|
@<|KronProdDimens| class declaration@>=
|
||
|
class KronProdDimens {
|
||
|
friend class KronProdAll;
|
||
|
friend class KronProdAllOptim;
|
||
|
friend class KronProdIA;
|
||
|
friend class KronProdIAI;
|
||
|
friend class KronProdAI;
|
||
|
private:@;
|
||
|
IntSequence rows;
|
||
|
IntSequence cols;
|
||
|
public:@;
|
||
|
@<|KronProdDimens| constructors@>;
|
||
|
@<|KronProdDimens| inline operators@>;
|
||
|
@<|KronProdDimens| inline methods@>;
|
||
|
};
|
||
|
|
||
|
@ We define three constructors. First initializes to a given
|
||
|
dimension, and all rows and cols are set to zeros. Second is a copy
|
||
|
constructor. The third constructor takes dimensions of $A_1\otimes
|
||
|
A_2\otimes\ldots\otimes A_n$, and makes dimensions of $I\otimes
|
||
|
A_i\otimes I$, or $I\otimes A_n$, or $A_1\otimes I$ for a given
|
||
|
$i$. The dimensions of identity matrices are such that
|
||
|
$$A_1\otimes A_2\otimes\ldots\otimes A_n=
|
||
|
(A_1\otimes I)\cdot\ldots\cdot(I\otimes A_i\otimes I)
|
||
|
\cdot\ldots\cdot(I\otimes A_n)$$
|
||
|
Note that the matrices on the right do not commute only because sizes
|
||
|
of identity matrices which are then given by this ordering.
|
||
|
|
||
|
@<|KronProdDimens| constructors@>=
|
||
|
KronProdDimens(int dim)
|
||
|
: rows(dim,0), cols(dim, 0)@+ {}
|
||
|
KronProdDimens(const KronProdDimens& kd)
|
||
|
: rows(kd.rows), cols(kd.cols)@+ {}
|
||
|
KronProdDimens(const KronProdDimens& kd, int i);
|
||
|
|
||
|
@
|
||
|
@<|KronProdDimens| inline operators@>=
|
||
|
const KronProdDimens& operator=(const KronProdDimens& kd)
|
||
|
{@+ rows = kd.rows;@+ cols = kd.cols;@+ return *this;@+}
|
||
|
bool operator==(const KronProdDimens& kd) const
|
||
|
{@+ return rows == kd.rows && cols == kd.cols;@+}
|
||
|
|
||
|
@
|
||
|
@<|KronProdDimens| inline methods@>=
|
||
|
int dimen() const
|
||
|
{@+ return rows.size();@+}
|
||
|
void setRC(int i, int r, int c)
|
||
|
{@+ rows[i] = r;@+ cols[i] = c;@+}
|
||
|
void getRC(int i, int& r, int& c) const
|
||
|
{@+ r = rows[i];@+ c = cols[i];@+}
|
||
|
void getRC(int& r, int& c) const
|
||
|
{@+ r = rows.mult();@+ c = cols.mult();@+}
|
||
|
int nrows() const
|
||
|
{@+ return rows.mult();@+}
|
||
|
int ncols() const
|
||
|
{@+ return cols.mult();@+}
|
||
|
int nrows(int i) const
|
||
|
{@+ return rows[i];@+}
|
||
|
int ncols(int i) const
|
||
|
{@+ return cols[i];@+}
|
||
|
|
||
|
@ Here we define an abstract class for all Kronecker product classes,
|
||
|
which are |KronProdAll| (the most general), |KronProdIA| (for
|
||
|
$I\otimes A$), |KronProdAI| (for $A\otimes I$), and |KronProdIAI| (for
|
||
|
$I\otimes A\otimes I$). The purpose of the super class is to only
|
||
|
define some common methods and common member |kpd| for dimensions and
|
||
|
declare pure virtual |mult| which is implemented by the subclasses.
|
||
|
|
||
|
The class also contains a static method |kronMult|, which calculates a
|
||
|
Kronecker product of two vectors and stores it in the provided
|
||
|
vector. It is useful at a few points of the library.
|
||
|
|
||
|
@<|KronProd| class declaration@>=
|
||
|
class KronProd {
|
||
|
protected:@/
|
||
|
KronProdDimens kpd;
|
||
|
public:@/
|
||
|
KronProd(int dim)
|
||
|
: kpd(dim)@+ {}
|
||
|
KronProd(const KronProdDimens& kd)
|
||
|
: kpd(kd)@+ {}
|
||
|
KronProd(const KronProd& kp)
|
||
|
: kpd(kp.kpd)@+ {}
|
||
|
virtual ~KronProd()@+ {}
|
||
|
|
||
|
int dimen() const
|
||
|
{@+ return kpd.dimen();@+}
|
||
|
|
||
|
virtual void mult(const ConstTwoDMatrix& in, TwoDMatrix& out) const =0;
|
||
|
void mult(const TwoDMatrix& in, TwoDMatrix& out) const
|
||
|
{@+ mult(ConstTwoDMatrix(in), out);@+}
|
||
|
|
||
|
void checkDimForMult(const ConstTwoDMatrix& in, const TwoDMatrix& out) const;
|
||
|
void checkDimForMult(const TwoDMatrix& in, const TwoDMatrix& out) const
|
||
|
{@+ checkDimForMult(ConstTwoDMatrix(in), out);@+}
|
||
|
|
||
|
static void kronMult(const ConstVector& v1, const ConstVector& v2,
|
||
|
Vector& res);
|
||
|
|
||
|
int nrows() const
|
||
|
{@+ return kpd.nrows();@+}
|
||
|
int ncols() const
|
||
|
{@+ return kpd.ncols();@+}
|
||
|
int nrows(int i) const
|
||
|
{@+ return kpd.nrows(i);@+}
|
||
|
int ncols(int i) const
|
||
|
{@+ return kpd.ncols(i);@+}
|
||
|
};
|
||
|
|
||
|
@ |KronProdAll| is a main class of this file. It represents the
|
||
|
Kronecker product $A_1\otimes A_2\otimes\ldots\otimes A_n$. Besides
|
||
|
dimensions, it stores pointers to matrices in |matlist| array. If a
|
||
|
pointer is null, then the matrix is considered to be unit. The array
|
||
|
is set by calls to |setMat| method (for real matrices) or |setUnit|
|
||
|
method (for unit matrices).
|
||
|
|
||
|
The object is constructed by a constructor, which allocates the
|
||
|
|matlist| and initializes dimensions to zeros. Then a caller must feed
|
||
|
the object with matrices by calling |setMat| and |setUnit| repeatedly
|
||
|
for different indices.
|
||
|
|
||
|
We implement the |mult| method of |KronProd|, and a new method
|
||
|
|multRows|, which creates a vector of kronecker product of all rows of
|
||
|
matrices in the object. The rows are given by the |IntSequence|.
|
||
|
|
||
|
@<|KronProdAll| class declaration@>=
|
||
|
class KronProdAll : public KronProd {
|
||
|
friend class KronProdIA;
|
||
|
friend class KronProdIAI;
|
||
|
friend class KronProdAI;
|
||
|
protected:@;
|
||
|
const TwoDMatrix** const matlist;
|
||
|
public:@;
|
||
|
KronProdAll(int dim)
|
||
|
: KronProd(dim), matlist(new const TwoDMatrix*[dim])@+ {}
|
||
|
virtual ~KronProdAll()
|
||
|
{@+ delete [] matlist;@+}
|
||
|
void setMat(int i, const TwoDMatrix& m);
|
||
|
void setUnit(int i, int n);
|
||
|
const TwoDMatrix& getMat(int i) const
|
||
|
{@+ return *(matlist[i]);@+}
|
||
|
|
||
|
void mult(const ConstTwoDMatrix& in, TwoDMatrix& out) const;
|
||
|
Vector* multRows(const IntSequence& irows) const;
|
||
|
private:@;
|
||
|
bool isUnit() const;
|
||
|
};
|
||
|
|
||
|
@ The class |KronProdAllOptim| minimizes memory consumption of the
|
||
|
product $B\cdot(A_1\otimes A_2\otimes\ldots\otimes A_k)$. The
|
||
|
optimization is done by reordering of the matrices $A_1,\ldots,A_k$,
|
||
|
in order to minimize a sum of all storages needed for intermediate
|
||
|
results. The optimal ordering is also nearly optimal with respect to
|
||
|
number of flops.
|
||
|
|
||
|
Let $(m_i,n_i)$ be dimensions of $A_i$. It is easy to observe, that
|
||
|
for $i$-th step we need storage of $r\cdot n_1\cdot\ldots\cdot
|
||
|
n_i\cdot m_{i+1}\cdot\ldots\cdot m_k$, where $r$ is a number of rows
|
||
|
of $B$. To minimize the sum through all $i$ over all permutations of
|
||
|
matrices, it is equivalent to minimize the sum
|
||
|
$\sum_{i=1}^k{m_{i+1}\cdot\ldots\cdot m_k\over n_{i+1}\cdot\ldots\cdot
|
||
|
n_k}$. The optimal ordering will yield ${m_k\over
|
||
|
n_k}\leq{m_{k-1}\over n_{k-1}}\ldots\leq{m_1\over n_1}$.
|
||
|
|
||
|
Now observe, that the number of flops for $i$-th step is $r\cdot
|
||
|
n_1\cdot\ldots\cdot n_i\cdot m_i\cdot\ldots\cdot m_k$. In order to
|
||
|
minimize a number of flops, it is equivalent to minimize
|
||
|
$\sum_{i=1}^km_i{m_{i+1}\cdot\ldots\cdot m_k\over
|
||
|
n_{i+1}\cdot\ldots\cdot n_k}$. Note that, normally, the $m_i$ does not
|
||
|
change as much as $n_{j+1},\ldots,n_k$, so the ordering minimizing the
|
||
|
memory will be nearly optimal with respect to number of flops.
|
||
|
|
||
|
The class |KronProdAllOptim| inherits from |KronProdAll|. A public
|
||
|
method |optimizeOrder| does the reordering. The permutation is stored
|
||
|
in |oper|. So, as long as |optimizeOrder| is not called, the class is
|
||
|
equivalent to |KronProdAll|.
|
||
|
|
||
|
@<|KronProdAllOptim| class declaration@>=
|
||
|
class KronProdAllOptim : public KronProdAll {
|
||
|
protected:@;
|
||
|
Permutation oper;
|
||
|
public:@;
|
||
|
KronProdAllOptim(int dim)
|
||
|
: KronProdAll(dim), oper(dim) @+ {}
|
||
|
void optimizeOrder();
|
||
|
const Permutation& getPer() const
|
||
|
{@+ return oper; @+}
|
||
|
};
|
||
|
|
||
|
@ This class represents $I\otimes A$. We have only one reference to
|
||
|
the matrix, which is set by constructor.
|
||
|
|
||
|
@<|KronProdIA| class declaration@>=
|
||
|
class KronProdIA : public KronProd {
|
||
|
friend class KronProdAll;
|
||
|
const TwoDMatrix& mat;
|
||
|
public:@/
|
||
|
KronProdIA(const KronProdAll& kpa)
|
||
|
: KronProd(KronProdDimens(kpa.kpd, kpa.dimen()-1)),
|
||
|
mat(kpa.getMat(kpa.dimen()-1))
|
||
|
{}
|
||
|
void mult(const ConstTwoDMatrix& in, TwoDMatrix& out) const;
|
||
|
};
|
||
|
|
||
|
@ This class represents $A\otimes I$. We have only one reference to
|
||
|
the matrix, which is set by constructor.
|
||
|
|
||
|
@<|KronProdAI| class declaration@>=
|
||
|
class KronProdAI : public KronProd {
|
||
|
friend class KronProdIAI;
|
||
|
friend class KronProdAll;
|
||
|
const TwoDMatrix& mat;
|
||
|
public:@/
|
||
|
KronProdAI(const KronProdAll& kpa)
|
||
|
: KronProd(KronProdDimens(kpa.kpd, 0)),
|
||
|
mat(kpa.getMat(0))
|
||
|
{}
|
||
|
KronProdAI(const KronProdIAI& kpiai);
|
||
|
|
||
|
void mult(const ConstTwoDMatrix& in, TwoDMatrix& out) const;
|
||
|
};
|
||
|
|
||
|
@ This class represents $I\otimes A\otimes I$. We have only one reference to
|
||
|
the matrix, which is set by constructor.
|
||
|
@<|KronProdIAI| class declaration@>=
|
||
|
class KronProdIAI : public KronProd {
|
||
|
friend class KronProdAI;
|
||
|
friend class KronProdAll;
|
||
|
const TwoDMatrix& mat;
|
||
|
public:@/
|
||
|
KronProdIAI(const KronProdAll& kpa, int i)
|
||
|
: KronProd(KronProdDimens(kpa.kpd, i)),
|
||
|
mat(kpa.getMat(i))
|
||
|
{}
|
||
|
void mult(const ConstTwoDMatrix& in, TwoDMatrix& out) const;
|
||
|
};
|
||
|
|
||
|
|
||
|
@ End of {\tt kron\_prod.h} file.
|