File: tensor.cweb

package info (click to toggle)
dynare 4.5.7-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 49,408 kB
  • sloc: cpp: 84,998; ansic: 29,058; pascal: 13,843; sh: 4,833; objc: 4,236; yacc: 3,622; makefile: 2,278; lex: 1,541; python: 236; lisp: 69; xml: 8
file content (229 lines) | stat: -rw-r--r-- 6,258 bytes parent folder | download | duplicates (5)
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.