File: t_polynomial.hweb

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 (507 lines) | stat: -rw-r--r-- 18,098 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
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
@q $Id: t_polynomial.hweb 2336 2009-01-14 10:37:02Z kamenik $ @>
@q Copyright 2004, Ondra Kamenik @>

@*2 Tensor polynomial evaluation. Start of {\tt t\_polynomial.h} file.

We need to evaluate a tensor polynomial of the form:
$$
\left[g_{x}\right]_{\alpha_1}[x]^{\alpha_1}+
\left[g_{x^2}\right]_{\alpha_1\alpha_2}[x]^{\alpha_1}[x]^{\alpha_2}+
\ldots+
\left[g_{x^n}\right]_{\alpha_1\ldots\alpha_n}\prod_{i=1}^n[x]^{\alpha_i}
$$
where $x$ is a column vector.

We have basically two options. The first is to use the formula above,
the second is to use a Horner-like formula:
$$
\left[\cdots\left[\left[\left[g_{x^{n-1}}\right]+
\left[g_{x^n}\right]_{\alpha_1\ldots\alpha_{n-1}\alpha_n}
[x]^{\alpha_n}\right]_{\alpha_1\ldots\alpha_{n-2}\alpha_{n-1}}
[x]^{\alpha_{n-1}}\right]\cdots\right]_{\alpha_1}
[x]^{\alpha_1}
$$

Alternativelly, we can put the the polynomial into a more compact form
$$\left[g_{x}\right]_{\alpha_1}[x]^{\alpha_1}+
\left[g_{x^2}\right]_{\alpha_1\alpha_2}[x]^{\alpha_1}[x]^{\alpha_2}+
\ldots+
\left[g_{x^n}\right]_{\alpha_1\ldots\alpha_n}\prod_{i=1}^n[x]^{\alpha_i}
= [G]_{\alpha_1\ldots\alpha_n}\prod_{i=1}^n\left[\matrix{1\cr x}\right]^{\alpha_i}
$$
Then the polynomial evaluation becomes just a matrix multiplication of the vector power.

Here we define the tensor polynomial as a container of full symmetry
tensors and add an evaluation methods. We have two sorts of
containers, folded and unfolded. For each type we declare two methods
implementing the above formulas. We define classes for the
compactification of the polynomial. The class derives from the tensor
and has a eval method.


@s PowerProvider int
@s TensorPolynomial int
@s UTensorPolynomial int
@s FTensorPolynomial int
@s CompactPolynomial int
@s UCompactPolynomial int
@s FCompactPolynomial int

@c
#include "t_container.h"
#include "fs_tensor.h"
#include "rfs_tensor.h"
#include"tl_static.h"

@<|PowerProvider| class declaration@>;
@<|TensorPolynomial| class declaration@>;
@<|UTensorPolynomial| class declaration@>;
@<|FTensorPolynomial| class declaration@>;
@<|CompactPolynomial| class declaration@>;
@<|UCompactPolynomial| class declaration@>;
@<|FCompactPolynomial| class declaration@>;

@ Just to make the code nicer, we implement a Kronecker power of a
vector encapsulated in the following class. It has |getNext| method
which returns either folded or unfolded row-oriented single column
Kronecker power of the vector according to the type of a dummy
argument. This allows us to use the type dependent code in templates
below.

The implementation of the Kronecker power is that we maintain the last
unfolded power. If unfolded |getNext| is called, we Kronecker multiply
the last power with a vector and return it. If folded |getNext| is
called, we do the same plus we fold it.

|getNext| returns the vector for the first call (first power), the
 second power is returned on the second call, and so on.

@<|PowerProvider| class declaration@>=
class PowerProvider {
	Vector origv;
	URSingleTensor* ut;
	FRSingleTensor* ft;
	int nv;
public:@;
	PowerProvider(const ConstVector& v)
		: origv(v), ut(NULL), ft(NULL), nv(v.length())@+ {}
	~PowerProvider();
	const URSingleTensor& getNext(const URSingleTensor* dummy);
	const FRSingleTensor& getNext(const FRSingleTensor* dummy);
};

@ The tensor polynomial is basically a tensor container which is more
strict on insertions. It maintains number of rows and number of
variables and allows insertions only of those tensors, which yield
these properties. The maximum dimension is maintained by |insert|
method.

So we re-implement |insert| method and implement |evalTrad|
(traditional polynomial evaluation) and horner-like evaluation
|evalHorner|.

In addition, we implement derivatives of the polynomial and its
evaluation. The evaluation of a derivative is different from the
evaluation of the whole polynomial, simply because the evaluation of
the derivatives is a tensor, and the evaluation of the polynomial is a
vector (zero dimensional tensor). See documentation to
|@<|TensorPolynomial::derivative| code@>| and
|@<|TensorPolynomial::evalPartially| code@>| for details.

@s _Stype int
@s _TGStype int

@<|TensorPolynomial| class declaration@>=
template <class _Ttype, class _TGStype, class _Stype>@;
class TensorPolynomial : public TensorContainer<_Ttype> {
	int nr;
	int nv;
	int maxdim;
	typedef TensorContainer<_Ttype> _Tparent;
	typedef typename _Tparent::_ptr _ptr;
public:@;
	TensorPolynomial(int rows, int vars)
		: TensorContainer<_Ttype>(1),
		  nr(rows), nv(vars), maxdim(0) {}
	TensorPolynomial(const TensorPolynomial<_Ttype, _TGStype, _Stype>& tp, int k)
		: TensorContainer<_Ttype>(tp),
		  nr(tp.nr), nv(tp.nv), maxdim(0) {@+ derivative(k);@+}
	TensorPolynomial(int first_row, int num, TensorPolynomial<_Ttype, _TGStype, _Stype>& tp)
		: TensorContainer<_Ttype>(first_row, num, tp),
		  nr(num), nv(tp.nv), maxdim(tp.maxdim)@+ {}
	@<|TensorPolynomial| contract constructor code@>;
	TensorPolynomial(const TensorPolynomial& tp)
		: TensorContainer<_Ttype>(tp), nr(tp.nr), nv(tp.nv), maxdim(tp.maxdim)@+ {}
	int nrows() const
		{@+ return nr;@+}
	int nvars() const
		{@+ return nv;@+}
	@<|TensorPolynomial::evalTrad| code@>;
	@<|TensorPolynomial::evalHorner| code@>;
	@<|TensorPolynomial::insert| code@>;
	@<|TensorPolynomial::derivative| code@>;
	@<|TensorPolynomial::evalPartially| code@>;
};


@ This constructor takes a tensor polynomial 
$$P(x,y)=\sum^m_{k=0}[g_{(xy)^k}]_{\alpha_1\ldots\alpha_k}
\left[\matrix{x\cr y}\right]^{\alpha_1\ldots\alpha_k}$$
and for a given $x$ it makes a polynomial
$$Q(y)=P(x,y).$$

The algorithm for each full symmetry $(xy)^k$ works with subtensors (slices) of
symmetry $x^iy^j$ (with $i+j=k$), and contracts these subtensors with respect to
$x^i$ to obtain a tensor of full symmetry $y^j$. Since the column
$x^i$ is calculated by |PowerProvider| we cycle for $i=1,...,m$. Then
we have to add everything for $i=0$.

The code works as follows: For slicing purposes we need stack sizes
|ss| corresponing to lengths of $x$ and $y$, and then identity |pp|
for unfolding a symmetry of the slice to obtain stack coordinates of
the slice. Then we do the calculations for $i=1,\ldots,m$ and then for
$i=0$.

@<|TensorPolynomial| contract constructor code@>=
TensorPolynomial(const TensorPolynomial<_Ttype, _TGStype, _Stype>& tp, const Vector& xval)
	: TensorContainer<_Ttype>(1),
	  nr(tp.nrows()), nv(tp.nvars() - xval.length()), maxdim(0)
{
	TL_RAISE_IF(nvars() < 0,
				"Length of xval too big in TensorPolynomial contract constructor");
	IntSequence ss(2);@+ ss[0] = xval.length();@+ ss[1] = nvars();
	IntSequence pp(2);@+ pp[0] = 0;@+ pp[1] = 1;

	@<do contraction for all $i>0$@>;
	@<do contraction for $i=0$@>;
}

@ Here we setup the |PowerProvider|, and cycle through
$i=1,\ldots,m$. Within the loop we cycle through $j=0,\ldots,m-i$. If
there is a tensor with symmetry $(xy)^{i+j}$ in the original
polynomial, we make its slice with symmetry $x^iy^j$, and
|contractAndAdd| it to the tensor |ten| in the |this| polynomial with
a symmetry $y^j$.

Note three things: First, the tensor |ten| is either created and put
to |this| container or just got from the container, this is done in
|@<initialize |ten| of dimension |j|@>|. Second, the contribution to
the |ten| tensor must be multiplied by $\left(\matrix{i+j\cr
j}\right)$, since there are exactly that number of slices of
$(xy)^{i+j}$ of the symmetry $x^iy^j$ and all must be added. Third,
the tensor |ten| is fully symmetric and |_TGStype::contractAndAdd|
works with general symmetry, that is why we have to in-place convert
fully syummetric |ten| to a general symmetry tensor.

@<do contraction for all $i>0$@>=
	PowerProvider pwp(xval);
	for (int i = 1; i <= tp.maxdim; i++) {
		const _Stype& xpow = pwp.getNext((const _Stype*)NULL);
		for (int j = 0; j <= tp.maxdim-i; j++) {
			if (tp.check(Symmetry(i+j))) {
				@<initialize |ten| of dimension |j|@>;
				Symmetry sym(i,j);
				IntSequence coor(sym, pp);
				_TGStype slice(*(tp.get(Symmetry(i+j))), ss, coor, TensorDimens(sym, ss));
				slice.mult(Tensor::noverk(i+j, j));
				_TGStype tmp(*ten);
				slice.contractAndAdd(0, tmp, xpow);
			}
		}
	}

@ This is easy. The code is equivalent to code |@<do contraction for
all $i>0$@>| as for $i=0$. The contraction here takes a form of a
simple addition.

@<do contraction for $i=0$@>=
	for (int j = 0; j <= tp.maxdim; j++) {
		if (tp.check(Symmetry(j))) {
			@<initialize |ten| of dimension |j|@>;
			Symmetry sym(0, j);
			IntSequence coor(sym, pp);
			_TGStype slice(*(tp.get(Symmetry(j))), ss, coor, TensorDimens(sym, ss));
			ten->add(1.0, slice);
		}
	}


@ The pointer |ten| is either a new tensor or got from |this| container.
@<initialize |ten| of dimension |j|@>=
	_Ttype* ten;
	if (_Tparent::check(Symmetry(j))) {
		ten = _Tparent::get(Symmetry(j));
	} else {
		ten = new _Ttype(nrows(), nvars(), j);
		ten->zeros();
		insert(ten);
	}


@ Here we cycle up to the maximum dimension, and if a tensor exists in
the container, then we multiply it with the Kronecker power of the
vector supplied by |PowerProvider|.

@<|TensorPolynomial::evalTrad| code@>=
void evalTrad(Vector& out, const ConstVector& v) const
{
	if (_Tparent::check(Symmetry(0)))
		out = _Tparent::get(Symmetry(0))->getData();
	else
		out.zeros();

	PowerProvider pp(v);
	for (int d = 1; d <= maxdim; d++) {
		const _Stype& p = pp.getNext((const _Stype*)NULL);
		Symmetry cs(d);
		if (_Tparent::check(cs)) {
			const _Ttype* t = _Tparent::get(cs);
			t->multaVec(out, p.getData());
		}
	}
}

@ Here we construct by contraction |maxdim-1| tensor first, and then
cycle. The code is clear, the only messy thing is |new| and |delete|.

@<|TensorPolynomial::evalHorner| code@>=
void evalHorner(Vector& out, const ConstVector& v) const
{
	if (_Tparent::check(Symmetry(0)))
		out = _Tparent::get(Symmetry(0))->getData();
	else
		out.zeros();

	if (maxdim == 0)
		return;

	_Ttype* last;
	if (maxdim == 1)
		last = new _Ttype(*(_Tparent::get(Symmetry(1))));
	else 
		last = new _Ttype(*(_Tparent::get(Symmetry(maxdim))), v);
	for (int d = maxdim-1; d >=1; d--) {
		Symmetry cs(d);
		if (_Tparent::check(cs)) {
			const _Ttype* nt = _Tparent::get(cs);
			last->add(1.0, ConstTwoDMatrix(*nt));
		}
		if (d > 1) {
			_Ttype* new_last = new _Ttype(*last, v);
			delete last;
			last = new_last;
		}
	}
	last->multaVec(out, v);
	delete last;
}

@ Before a tensor is inserted, we check for the number of rows, and
number of variables. Then we insert and update the |maxdim|.

@<|TensorPolynomial::insert| code@>=
void insert(_ptr t)
{
	TL_RAISE_IF(t->nrows() != nr,
				"Wrong number of rows in TensorPolynomial::insert");
	TL_RAISE_IF(t->nvar() != nv,
				"Wrong number of variables in TensorPolynomial::insert");
	TensorContainer<_Ttype>::insert(t);
	if (maxdim < t->dimen())
		maxdim = t->dimen();
}

@ The polynomial takes the form
$$\sum_{i=0}^n{1\over i!}\left[g_{y^i}\right]_{\alpha_1\ldots\alpha_i}
\left[y\right]^{\alpha_1}\ldots\left[y\right]^{\alpha_i},$$ where
$\left[g_{y^i}\right]$ are $i$-order derivatives of the polynomial. We
assume that ${1\over i!}\left[g_{y^i}\right]$ are items in the tensor
container.  This method differentiates the polynomial by one order to
yield:
$$\sum_{i=1}^n{1\over i!}\left[i\cdot g_{y^i}\right]_{\alpha_1\ldots\alpha_i}
\left[y\right]^{\alpha_1}\ldots\left[y\right]^{\alpha_{i-1}},$$
where $\left[i\cdot{1\over i!}\cdot g_{y^i}\right]$ are put to the container.

A polynomial can be derivative of some order, and the order cannot be
recognized from the object. That is why we need to input the order.

@<|TensorPolynomial::derivative| code@>=
void derivative(int k)
{
	for (int d = 1; d <= maxdim; d++) {
		if (_Tparent::check(Symmetry(d))) {
			_Ttype* ten = _Tparent::get(Symmetry(d));
			ten->mult((double) max((d-k), 0));
		}
	}
}

@ Now let us suppose that we have an |s| order derivative of a
polynomial whose $i$ order derivatives are $\left[g_{y^i}\right]$, so
we have
$$\sum_{i=s}^n{1\over i!}\left[g_{y^i}\right]_{\alpha_1\ldots\alpha_i}
\prod_{k=1}^{i-s}\left[y\right]^{\alpha_k},$$
where ${1\over i!}\left[g_{y^i}\right]$ are tensors in the container.

This methods performs this evaluation. The result is an |s| dimensional
tensor. Note that when combined with the method |derivative|, they
evaluate a derivative of some order. For example a sequence of calls
|g.derivative(0)|, |g.derivative(1)| and |der=g.evalPartially(2, v)|
calculates $2!$ multiple of the second derivative of |g| at |v|.

@<|TensorPolynomial::evalPartially| code@>=
_Ttype* evalPartially(int s, const ConstVector& v)
{
	TL_RAISE_IF(v.length() != nvars(),
				"Wrong length of vector for TensorPolynomial::evalPartially");

	_Ttype* res = new _Ttype(nrows(), nvars(), s);
	res->zeros();

	if (_Tparent::check(Symmetry(s)))
		res->add(1.0, *(_Tparent::get(Symmetry(s))));

	for (int d = s+1; d <= maxdim; d++) {
		if (_Tparent::check(Symmetry(d))) {
			const _Ttype& ltmp = *(_Tparent::get(Symmetry(d)));
			_Ttype* last = new _Ttype(ltmp);
			for (int j = 0; j < d - s; j++) {
				_Ttype* newlast = new _Ttype(*last, v);
				delete last;
				last = newlast;
			}
			res->add(1.0, *last);
			delete last;
		}
	}

	return res;
}

@ This just gives a name to unfolded tensor polynomial.
@<|UTensorPolynomial| class declaration@>=
class FTensorPolynomial;
class UTensorPolynomial : public TensorPolynomial<UFSTensor, UGSTensor, URSingleTensor> {
public:@;
	UTensorPolynomial(int rows, int vars)
		: TensorPolynomial<UFSTensor, UGSTensor, URSingleTensor>(rows, vars)@+ {}
	UTensorPolynomial(const UTensorPolynomial& up, int k)
		: TensorPolynomial<UFSTensor, UGSTensor, URSingleTensor>(up, k)@+ {}
	UTensorPolynomial(const FTensorPolynomial& fp);
	UTensorPolynomial(const UTensorPolynomial& tp, const Vector& xval)
		: TensorPolynomial<UFSTensor, UGSTensor, URSingleTensor>(tp, xval)@+ {}
	UTensorPolynomial(int first_row, int num, UTensorPolynomial& tp)
		: TensorPolynomial<UFSTensor, UGSTensor, URSingleTensor>(first_row, num, tp)@+ {}
};

@ This just gives a name to folded tensor polynomial.
@<|FTensorPolynomial| class declaration@>=
class FTensorPolynomial : public TensorPolynomial<FFSTensor, FGSTensor, FRSingleTensor> {
public:@;
	FTensorPolynomial(int rows, int vars)
		: TensorPolynomial<FFSTensor, FGSTensor, FRSingleTensor>(rows, vars)@+ {}
	FTensorPolynomial(const FTensorPolynomial& fp, int k)
		: TensorPolynomial<FFSTensor, FGSTensor, FRSingleTensor>(fp, k)@+ {}
	FTensorPolynomial(const UTensorPolynomial& up);
	FTensorPolynomial(const FTensorPolynomial& tp, const Vector& xval)
		: TensorPolynomial<FFSTensor, FGSTensor, FRSingleTensor>(tp, xval)@+ {}
	FTensorPolynomial(int first_row, int num, FTensorPolynomial& tp)
		: TensorPolynomial<FFSTensor, FGSTensor, FRSingleTensor>(first_row, num, tp)@+ {}
};

@ The compact form of |TensorPolynomial| is in fact a full symmetry
tensor, with the number of variables equal to the number of variables
of the polynomial plus 1 for $1$.

@<|CompactPolynomial| class declaration@>=
template <class _Ttype, class _TGStype, class _Stype>@;
class CompactPolynomial : public _Ttype {
public:@;
	@<|CompactPolynomial| constructor code@>;
	@<|CompactPolynomial::eval| method code@>;
};

@ This constructor copies matrices from the given tensor polynomial to
the appropriate location in this matrix. It creates a dummy tensor
|dum| with two variables (one corresponds to $1$, the other to
$x$). The index goes through this dummy tensor and the number of
columns of the folded/unfolded general symmetry tensor corresponding
to the selections of $1$ or $x$ given by the index. Length of $1$ is
one, and length of $x$ is |pol.nvars()|. This nvs information is
stored in |dumnvs|. The symmetry of this general symmetry dummy tensor
|dumgs| is given by a number of ones and x's in the index. We then
copy the matrix, if it exists in the polynomial and increase |offset|
for the following cycle.

@<|CompactPolynomial| constructor code@>=
CompactPolynomial(const TensorPolynomial<_Ttype, _TGStype, _Stype>& pol)
	: _Ttype(pol.nrows(), pol.nvars()+1, pol.getMaxDim())
{
	_Ttype::zeros();

	IntSequence dumnvs(2);
	dumnvs[0] = 1;
	dumnvs[1] = pol.nvars();

	int offset = 0;
	_Ttype dum(0, 2, _Ttype::dimen());
	for (Tensor::index i = dum.begin(); i != dum.end(); ++i) {
		int d = i.getCoor().sum();
		Symmetry symrun(_Ttype::dimen()-d, d);
		_TGStype dumgs(0, TensorDimens(symrun, dumnvs));
		if (pol.check(Symmetry(d))) {
			TwoDMatrix subt(*this, offset, dumgs.ncols());
			subt.add(1.0, *(pol.get(Symmetry(d))));	
		}
		offset += dumgs.ncols();
	}
}


@ We create |x1| to be a concatenation of $1$ and $x$, and then create
|PowerProvider| to make a corresponding power |xpow| of |x1|, and
finally multiply this matrix with the power.

@<|CompactPolynomial::eval| method code@>=
void eval(Vector& out, const ConstVector& v) const
{
	TL_RAISE_IF(v.length()+1 != _Ttype::nvar(),
				"Wrong input vector length in CompactPolynomial::eval");
	TL_RAISE_IF(out.length() != _Ttype::nrows(),
				"Wrong output vector length in CompactPolynomial::eval");

	Vector x1(v.length()+1);
	Vector x1p(x1, 1, v.length());
	x1p = v;
	x1[0] = 1.0;

	if (_Ttype::dimen() == 0)
		out = ConstVector(*this, 0);
	else {
		PowerProvider pp(x1);
		const _Stype& xpow = pp.getNext((const _Stype*)NULL);
		for (int i = 1; i < _Ttype::dimen(); i++)
			xpow = pp.getNext((const _Stype*)NULL);
		multVec(0.0, out, 1.0, xpow);
	}
}

@ Specialization of the |CompactPolynomial| for unfolded tensor.
@<|UCompactPolynomial| class declaration@>=
class UCompactPolynomial : public CompactPolynomial<UFSTensor, UGSTensor, URSingleTensor> {
public:@;
	UCompactPolynomial(const UTensorPolynomial& upol)
		: CompactPolynomial<UFSTensor, UGSTensor, URSingleTensor>(upol)@+ {}
};

@ Specialization of the |CompactPolynomial| for folded tensor.
@<|FCompactPolynomial| class declaration@>=
class FCompactPolynomial : public CompactPolynomial<FFSTensor, FGSTensor, FRSingleTensor> {
public:@;
	FCompactPolynomial(const FTensorPolynomial& fpol)
		: CompactPolynomial<FFSTensor, FGSTensor, FRSingleTensor>(fpol)@+ {}
};



@ End of {\tt t\_polynomial.h} file.