1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
|
@q $Id: tensor.cweb 429 2005-08-16 15:20:09Z kamenik $ @>
@q Copyright 2004, Ondra Kamenik @>
@ Start of {\tt tensor.cpp} file.
@c
#include "tensor.h"
#include "tl_exception.h"
#include "tl_static.h"
@<|Tensor| static methods@>;
@<|Tensor::noverseq_ip| static method@>;
@<|UTensor::increment| code 1@>;
@<|UTensor::decrement| code 1@>;
@<|UTensor::increment| code 2@>;
@<|UTensor::decrement| code 2@>;
@<|UTensor::getOffset| code 1@>;
@<|UTensor::getOffset| code 2@>;
@<|FTensor::decrement| code@>;
@<|FTensor::getOffsetRecurse| code@>;
@ Here we implement calculation of $\pmatrix{n\cr k}$ where $n-k$ is
usually bigger than $k$.
Also we implement $a^b$.
@<|Tensor| static methods@>=
int Tensor::noverk(int n, int k)
{
return tls.ptriang->noverk(n,k);
}
@#
int Tensor::power(int a, int b)
{
int res = 1;
for (int i = 0; i < b; i++)
res *= a;
return res;
}
@ Here we calculate a generalized combination number
$\left(\matrix{a\cr b_1,\ldots,b_n}\right)$, where $a=b_1+\ldots+
b_n$. We use the identity
$$\left(\matrix{a\cr b_1,\ldots,b_n}\right)=\left(\matrix{b_1+b_2\cr b_1}\right)\cdot
\left(\matrix{a\cr b_1+b_2,b_3,\ldots,b_n}\right)$$
This number is exactly a number of unfolded indices corresponding to
one folded index, where the sequence $b_1,\ldots,b_n$ is the symmetry
of the index.
@<|Tensor::noverseq_ip| static method@>=
int Tensor::noverseq_ip(IntSequence& s)
{
if (s.size() == 0 || s.size() == 1)
return 1;
s[1] += s[0];
return noverk(s[1],s[0]) * noverseq(IntSequence(s, 1, s.size()));
}
@ Here we increment a given sequence within full symmetry given by
|nv|, which is number of variables in each dimension. The underlying
tensor is unfolded, so we increase the rightmost by one, and if it is
|nv| we zero it and increase the next one to the left.
@<|UTensor::increment| code 1@>=
void UTensor::increment(IntSequence& v, int nv)
{
if (v.size() == 0)
return;
int i = v.size()-1;
v[i]++;
while (i > 0 && v[i] == nv) {
v[i] = 0;
v[--i]++;
}
}
@ This is dual to |UTensor::increment(IntSequence& v, int nv)|.
@<|UTensor::decrement| code 1@>=
void UTensor::decrement(IntSequence& v, int nv)
{
if (v.size() == 0)
return;
int i = v.size()-1;
v[i]--;
while (i > 0 && v[i] == -1) {
v[i] = nv -1;
v[--i]--;
}
}
@ Here we increment index for general symmetry for unfolded
storage. The sequence |nvmx| assigns for each coordinate a number of
variables. Since the storage is unfolded, we do not need information
about what variables are symmetric, everything necessary is given by
|nvmx|.
@<|UTensor::increment| code 2@>=
void UTensor::increment(IntSequence& v, const IntSequence& nvmx)
{
if (v.size() == 0)
return;
int i = v.size()-1;
v[i]++;
while (i > 0 && v[i] == nvmx[i]) {
v[i] = 0;
v[--i]++;
}
}
@ This is a dual code to |UTensor::increment(IntSequence& v, const
IntSequence& nvmx)|.
@<|UTensor::decrement| code 2@>=
void UTensor::decrement(IntSequence& v, const IntSequence& nvmx)
{
if (v.size() == 0)
return;
int i = v.size()-1;
v[i]--;
while (i > 0 && v[i] == -1) {
v[i] = nvmx[i] -1;
v[--i]--;
}
}
@ Here we return an offset for a given coordinates of unfolded full
symmetry tensor. This is easy.
@<|UTensor::getOffset| code 1@>=
int UTensor::getOffset(const IntSequence& v, int nv)
{
int pow = 1;
int res = 0;
for (int i = v.size()-1; i >= 0; i--) {
res += v[i]*pow;
pow *= nv;
}
return res;
}
@ Also easy.
@<|UTensor::getOffset| code 2@>=
int UTensor::getOffset(const IntSequence& v, const IntSequence& nvmx)
{
int pow = 1;
int res = 0;
for (int i = v.size()-1; i >= 0; i--) {
res += v[i]*pow;
pow *= nvmx[i];
}
return res;
}
@ Decrementing of coordinates of folded index is not that easy. Note
that if a trailing part of coordinates is $(b, a, a, a)$ (for
instance) with $b<a$, then a preceding coordinates are $(b, a-1, n-1,
n-1)$, where $n$ is a number of variables |nv|. So we find the left
most element which is equal to the last element, decrease it by one,
and then set all elements to the right to $n-1$.
@<|FTensor::decrement| code@>=
void FTensor::decrement(IntSequence& v, int nv)
{
int i = v.size()-1;
while (i > 0 && v[i-1]==v[i])
i--;
v[i]--;
for (int j = i+1; j < v.size(); j++)
v[j] = nv-1;
}
@ This calculates order of the given index of our ordering of
indices. In order to understand how it works, let us take number of
variables $n$ and dimension $k$, and write down all the possible
combinations of indices in our ordering. For example for $n=4$ and
$k=3$, the sequence looks as:
\def\tr#1#2#3{\hbox{\rlap{#1}\hskip 0.7em\rlap{#2}\hskip 0.7em\rlap{#3}\hskip 0.7em}}
\halign{\tabskip=3em \hskip2cm #&#&#&#\cr
\tr 000 &\tr 111 &\tr 222 &\tr 333\cr
\tr 001 &\tr 112 &\tr 223 \cr
\tr 002 &\tr 113 &\tr 233 \cr
\tr 003 &\tr 122 \cr
\tr 011 &\tr 123\cr
\tr 012 &\tr 133\cr
\tr 013\cr
\tr 022\cr
\tr 023\cr
\tr 033\cr
}
Now observe, that a number of sequences starting with zero is the same
as total number of sequences with the same number of variables but
with dimension minus one. More generally, if $S_{n,k}$ denotes number
of indices of $n$ variables and dimension $k$, then the number of
indices beginning with $m$ is exactly $S_{n-m,k-1}$. This is because $m$
can be subtracted from all items, and we obtain sequence of indices of
$n-m$ variables. So we have formula:
$$S_{n,k}=S_{n,k-1}+S_{n-1,k-1}+\ldots+S_{1,k-1}$$
Now it is easy to calculate offset of index of the form
$(m,\ldots,m)$. It is a sum of all above it, this is
$S_{n,k-1}+\ldots+S_{n-m,k-1}$. We know that $S_{n,k}=\pmatrix{n+k-1\cr
k}$. Using above formula, we can calculate offset of $(m,\ldots,m)$ as
$$\pmatrix{n+k-1\cr k}-\pmatrix{n-m+k-1\cr k}$$
The offset of general index $(m_1,m_2,\ldots,m_k)$ is calculated
recursively, since it is offset of $(m_1,\ldots,m_1)$ for $n$
variables plus offset of $(m_2-m_1,m_3-m_1,\ldots,m_k-m_1)$ for
$n-m_1$ variables.
@<|FTensor::getOffsetRecurse| code@>=
int FTensor::getOffsetRecurse(IntSequence& v, int nv)
{
if (v.size() == 0) return 0;
int prefix = v.getPrefixLength();
int m = v[0];
int k = v.size();
int s1 = noverk(nv+k-1,k) - noverk(nv-m+k-1,k);
IntSequence subv(v, prefix, k);
subv.add(-m);
int s2 = getOffsetRecurse(subv, nv-m);
return s1+s2;
}
@ End of {\tt tensor.cpp} file.
|