From ce1ef47093902da2413d942a9338196364647bb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Villemot?= Date: Tue, 8 Jan 2019 16:09:25 +0100 Subject: [PATCH] dynare++ / tensor library (TL): move away from CWEB By the way apply Dynare C++ coding style and extensions (.cc/.hh). --- .gitignore | 3 - dynare++/extern/matlab/dynare_simul.cpp | 2 +- dynare++/integ/cc/product.cc | 2 +- dynare++/integ/cc/product.hh | 2 +- dynare++/integ/cc/quadrature.hh | 4 +- dynare++/integ/cc/quasi_mcarlo.hh | 2 +- dynare++/integ/cc/smolyak.cc | 2 +- dynare++/integ/cc/smolyak.hh | 4 +- dynare++/kord/dynamic_model.hh | 4 +- dynare++/kord/faa_di_bruno.cc | 2 +- dynare++/kord/faa_di_bruno.hh | 8 +- dynare++/kord/journal.hh | 2 +- dynare++/kord/korder.hh | 14 +- dynare++/kord/normal_conjugate.hh | 2 +- dynare++/src/dynare3.cpp | 2 +- dynare++/src/dynare3.h | 4 +- dynare++/src/dynare_model.h | 2 +- dynare++/src/nlsolve.h | 2 +- dynare++/tl/cc/Makefile.am | 160 +-- dynare++/tl/cc/dummy.ch | 0 dynare++/tl/cc/equivalence.cc | 435 ++++++ dynare++/tl/cc/equivalence.cweb | 477 ------- dynare++/tl/cc/equivalence.hh | 226 ++++ dynare++/tl/cc/equivalence.hweb | 203 --- dynare++/tl/cc/fine_container.cc | 35 + dynare++/tl/cc/fine_container.cweb | 41 - dynare++/tl/cc/fine_container.hh | 162 +++ dynare++/tl/cc/fine_container.hweb | 164 --- dynare++/tl/cc/fs_tensor.cc | 290 ++++ dynare++/tl/cc/fs_tensor.cweb | 306 ----- dynare++/tl/cc/fs_tensor.hh | 141 ++ dynare++/tl/cc/fs_tensor.hweb | 129 -- dynare++/tl/cc/gs_tensor.cc | 490 +++++++ dynare++/tl/cc/gs_tensor.cweb | 501 ------- dynare++/tl/cc/gs_tensor.hh | 274 ++++ dynare++/tl/cc/gs_tensor.hweb | 222 --- dynare++/tl/cc/int_sequence.cc | 312 +++++ dynare++/tl/cc/int_sequence.cweb | 351 ----- dynare++/tl/cc/int_sequence.hh | 148 ++ dynare++/tl/cc/int_sequence.hweb | 132 -- dynare++/tl/cc/kron_prod.cc | 430 ++++++ dynare++/tl/cc/kron_prod.cweb | 457 ------- dynare++/tl/cc/kron_prod.hh | 348 +++++ dynare++/tl/cc/kron_prod.hweb | 296 ---- dynare++/tl/cc/main.web | 387 ------ dynare++/tl/cc/normal_moments.cc | 103 ++ dynare++/tl/cc/normal_moments.cweb | 115 -- dynare++/tl/cc/normal_moments.hh | 129 ++ dynare++/tl/cc/normal_moments.hweb | 139 -- dynare++/tl/cc/permutation.cc | 163 +++ dynare++/tl/cc/permutation.cweb | 188 --- dynare++/tl/cc/permutation.hh | 177 +++ dynare++/tl/cc/permutation.hweb | 147 -- dynare++/tl/cc/ps_tensor.cc | 399 ++++++ dynare++/tl/cc/ps_tensor.cweb | 422 ------ dynare++/tl/cc/ps_tensor.hh | 384 ++++++ dynare++/tl/cc/ps_tensor.hweb | 351 ----- dynare++/tl/cc/pyramid_prod.cc | 78 ++ dynare++/tl/cc/pyramid_prod.cweb | 86 -- dynare++/tl/cc/pyramid_prod.hh | 74 + dynare++/tl/cc/pyramid_prod.hweb | 80 -- dynare++/tl/cc/pyramid_prod2.cc | 116 ++ dynare++/tl/cc/pyramid_prod2.cweb | 129 -- dynare++/tl/cc/pyramid_prod2.hh | 155 +++ dynare++/tl/cc/pyramid_prod2.hweb | 151 --- dynare++/tl/cc/rfs_tensor.cc | 187 +++ dynare++/tl/cc/rfs_tensor.cweb | 205 --- dynare++/tl/cc/rfs_tensor.hh | 173 +++ dynare++/tl/cc/rfs_tensor.hweb | 148 -- dynare++/tl/cc/sparse_tensor.cc | 248 ++++ dynare++/tl/cc/sparse_tensor.cweb | 274 ---- dynare++/tl/cc/sparse_tensor.hh | 187 +++ dynare++/tl/cc/sparse_tensor.hweb | 154 --- dynare++/tl/cc/stack_container.cc | 662 +++++++++ dynare++/tl/cc/stack_container.cweb | 670 --------- dynare++/tl/cc/stack_container.hh | 744 ++++++++++ dynare++/tl/cc/stack_container.hweb | 771 ----------- dynare++/tl/cc/sthread.cc | 232 ++++ dynare++/tl/cc/sthread.cweb | 224 --- dynare++/tl/cc/sthread.hh | 627 +++++++++ dynare++/tl/cc/sthread.hweb | 625 --------- dynare++/tl/cc/symmetry.cc | 144 ++ dynare++/tl/cc/symmetry.cweb | 153 --- dynare++/tl/cc/symmetry.hh | 227 ++++ dynare++/tl/cc/symmetry.hweb | 208 --- dynare++/tl/cc/t_container.cc | 127 ++ dynare++/tl/cc/t_container.cweb | 138 -- dynare++/tl/cc/t_container.hh | 387 ++++++ dynare++/tl/cc/t_container.hweb | 380 ------ dynare++/tl/cc/t_polynomial.cc | 68 + dynare++/tl/cc/t_polynomial.cweb | 80 -- dynare++/tl/cc/t_polynomial.hh | 536 ++++++++ dynare++/tl/cc/t_polynomial.hweb | 507 ------- dynare++/tl/cc/tensor.cc | 222 +++ dynare++/tl/cc/tensor.cweb | 229 ---- dynare++/tl/cc/tensor.hh | 309 +++++ dynare++/tl/cc/tensor.hweb | 252 ---- dynare++/tl/cc/tl_exception.hh | 74 + dynare++/tl/cc/tl_exception.hweb | 79 -- dynare++/tl/cc/tl_static.cc | 82 ++ dynare++/tl/cc/tl_static.cweb | 89 -- dynare++/tl/cc/tl_static.hh | 55 + dynare++/tl/cc/tl_static.hweb | 62 - dynare++/tl/cc/twod_matrix.cc | 118 ++ dynare++/tl/cc/twod_matrix.cweb | 121 -- dynare++/tl/cc/twod_matrix.hh | 192 +++ dynare++/tl/cc/twod_matrix.hweb | 139 -- dynare++/tl/testing/Makefile.am | 2 +- dynare++/tl/testing/factory.cc | 54 + dynare++/tl/testing/factory.cpp | 49 - dynare++/tl/testing/{factory.h => factory.hh} | 12 +- dynare++/tl/testing/monoms.cc | 542 ++++++++ dynare++/tl/testing/monoms.cpp | 491 ------- dynare++/tl/testing/{monoms.h => monoms.hh} | 8 +- dynare++/tl/testing/tests.cc | 1205 +++++++++++++++++ dynare++/tl/testing/tests.cpp | 1019 -------------- mex/build/libdynare++.am | 64 +- .../k_order_perturbation/k_ord_dynare.hh | 8 +- windows/dynare.nsi | 2 +- 119 files changed, 12604 insertions(+), 12727 deletions(-) delete mode 100644 dynare++/tl/cc/dummy.ch create mode 100644 dynare++/tl/cc/equivalence.cc delete mode 100644 dynare++/tl/cc/equivalence.cweb create mode 100644 dynare++/tl/cc/equivalence.hh delete mode 100644 dynare++/tl/cc/equivalence.hweb create mode 100644 dynare++/tl/cc/fine_container.cc delete mode 100644 dynare++/tl/cc/fine_container.cweb create mode 100644 dynare++/tl/cc/fine_container.hh delete mode 100644 dynare++/tl/cc/fine_container.hweb create mode 100644 dynare++/tl/cc/fs_tensor.cc delete mode 100644 dynare++/tl/cc/fs_tensor.cweb create mode 100644 dynare++/tl/cc/fs_tensor.hh delete mode 100644 dynare++/tl/cc/fs_tensor.hweb create mode 100644 dynare++/tl/cc/gs_tensor.cc delete mode 100644 dynare++/tl/cc/gs_tensor.cweb create mode 100644 dynare++/tl/cc/gs_tensor.hh delete mode 100644 dynare++/tl/cc/gs_tensor.hweb create mode 100644 dynare++/tl/cc/int_sequence.cc delete mode 100644 dynare++/tl/cc/int_sequence.cweb create mode 100644 dynare++/tl/cc/int_sequence.hh delete mode 100644 dynare++/tl/cc/int_sequence.hweb create mode 100644 dynare++/tl/cc/kron_prod.cc delete mode 100644 dynare++/tl/cc/kron_prod.cweb create mode 100644 dynare++/tl/cc/kron_prod.hh delete mode 100644 dynare++/tl/cc/kron_prod.hweb delete mode 100644 dynare++/tl/cc/main.web create mode 100644 dynare++/tl/cc/normal_moments.cc delete mode 100644 dynare++/tl/cc/normal_moments.cweb create mode 100644 dynare++/tl/cc/normal_moments.hh delete mode 100644 dynare++/tl/cc/normal_moments.hweb create mode 100644 dynare++/tl/cc/permutation.cc delete mode 100644 dynare++/tl/cc/permutation.cweb create mode 100644 dynare++/tl/cc/permutation.hh delete mode 100644 dynare++/tl/cc/permutation.hweb create mode 100644 dynare++/tl/cc/ps_tensor.cc delete mode 100644 dynare++/tl/cc/ps_tensor.cweb create mode 100644 dynare++/tl/cc/ps_tensor.hh delete mode 100644 dynare++/tl/cc/ps_tensor.hweb create mode 100644 dynare++/tl/cc/pyramid_prod.cc delete mode 100644 dynare++/tl/cc/pyramid_prod.cweb create mode 100644 dynare++/tl/cc/pyramid_prod.hh delete mode 100644 dynare++/tl/cc/pyramid_prod.hweb create mode 100644 dynare++/tl/cc/pyramid_prod2.cc delete mode 100644 dynare++/tl/cc/pyramid_prod2.cweb create mode 100644 dynare++/tl/cc/pyramid_prod2.hh delete mode 100644 dynare++/tl/cc/pyramid_prod2.hweb create mode 100644 dynare++/tl/cc/rfs_tensor.cc delete mode 100644 dynare++/tl/cc/rfs_tensor.cweb create mode 100644 dynare++/tl/cc/rfs_tensor.hh delete mode 100644 dynare++/tl/cc/rfs_tensor.hweb create mode 100644 dynare++/tl/cc/sparse_tensor.cc delete mode 100644 dynare++/tl/cc/sparse_tensor.cweb create mode 100644 dynare++/tl/cc/sparse_tensor.hh delete mode 100644 dynare++/tl/cc/sparse_tensor.hweb create mode 100644 dynare++/tl/cc/stack_container.cc delete mode 100644 dynare++/tl/cc/stack_container.cweb create mode 100644 dynare++/tl/cc/stack_container.hh delete mode 100644 dynare++/tl/cc/stack_container.hweb create mode 100644 dynare++/tl/cc/sthread.cc delete mode 100644 dynare++/tl/cc/sthread.cweb create mode 100644 dynare++/tl/cc/sthread.hh delete mode 100644 dynare++/tl/cc/sthread.hweb create mode 100644 dynare++/tl/cc/symmetry.cc delete mode 100644 dynare++/tl/cc/symmetry.cweb create mode 100644 dynare++/tl/cc/symmetry.hh delete mode 100644 dynare++/tl/cc/symmetry.hweb create mode 100644 dynare++/tl/cc/t_container.cc delete mode 100644 dynare++/tl/cc/t_container.cweb create mode 100644 dynare++/tl/cc/t_container.hh delete mode 100644 dynare++/tl/cc/t_container.hweb create mode 100644 dynare++/tl/cc/t_polynomial.cc delete mode 100644 dynare++/tl/cc/t_polynomial.cweb create mode 100644 dynare++/tl/cc/t_polynomial.hh delete mode 100644 dynare++/tl/cc/t_polynomial.hweb create mode 100644 dynare++/tl/cc/tensor.cc delete mode 100644 dynare++/tl/cc/tensor.cweb create mode 100644 dynare++/tl/cc/tensor.hh delete mode 100644 dynare++/tl/cc/tensor.hweb create mode 100644 dynare++/tl/cc/tl_exception.hh delete mode 100644 dynare++/tl/cc/tl_exception.hweb create mode 100644 dynare++/tl/cc/tl_static.cc delete mode 100644 dynare++/tl/cc/tl_static.cweb create mode 100644 dynare++/tl/cc/tl_static.hh delete mode 100644 dynare++/tl/cc/tl_static.hweb create mode 100644 dynare++/tl/cc/twod_matrix.cc delete mode 100644 dynare++/tl/cc/twod_matrix.cweb create mode 100644 dynare++/tl/cc/twod_matrix.hh delete mode 100644 dynare++/tl/cc/twod_matrix.hweb create mode 100644 dynare++/tl/testing/factory.cc delete mode 100644 dynare++/tl/testing/factory.cpp rename dynare++/tl/testing/{factory.h => factory.hh} (92%) create mode 100644 dynare++/tl/testing/monoms.cc delete mode 100644 dynare++/tl/testing/monoms.cpp rename dynare++/tl/testing/{monoms.h => monoms.hh} (96%) create mode 100644 dynare++/tl/testing/tests.cc delete mode 100644 dynare++/tl/testing/tests.cpp diff --git a/.gitignore b/.gitignore index 6714f985f..91286a26b 100644 --- a/.gitignore +++ b/.gitignore @@ -137,9 +137,6 @@ mex/build/matlab/run_m2html.m /dynare++/src/dynglob_ll.cc /dynare++/src/dynglob_tab.cc /dynare++/src/dynglob_tab.hh -/dynare++/tl/cc/*.cpp -/dynare++/tl/cc/*.h -/dynare++/tl/cc/main.tex /dynare++/tl/testing/tests /dynare++/tl/testing/tests.exe !/dynare++/extern/R/Makefile diff --git a/dynare++/extern/matlab/dynare_simul.cpp b/dynare++/extern/matlab/dynare_simul.cpp index cf8645ff9..03ea0d397 100644 --- a/dynare++/extern/matlab/dynare_simul.cpp +++ b/dynare++/extern/matlab/dynare_simul.cpp @@ -23,7 +23,7 @@ #include "mex.h" #include "decision_rule.hh" -#include "fs_tensor.h" +#include "fs_tensor.hh" #include "SylvException.h" extern "C" { diff --git a/dynare++/integ/cc/product.cc b/dynare++/integ/cc/product.cc index 08a83a9a1..2e8b22626 100644 --- a/dynare++/integ/cc/product.cc +++ b/dynare++/integ/cc/product.cc @@ -1,7 +1,7 @@ // Copyright 2005, Ondra Kamenik #include "product.hh" -#include "symmetry.h" +#include "symmetry.hh" prodpit::prodpit() : prodq(NULL), level(0), npoints(0), jseq(NULL), diff --git a/dynare++/integ/cc/product.hh b/dynare++/integ/cc/product.hh index 3a3960424..681341261 100644 --- a/dynare++/integ/cc/product.hh +++ b/dynare++/integ/cc/product.hh @@ -16,7 +16,7 @@ #ifndef PRODUCT_H #define PRODUCT_H -#include "int_sequence.h" +#include "int_sequence.hh" #include "vector_function.hh" #include "quadrature.hh" diff --git a/dynare++/integ/cc/quadrature.hh b/dynare++/integ/cc/quadrature.hh index 73ee9d9f4..8ae43f647 100644 --- a/dynare++/integ/cc/quadrature.hh +++ b/dynare++/integ/cc/quadrature.hh @@ -32,8 +32,8 @@ #include #include "vector_function.hh" -#include "int_sequence.h" -#include "sthread.h" +#include "int_sequence.hh" +#include "sthread.hh" /* This pure virtual class represents a concept of one-dimensional (non-nested) quadrature. So, one dimensional quadrature must return diff --git a/dynare++/integ/cc/quasi_mcarlo.hh b/dynare++/integ/cc/quasi_mcarlo.hh index 69e948930..fa9f97ed1 100644 --- a/dynare++/integ/cc/quasi_mcarlo.hh +++ b/dynare++/integ/cc/quasi_mcarlo.hh @@ -25,7 +25,7 @@ #ifndef QUASI_MCARLO_H #define QUASI_MCARLO_H -#include "int_sequence.h" +#include "int_sequence.hh" #include "quadrature.hh" #include "Vector.h" diff --git a/dynare++/integ/cc/smolyak.cc b/dynare++/integ/cc/smolyak.cc index 21fc850b1..a07bbb2dc 100644 --- a/dynare++/integ/cc/smolyak.cc +++ b/dynare++/integ/cc/smolyak.cc @@ -1,7 +1,7 @@ // Copyright 2005, Ondra Kamenik #include "smolyak.hh" -#include "symmetry.h" +#include "symmetry.hh" smolpit::smolpit() : smolq(NULL), isummand(0), jseq(NULL), sig(NULL), p(NULL) diff --git a/dynare++/integ/cc/smolyak.hh b/dynare++/integ/cc/smolyak.hh index ed930814e..d5c6e59a4 100644 --- a/dynare++/integ/cc/smolyak.hh +++ b/dynare++/integ/cc/smolyak.hh @@ -17,8 +17,8 @@ #ifndef SMOLYAK_H #define SMOLYAK_H -#include "int_sequence.h" -#include "tl_static.h" +#include "int_sequence.hh" +#include "tl_static.hh" #include "vector_function.hh" #include "quadrature.hh" diff --git a/dynare++/kord/dynamic_model.hh b/dynare++/kord/dynamic_model.hh index c0555f71f..279985f3f 100644 --- a/dynare++/kord/dynamic_model.hh +++ b/dynare++/kord/dynamic_model.hh @@ -10,8 +10,8 @@ #ifndef DYNAMIC_MODEL_H #define DYNAMIC_MODEL_H -#include "t_container.h" -#include "sparse_tensor.h" +#include "t_container.hh" +#include "sparse_tensor.hh" #include "Vector.h" diff --git a/dynare++/kord/faa_di_bruno.cc b/dynare++/kord/faa_di_bruno.cc index 83a37d8fa..4c6054171 100644 --- a/dynare++/kord/faa_di_bruno.cc +++ b/dynare++/kord/faa_di_bruno.cc @@ -1,7 +1,7 @@ // Copyright 2005, Ondra Kamenik #include "faa_di_bruno.hh" -#include "fine_container.h" +#include "fine_container.hh" #include diff --git a/dynare++/kord/faa_di_bruno.hh b/dynare++/kord/faa_di_bruno.hh index be390b72e..07913e2b8 100644 --- a/dynare++/kord/faa_di_bruno.hh +++ b/dynare++/kord/faa_di_bruno.hh @@ -12,10 +12,10 @@ #define FAA_DI_BRUNO_H #include "journal.hh" -#include "stack_container.h" -#include "t_container.h" -#include "sparse_tensor.h" -#include "gs_tensor.h" +#include "stack_container.hh" +#include "t_container.hh" +#include "sparse_tensor.hh" +#include "gs_tensor.hh" /* Nothing special here. See |@<|FaaDiBruno::calculate| folded sparse code@>| for reason of having |magic_mult|. */ diff --git a/dynare++/kord/journal.hh b/dynare++/kord/journal.hh index a042d6693..5890d7223 100644 --- a/dynare++/kord/journal.hh +++ b/dynare++/kord/journal.hh @@ -5,7 +5,7 @@ #ifndef JOURNAL_H #define JOURNAL_H -#include "int_sequence.h" +#include "int_sequence.hh" #include #include diff --git a/dynare++/kord/korder.hh b/dynare++/kord/korder.hh index 75c3ff27c..20e7a2541 100644 --- a/dynare++/kord/korder.hh +++ b/dynare++/kord/korder.hh @@ -25,13 +25,13 @@ #ifndef KORDER_H #define KORDER_H -#include "int_sequence.h" -#include "fs_tensor.h" -#include "gs_tensor.h" -#include "t_container.h" -#include "stack_container.h" -#include "normal_moments.h" -#include "t_polynomial.h" +#include "int_sequence.hh" +#include "fs_tensor.hh" +#include "gs_tensor.hh" +#include "t_container.hh" +#include "stack_container.hh" +#include "normal_moments.hh" +#include "t_polynomial.hh" #include "faa_di_bruno.hh" #include "journal.hh" diff --git a/dynare++/kord/normal_conjugate.hh b/dynare++/kord/normal_conjugate.hh index 4f361f31d..8faed5e2d 100644 --- a/dynare++/kord/normal_conjugate.hh +++ b/dynare++/kord/normal_conjugate.hh @@ -33,7 +33,7 @@ #ifndef NORMAL_CONJUGATE_H #define NORMAL_CONJUGATE_H -#include "twod_matrix.h" +#include "twod_matrix.hh" /* The class is described by the four parameters: $\mu$, $\kappa$, $\nu$ and $\Lambda$. */ diff --git a/dynare++/src/dynare3.cpp b/dynare++/src/dynare3.cpp index 3ff743d15..a5f0cbdab 100644 --- a/dynare++/src/dynare3.cpp +++ b/dynare++/src/dynare3.cpp @@ -8,7 +8,7 @@ #include "utils/cc/exception.h" #include "parser/cc/parser_exception.h" #include "parser/cc/atom_substitutions.h" -#include "../tl/cc/tl_exception.h" +#include "../tl/cc/tl_exception.hh" #include "../kord/kord_exception.hh" #ifndef DYNVERSION diff --git a/dynare++/src/dynare3.h b/dynare++/src/dynare3.h index 0611df0fc..59d37055e 100644 --- a/dynare++/src/dynare3.h +++ b/dynare++/src/dynare3.h @@ -4,8 +4,8 @@ #ifndef DYNARE3_H #define DYNARE3_H -#include "../tl/cc/t_container.h" -#include "../tl/cc/sparse_tensor.h" +#include "../tl/cc/t_container.hh" +#include "../tl/cc/sparse_tensor.hh" #include "../kord/decision_rule.hh" #include "../kord/dynamic_model.hh" diff --git a/dynare++/src/dynare_model.h b/dynare++/src/dynare_model.h index d5c183446..543a5e393 100644 --- a/dynare++/src/dynare_model.h +++ b/dynare++/src/dynare_model.h @@ -7,7 +7,7 @@ #include "parser/cc/atom_assignings.h" #include "dynare_atoms.h" -#include "twod_matrix.h" +#include "twod_matrix.hh" #include "Vector.h" #include "GeneralMatrix.h" diff --git a/dynare++/src/nlsolve.h b/dynare++/src/nlsolve.h index d286439da..c4b50f56e 100644 --- a/dynare++/src/nlsolve.h +++ b/dynare++/src/nlsolve.h @@ -5,7 +5,7 @@ #ifndef OGU_NLSOLVE_H #define OGU_NLSOLVE_H -#include "twod_matrix.h" +#include "twod_matrix.hh" #include "journal.hh" namespace ogu diff --git a/dynare++/tl/cc/Makefile.am b/dynare++/tl/cc/Makefile.am index 1ad9f87f2..d1a4e2386 100644 --- a/dynare++/tl/cc/Makefile.am +++ b/dynare++/tl/cc/Makefile.am @@ -1,120 +1,48 @@ -CWEBSRC = \ - normal_moments.cweb \ - int_sequence.cweb \ - tensor.cweb \ - ps_tensor.cweb \ - pyramid_prod2.cweb \ - equivalence.cweb \ - fine_container.cweb \ - kron_prod.cweb \ - ps_tensor.hweb \ - t_polynomial.cweb \ - symmetry.cweb \ - stack_container.cweb \ - sthread.hweb \ - twod_matrix.hweb \ - twod_matrix.cweb \ - symmetry.hweb \ - sparse_tensor.cweb \ - fine_container.hweb \ - sthread.cweb \ - int_sequence.hweb \ - tl_exception.hweb \ - pyramid_prod2.hweb \ - t_container.hweb \ - permutation.hweb \ - tensor.hweb \ - gs_tensor.cweb \ - rfs_tensor.hweb \ - pyramid_prod.hweb \ - t_polynomial.hweb \ - pyramid_prod.cweb \ - fs_tensor.cweb \ - sparse_tensor.hweb \ - permutation.cweb \ - equivalence.hweb \ - gs_tensor.hweb \ - normal_moments.hweb \ - tl_static.hweb \ - kron_prod.hweb \ - fs_tensor.hweb \ - stack_container.hweb \ - rfs_tensor.cweb \ - t_container.cweb \ - tl_static.cweb - -GENERATED_FILES = \ - normal_moments.cpp \ - int_sequence.cpp \ - tensor.cpp \ - ps_tensor.cpp \ - pyramid_prod2.cpp \ - equivalence.cpp \ - fine_container.cpp \ - kron_prod.cpp \ - ps_tensor.h \ - t_polynomial.cpp \ - symmetry.cpp \ - stack_container.cpp \ - sthread.h \ - twod_matrix.h \ - twod_matrix.cpp \ - symmetry.h \ - sparse_tensor.cpp \ - fine_container.h \ - sthread.cpp \ - int_sequence.h \ - tl_exception.h \ - pyramid_prod2.h \ - t_container.h \ - permutation.h \ - tensor.h \ - gs_tensor.cpp \ - rfs_tensor.h \ - pyramid_prod.h \ - t_polynomial.h \ - pyramid_prod.cpp \ - fs_tensor.cpp \ - sparse_tensor.h \ - permutation.cpp \ - equivalence.h \ - gs_tensor.h \ - normal_moments.h \ - tl_static.h \ - kron_prod.h \ - fs_tensor.h \ - stack_container.h \ - rfs_tensor.cpp \ - t_container.cpp \ - tl_static.cpp - noinst_LIBRARIES = libtl.a -libtl_a_SOURCES = $(CWEBSRC) $(GENERATED_FILES) +libtl_a_SOURCES = \ + equivalence.cc \ + equivalence.hh \ + fine_container.cc \ + fine_container.hh \ + fs_tensor.cc \ + fs_tensor.hh \ + gs_tensor.cc \ + gs_tensor.hh \ + int_sequence.cc \ + int_sequence.hh \ + kron_prod.cc \ + kron_prod.hh \ + normal_moments.cc \ + normal_moments.hh \ + permutation.cc \ + permutation.hh \ + ps_tensor.cc \ + ps_tensor.hh \ + pyramid_prod.cc \ + pyramid_prod.hh \ + pyramid_prod2.cc \ + pyramid_prod2.hh \ + rfs_tensor.cc \ + rfs_tensor.hh \ + sparse_tensor.cc \ + sparse_tensor.hh \ + stack_container.cc \ + stack_container.hh \ + sthread.cc \ + sthread.hh \ + symmetry.cc \ + symmetry.hh \ + t_container.cc \ + t_container.hh \ + t_polynomial.cc \ + t_polynomial.hh \ + tensor.cc \ + tensor.hh \ + tl_exception.hh \ + tl_static.cc \ + tl_static.hh \ + twod_matrix.cc \ + twod_matrix.hh libtl_a_CPPFLAGS = -I../../sylv/cc $(CPPFLAGS_MATIO) libtl_a_CXXFLAGS = $(AM_CXXFLAGS) $(PTHREAD_CFLAGS) - -BUILT_SOURCES = $(GENERATED_FILES) - -EXTRA_DIST = main.web dummy.ch - -%.cpp: %.cweb dummy.ch - $(CTANGLE) -bhp $< dummy.ch $@ - -%.h: %.hweb dummy.ch - $(CTANGLE) -bhp $< dummy.ch $@ - -if HAVE_CWEAVE -if HAVE_PDFTEX -if HAVE_EPLAIN -pdf-local: tl.pdf - -tl.pdf: main.web $(CWEBSRC) - $(CWEAVE) -bhp main.web - $(PDFTEX) main - mv main.pdf tl.pdf -endif -endif -endif - -CLEANFILES = tl.pdf main.idx main.log main.scn main.tex main.toc diff --git a/dynare++/tl/cc/dummy.ch b/dynare++/tl/cc/dummy.ch deleted file mode 100644 index e69de29bb..000000000 diff --git a/dynare++/tl/cc/equivalence.cc b/dynare++/tl/cc/equivalence.cc new file mode 100644 index 000000000..943c2dd0c --- /dev/null +++ b/dynare++/tl/cc/equivalence.cc @@ -0,0 +1,435 @@ +// Copyright 2004, Ondra Kamenik + +#include "equivalence.hh" +#include "permutation.hh" +#include "tl_exception.hh" + +#include + +int +OrdSequence::operator[](int i) const +{ + TL_RAISE_IF((i < 0 || i >= length()), + "Index out of range in OrdSequence::operator[]"); + return data[i]; +} + +/* Here we implement the ordering. It can be changed, or various + orderings can be used for different problem sizes. We order them + according to the average, and then according to the first item. */ + +bool +OrdSequence::operator<(const OrdSequence &s) const +{ + double ta = average(); + double sa = s.average(); + return (ta < sa || ((ta == sa) && (operator[](0) > s[0]))); +} + +bool +OrdSequence::operator==(const OrdSequence &s) const +{ + if (length() != s.length()) + return false; + + int i = 0; + while (i < length() && operator[](i) == s[i]) + i++; + + return (i == length()); +} + +/* The first |add| adds a given integer to the class, the second + iterates through a given sequence and adds everything found in the + given class. */ + +void +OrdSequence::add(int i) +{ + vector::iterator vit = data.begin(); + while (vit != data.end() && *vit < i) + ++vit; + if (vit != data.end() && *vit == i) + return; + data.insert(vit, i); +} + +void +OrdSequence::add(const OrdSequence &s) +{ + vector::const_iterator vit = s.data.begin(); + while (vit != s.data.end()) + { + add(*vit); + ++vit; + } +} + +/* Answers |true| if a given number is in the class. */ +bool +OrdSequence::has(int i) const +{ + vector::const_iterator vit = data.begin(); + while (vit != data.end()) + { + if (*vit == i) + return true; + ++vit; + } + return false; +} + +/* Return an average of the class. */ +double +OrdSequence::average() const +{ + double res = 0; + for (unsigned int i = 0; i < data.size(); i++) + res += data[i]; + TL_RAISE_IF(data.size() == 0, + "Attempt to take average of empty class in OrdSequence::average"); + return res/data.size(); +} + +/* Debug print. */ +void +OrdSequence::print(const char *prefix) const +{ + printf("%s", prefix); + for (unsigned int i = 0; i < data.size(); i++) + printf("%d ", data[i]); + printf("\n"); +} + +Equivalence::Equivalence(int num) + : n(num) +{ + for (int i = 0; i < num; i++) + { + OrdSequence s; + s.add(i); + classes.push_back(s); + } +} + +Equivalence::Equivalence(int num, const char *dummy) + : n(num) +{ + OrdSequence s; + for (int i = 0; i < num; i++) + s.add(i); + classes.push_back(s); +} + +/* Copy constructors. The second also glues a given couple. */ + +Equivalence::Equivalence(const Equivalence &e) + : n(e.n), + classes(e.classes) +{ +} + +Equivalence::Equivalence(const Equivalence &e, int i1, int i2) + : n(e.n), + classes(e.classes) +{ + seqit s1 = find(i1); + seqit s2 = find(i2); + if (s1 != s2) + { + OrdSequence ns(*s1); + ns.add(*s2); + classes.erase(s1); + classes.erase(s2); + insert(ns); + } +} + +const Equivalence & +Equivalence::operator=(const Equivalence &e) +{ + classes.clear(); + n = e.n; + classes = e.classes; + return *this; +} + +bool +Equivalence::operator==(const Equivalence &e) const +{ + if (!std::operator==(classes, e.classes)) + return false; + + if (n != e.n) + return false; + + return true; +} + +/* Return an iterator pointing to a class having a given integer. */ + +Equivalence::const_seqit +Equivalence::findHaving(int i) const +{ + const_seqit si = classes.begin(); + while (si != classes.end()) + { + if ((*si).has(i)) + return si; + ++si; + } + TL_RAISE_IF(si == classes.end(), + "Couldn't find equivalence class in Equivalence::findHaving"); + return si; +} + +Equivalence::seqit +Equivalence::findHaving(int i) +{ + seqit si = classes.begin(); + while (si != classes.end()) + { + if ((*si).has(i)) + return si; + ++si; + } + TL_RAISE_IF(si == classes.end(), + "Couldn't find equivalence class in Equivalence::findHaving"); + return si; +} + +/* Find $j$-th class for a given $j$. */ + +Equivalence::const_seqit +Equivalence::find(int j) const +{ + const_seqit si = classes.begin(); + int i = 0; + while (si != classes.end() && i < j) + { + ++si; + i++; + } + TL_RAISE_IF(si == classes.end(), + "Couldn't find equivalence class in Equivalence::find"); + return si; +} + +Equivalence::seqit +Equivalence::find(int j) +{ + seqit si = classes.begin(); + int i = 0; + while (si != classes.end() && i < j) + { + ++si; + i++; + } + TL_RAISE_IF(si == classes.end(), + "Couldn't find equivalence class in Equivalence::find"); + return si; +} + +/* Insert a new class yielding the ordering. */ +void +Equivalence::insert(const OrdSequence &s) +{ + seqit si = classes.begin(); + while (si != classes.end() && *si < s) + ++si; + classes.insert(si, s); +} + +/* Trace the equivalence into the integer sequence. The classes are in + some order (described earlier), and items within classes are ordered, + so this implies, that the data can be linearized. This method + ``prints'' them to the sequence. We allow for tracing only a given + number of classes from the beginning. */ + +void +Equivalence::trace(IntSequence &out, int num) const +{ + int i = 0; + int nc = 0; + for (const_seqit it = begin(); it != end() && nc < num; ++it, ++nc) + for (int j = 0; j < (*it).length(); j++, i++) + { + TL_RAISE_IF(i >= out.size(), + "Wrong size of output sequence in Equivalence::trace"); + out[i] = (*it)[j]; + } +} + +void +Equivalence::trace(IntSequence &out, const Permutation &per) const +{ + TL_RAISE_IF(out.size() != n, + "Wrong size of output sequence in Equivalence::trace"); + TL_RAISE_IF(per.size() != numClasses(), + "Wrong permutation for permuted Equivalence::trace"); + int i = 0; + for (int iclass = 0; iclass < numClasses(); iclass++) + { + const_seqit itper = find(per.getMap()[iclass]); + for (int j = 0; j < (*itper).length(); j++, i++) + out[i] = (*itper)[j]; + } +} + +/* Debug print. */ +void +Equivalence::print(const char *prefix) const +{ + int i = 0; + for (const_seqit it = classes.begin(); + it != classes.end(); + ++it, i++) + { + printf("%sclass %d: ", prefix, i); + (*it).print(""); + } +} + +/* Here we construct a set of all equivalences over $n$-element + set. The construction proceeds as follows. We maintain a list of added + equivalences. At each iteration we pop front of the list, try to add + all parents of the popped equivalence. This action adds new + equivalences to the object and also to the added list. We finish the + iterations when the added list is empty. + + In the beginning we start with + $\{\{0\},\{1\},\ldots,\{n-1\}\}$. Adding of parents is an action which + for a given equivalence tries to glue all possible couples and checks + whether a new equivalence is already in the equivalence set. This is + not effective, but we will do the construction only ones. + + In this way we breath-first search a lattice of all equivalences. Note + that the lattice is modular, that is why the result of a construction + is a list with a property that between two equivalences with the same + number of classes there are only equivalences with that number of + classes. Obviously, the list is decreasing in a number of classes + (since it is constructed by gluing attempts). */ + +EquivalenceSet::EquivalenceSet(int num) + : n(num), + equis() +{ + list added; + Equivalence first(n); + equis.push_back(first); + addParents(first, added); + while (!added.empty()) + { + addParents(added.front(), added); + added.pop_front(); + } + if (n > 1) + { + Equivalence last(n, ""); + equis.push_back(last); + } +} + +/* This method is used in |addParents| and returns |true| if the object + already has that equivalence. We trace list of equivalences in reverse + order since equivalences are ordered in the list from the most + primitive (nothing equivalent) to maximal (all is equivalent). Since + we will have much more results of |has| method as |true|, and + |operator==| between equivalences is quick if number of classes + differ, and in time we will compare with equivalences with less + classes, then it is more efficient to trace the equivalences from less + classes to more classes. hence the reverse order. */ + +bool +EquivalenceSet::has(const Equivalence &e) const +{ + list::const_reverse_iterator rit = equis.rbegin(); + while (rit != equis.rend() && *rit != e) + ++rit; + if (rit != equis.rend()) + return true; + return false; +} + +/* Responsibility of this methods is to try to glue all possible + couples within a given equivalence and add those which are not in the + list yet. These are added also to the |added| list. + + If number of classes is 2 or 1, we exit, because there is nothing to + be added. */ + +void +EquivalenceSet::addParents(const Equivalence &e, + list &added) +{ + if (e.numClasses() == 2 || e.numClasses() == 1) + return; + + for (int i1 = 0; i1 < e.numClasses(); i1++) + for (int i2 = i1+1; i2 < e.numClasses(); i2++) + { + Equivalence ns(e, i1, i2); + if (!has(ns)) + { + added.push_back(ns); + equis.push_back(ns); + } + } +} + +/* Debug print. */ +void +EquivalenceSet::print(const char *prefix) const +{ + char tmp[100]; + strcpy(tmp, prefix); + strcat(tmp, " "); + int i = 0; + for (list::const_iterator it = equis.begin(); + it != equis.end(); + ++it, i++) + { + printf("%sequivalence %d:(classes %d)\n", prefix, i, (*it).numClasses()); + (*it).print(tmp); + } +} + +/* Construct the bundle. |nmax| is a maximum size of underlying set. */ +EquivalenceBundle::EquivalenceBundle(int nmax) +{ + nmax = max(nmax, 1); + generateUpTo(nmax); +} + +/* Destruct bundle. Just free all pointers. */ +EquivalenceBundle::~EquivalenceBundle() +{ + for (unsigned int i = 0; i < bundle.size(); i++) + delete bundle[i]; +} + +/* Remember, that the first item is |EquivalenceSet(1)|. */ +const EquivalenceSet & +EquivalenceBundle::get(int n) const +{ + if (n > (int) (bundle.size()) || n < 1) + { + TL_RAISE("Equivalence set not found in EquivalenceBundle::get"); + return *(bundle[0]); + } + else + { + return *(bundle[n-1]); + } +} + +/* Get |curmax| which is a maximum size in the bundle, and generate for + all sizes from |curmax+1| up to |nmax|. */ + +void +EquivalenceBundle::generateUpTo(int nmax) +{ + int curmax = bundle.size(); + for (int i = curmax+1; i <= nmax; i++) + bundle.push_back(new EquivalenceSet(i)); +} diff --git a/dynare++/tl/cc/equivalence.cweb b/dynare++/tl/cc/equivalence.cweb deleted file mode 100644 index 4583df15a..000000000 --- a/dynare++/tl/cc/equivalence.cweb +++ /dev/null @@ -1,477 +0,0 @@ -@q $Id: equivalence.cweb 148 2005-04-19 15:12:26Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@ Start of {\tt equivalence.cpp} file. - -@c -#include "equivalence.h" -#include "permutation.h" -#include "tl_exception.h" - -#include - -@<|OrdSequence| method codes@>; -@<|Equivalence| method codes@>; -@<|EquivalenceSet| method codes@>; -@<|EquivalenceBundle| method codes@>; - -@ -@<|OrdSequence| method codes@>= -@<|OrdSequence::operator[]| code@>; -@<|OrdSequence::operator<| code@>; -@<|OrdSequence::operator==| code@>; -@<|OrdSequence::add| codes@>; -@<|OrdSequence::has| code@>; -@<|OrdSequence::average()| code@>; -@<|OrdSequence::print| code@>; - -@ -@<|Equivalence| method codes@>= -@<|Equivalence| constructors@>; -@<|Equivalence| copy constructors@>; -@<|Equivalence::findHaving| codes@>; -@<|Equivalence::find| codes@>; -@<|Equivalence::insert| code@>; -@<|Equivalence::operator=| code@>; -@<|Equivalence::operator==| code@>; -@<|Equivalence::trace| code@>; -@<|Equivalence::trace| permuted code@>; -@<|Equivalence::print| code@>; - -@ -@<|EquivalenceSet| method codes@>= -@<|EquivalenceSet| constructor code@>; -@<|EquivalenceSet::has| code@>; -@<|EquivalenceSet::addParents| code@>; -@<|EquivalenceSet::print| code@>; - -@ -@<|EquivalenceBundle| method codes@>= -@<|EquivalenceBundle| constructor code@>; -@<|EquivalenceBundle| destructor code@>; -@<|EquivalenceBundle::get| code@>; -@<|EquivalenceBundle::generateUpTo| code@>; - - -@ -@<|OrdSequence::operator[]| code@>= -int OrdSequence::operator[](int i) const -{ - TL_RAISE_IF((i<0 || i>=length()), - "Index out of range in OrdSequence::operator[]"); - return data[i]; -} - -@ Here we implement the ordering. It can be changed, or various -orderings can be used for different problem sizes. We order them -according to the average, and then according to the first item. - -@<|OrdSequence::operator<| code@>= -bool OrdSequence::operator<(const OrdSequence& s) const -{ - double ta = average(); - double sa = s.average(); - return (ta < sa || ((ta == sa) && (operator[](0) > s[0]))); -} - -@ -@<|OrdSequence::operator==| code@>= -bool OrdSequence::operator==(const OrdSequence& s) const -{ - if (length() != s.length()) - return false; - - int i = 0; - while (i < length() && operator[](i) == s[i]) - i++; - - return (i == length()); -} - - -@ The first |add| adds a given integer to the class, the second -iterates through a given sequence and adds everything found in the -given class. - -@<|OrdSequence::add| codes@>= -void OrdSequence::add(int i) -{ - vector::iterator vit = data.begin(); - while (vit != data.end() && *vit < i) - ++vit; - if (vit != data.end() && *vit == i) - return; - data.insert(vit, i); -} -@# -void OrdSequence::add(const OrdSequence& s) -{ - vector::const_iterator vit = s.data.begin(); - while (vit != s.data.end()) { - add(*vit); - ++vit; - } -} - -@ Answers |true| if a given number is in the class. -@<|OrdSequence::has| code@>= -bool OrdSequence::has(int i) const -{ - vector::const_iterator vit = data.begin(); - while (vit != data.end()) { - if (*vit == i) - return true; - ++vit; - } - return false; -} - -@ Return an average of the class. -@<|OrdSequence::average()| code@>= -double OrdSequence::average() const -{ - double res = 0; - for (unsigned int i = 0; i < data.size(); i++) - res += data[i]; - TL_RAISE_IF(data.size() == 0, - "Attempt to take average of empty class in OrdSequence::average"); - return res/data.size(); -} - -@ Debug print. -@<|OrdSequence::print| code@>= -void OrdSequence::print(const char* prefix) const -{ - printf("%s",prefix); - for (unsigned int i = 0; i < data.size(); i++) - printf("%d ",data[i]); - printf("\n"); -} - -@ -@<|Equivalence| constructors@>= -Equivalence::Equivalence(int num) - : n(num) -{ - for (int i = 0; i < num; i++) { - OrdSequence s; - s.add(i); - classes.push_back(s); - } -} -@# -Equivalence::Equivalence(int num, const char* dummy) - : n(num) -{ - OrdSequence s; - for (int i = 0; i < num; i++) - s.add(i); - classes.push_back(s); -} - -@ Copy constructors. The second also glues a given couple. -@<|Equivalence| copy constructors@>= -Equivalence::Equivalence(const Equivalence& e) - : n(e.n), - classes(e.classes) -{ -} -@# -Equivalence::Equivalence(const Equivalence& e, int i1, int i2) - : n(e.n), - classes(e.classes) -{ - seqit s1 = find(i1); - seqit s2 = find(i2); - if (s1 != s2) { - OrdSequence ns(*s1); - ns.add(*s2); - classes.erase(s1); - classes.erase(s2); - insert(ns); - } -} - -@ -@<|Equivalence::operator=| code@>= -const Equivalence& Equivalence::operator=(const Equivalence& e) -{ - classes.clear(); - n = e.n; - classes = e.classes; - return *this; -} - -@ -@<|Equivalence::operator==| code@>= -bool Equivalence::operator==(const Equivalence& e) const -{ - if (! std::operator==(classes, e.classes)) - return false; - - if (n != e.n) - return false; - - return true; -} - - -@ Return an iterator pointing to a class having a given integer. -@<|Equivalence::findHaving| codes@>= -Equivalence::const_seqit Equivalence::findHaving(int i) const -{ - const_seqit si = classes.begin(); - while (si != classes.end()) { - if ((*si).has(i)) - return si; - ++si; - } - TL_RAISE_IF(si == classes.end(), - "Couldn't find equivalence class in Equivalence::findHaving"); - return si; -} -@# -Equivalence::seqit Equivalence::findHaving(int i) -{ - seqit si = classes.begin(); - while (si != classes.end()) { - if ((*si).has(i)) - return si; - ++si; - } - TL_RAISE_IF(si == classes.end(), - "Couldn't find equivalence class in Equivalence::findHaving"); - return si; -} - - -@ Find $j$-th class for a given $j$. -@<|Equivalence::find| codes@>= -Equivalence::const_seqit Equivalence::find(int j) const -{ - const_seqit si = classes.begin(); - int i = 0; - while (si != classes.end() && i < j) { - ++si; - i++; - } - TL_RAISE_IF(si == classes.end(), - "Couldn't find equivalence class in Equivalence::find"); - return si; -} -@# -Equivalence::seqit Equivalence::find(int j) -{ - seqit si = classes.begin(); - int i = 0; - while (si != classes.end() && i < j) { - ++si; - i++; - } - TL_RAISE_IF(si == classes.end(), - "Couldn't find equivalence class in Equivalence::find"); - return si; -} - - -@ Insert a new class yielding the ordering. -@<|Equivalence::insert| code@>= -void Equivalence::insert(const OrdSequence& s) -{ - seqit si = classes.begin(); - while (si != classes.end() && *si < s) - ++si; - classes.insert(si, s); -} - -@ Trace the equivalence into the integer sequence. The classes are in -some order (described earlier), and items within classes are ordered, -so this implies, that the data can be linearized. This method -``prints'' them to the sequence. We allow for tracing only a given -number of classes from the beginning. - -@<|Equivalence::trace| code@>= -void Equivalence::trace(IntSequence& out, int num) const -{ - int i = 0; - int nc = 0; - for (const_seqit it = begin(); it != end() && nc < num; ++it, ++nc) - for (int j = 0; j < (*it).length(); j++, i++) { - TL_RAISE_IF(i >= out.size(), - "Wrong size of output sequence in Equivalence::trace"); - out[i] = (*it)[j]; - } -} - -@ -@<|Equivalence::trace| permuted code@>= -void Equivalence::trace(IntSequence& out, const Permutation& per) const -{ - TL_RAISE_IF(out.size() != n, - "Wrong size of output sequence in Equivalence::trace"); - TL_RAISE_IF(per.size() != numClasses(), - "Wrong permutation for permuted Equivalence::trace"); - int i = 0; - for (int iclass = 0; iclass < numClasses(); iclass++) { - const_seqit itper = find(per.getMap()[iclass]); - for (int j = 0; j < (*itper).length(); j++, i++) - out[i] = (*itper)[j]; - } -} - - -@ Debug print. -@<|Equivalence::print| code@>= -void Equivalence::print(const char* prefix) const -{ - int i = 0; - for (const_seqit it = classes.begin(); - it != classes.end(); - ++it, i++) { - printf("%sclass %d: ",prefix,i); - (*it).print(""); - } -} - -@ Here we construct a set of all equivalences over $n$-element -set. The construction proceeds as follows. We maintain a list of added -equivalences. At each iteration we pop front of the list, try to add -all parents of the popped equivalence. This action adds new -equivalences to the object and also to the added list. We finish the -iterations when the added list is empty. - -In the beginning we start with -$\{\{0\},\{1\},\ldots,\{n-1\}\}$. Adding of parents is an action which -for a given equivalence tries to glue all possible couples and checks -whether a new equivalence is already in the equivalence set. This is -not effective, but we will do the construction only ones. - -In this way we breath-first search a lattice of all equivalences. Note -that the lattice is modular, that is why the result of a construction -is a list with a property that between two equivalences with the same -number of classes there are only equivalences with that number of -classes. Obviously, the list is decreasing in a number of classes -(since it is constructed by gluing attempts). - - -@<|EquivalenceSet| constructor code@>= -EquivalenceSet::EquivalenceSet(int num) - : n(num), - equis() -{ - list added; - Equivalence first(n); - equis.push_back(first); - addParents(first, added); - while (! added.empty()) { - addParents(added.front(), added); - added.pop_front(); - } - if (n > 1) { - Equivalence last(n, ""); - equis.push_back(last); - } -} - -@ This method is used in |addParents| and returns |true| if the object -already has that equivalence. We trace list of equivalences in reverse -order since equivalences are ordered in the list from the most -primitive (nothing equivalent) to maximal (all is equivalent). Since -we will have much more results of |has| method as |true|, and -|operator==| between equivalences is quick if number of classes -differ, and in time we will compare with equivalences with less -classes, then it is more efficient to trace the equivalences from less -classes to more classes. hence the reverse order. - -@<|EquivalenceSet::has| code@>= -bool EquivalenceSet::has(const Equivalence& e) const -{ - list::const_reverse_iterator rit = equis.rbegin(); - while (rit != equis.rend() && *rit != e) - ++rit; - if (rit != equis.rend()) - return true; - return false; -} - -@ Responsibility of this methods is to try to glue all possible -couples within a given equivalence and add those which are not in the -list yet. These are added also to the |added| list. - -If number of classes is 2 or 1, we exit, because there is nothing to -be added. - -@<|EquivalenceSet::addParents| code@>= -void EquivalenceSet::addParents(const Equivalence& e, - list& added) -{ - if (e.numClasses() == 2 || e.numClasses() == 1) - return; - - for (int i1 = 0; i1 < e.numClasses(); i1++) - for (int i2 = i1+1; i2 < e.numClasses(); i2++) { - Equivalence ns(e, i1, i2); - if (! has(ns)) { - added.push_back(ns); - equis.push_back(ns); - } - } -} - -@ Debug print. -@<|EquivalenceSet::print| code@>= -void EquivalenceSet::print(const char* prefix) const -{ - char tmp[100]; - strcpy(tmp, prefix); - strcat(tmp, " "); - int i = 0; - for (list::const_iterator it = equis.begin(); - it != equis.end(); - ++it, i++) { - printf("%sequivalence %d:(classes %d)\n",prefix,i,(*it).numClasses()); - (*it).print(tmp); - } -} - -@ Construct the bundle. |nmax| is a maximum size of underlying set. -@<|EquivalenceBundle| constructor code@>= -EquivalenceBundle::EquivalenceBundle(int nmax) -{ - nmax = max(nmax, 1); - generateUpTo(nmax); -} - -@ Destruct bundle. Just free all pointers. -@<|EquivalenceBundle| destructor code@>= -EquivalenceBundle::~EquivalenceBundle() -{ - for (unsigned int i = 0; i < bundle.size(); i++) - delete bundle[i]; -} - -@ Remember, that the first item is |EquivalenceSet(1)|. -@<|EquivalenceBundle::get| code@>= -const EquivalenceSet& EquivalenceBundle::get(int n) const -{ - if (n > (int)(bundle.size()) || n < 1) { - TL_RAISE("Equivalence set not found in EquivalenceBundle::get"); - return *(bundle[0]); - } else { - return *(bundle[n-1]); - } -} - -@ Get |curmax| which is a maximum size in the bundle, and generate for -all sizes from |curmax+1| up to |nmax|. - -@<|EquivalenceBundle::generateUpTo| code@>= -void EquivalenceBundle::generateUpTo(int nmax) -{ - int curmax = bundle.size(); - for (int i = curmax+1; i <= nmax; i++) - bundle.push_back(new EquivalenceSet(i)); -} - - -@ End of {\tt equivalence.cpp} file. \ No newline at end of file diff --git a/dynare++/tl/cc/equivalence.hh b/dynare++/tl/cc/equivalence.hh new file mode 100644 index 000000000..8b9ee754c --- /dev/null +++ b/dynare++/tl/cc/equivalence.hh @@ -0,0 +1,226 @@ +// Copyright 2004, Ondra Kamenik + +// Equivalences. + +/* Here we define an equivalence of a set of integers $\{0, 1, \ldots, + k-1\}$. The purpose is clear, in the tensor library we often iterate + through all equivalences and sum matrices. We need an abstraction for + an equivalence class, equivalence and a set of all equivalences. + + The equivalence class (which is basically a set of integers) is here + implemented as ordered integer sequence. The ordered sequence is not + implemented via |IntSequence|, but via |vector| since we need + insertions. The equivalence is implemented as an ordered list of + equivalence classes, and equivalence set is a list of equivalences. + + The ordering of the equivalence classes within an equivalence is very + important. For instance, if we iterate through equivalences for $k=5$ + and pickup some equivalence class, say $\{\{0,4\},\{1,2\},\{3\}\}$, we + then evaluate something like: + $$\left[B_{y^2u^3}\right]_{\alpha_1\alpha_2\beta_1\beta_2\beta_3}= + \cdots+\left[g_{y^3}\right]_{\gamma_1\gamma_2\gamma_3} + \left[g_{yu}\right]^{\gamma_1}_{\alpha_1\beta_3} + \left[g_{yu}\right]^{\gamma_2}_{\alpha_2\beta_1} + \left[g_u\right]^{\gamma_3}_{\beta_2}+\cdots + $$ + If the tensors are unfolded, we can evaluate this expression as + $$g_{y^3}\cdot\left(g_{yu}\otimes g_{yu}\otimes g_{u}\right)\cdot P,$$ + where $P$ is a suitable permutation of columns of the expressions, + which permutes them so that the index + $(\alpha_1,\beta_3,\alpha_2,\beta_1,\beta_2)$ would go to + $(\alpha_1,\alpha_2,\beta_1,\beta_2,\beta_3)$. + The permutation $P$ can be very ineffective (copying great amount of + small chunks of data) if the equivalence class ordering is chosen + badly. However, we do not provide any heuristic minimizing a total + time spent in all permutations. We choose an ordering which orders the + classes according to their averages, and according to the smallest + equivalence class element if the averages are the same. */ + +#ifndef EQUIVALENCE_H +#define EQUIVALENCE_H + +#include "int_sequence.hh" + +#include +#include + +using namespace std; + +/* Here is the abstraction for an equivalence class. We implement it as + |vector|. We have a constructor for empty class, copy + constructor. What is important here is the ordering operator + |operator<| and methods for addition of an integer, and addition of + another sequence. Also we provide method |has| which returns true if a + given integer is contained. */ + +class OrdSequence +{ + vector data; +public: + OrdSequence() : data() + { + } + OrdSequence(const OrdSequence &s) : data(s.data) + { + } + const OrdSequence & + operator=(const OrdSequence &s) + { + data = s.data; return *this; + } + bool operator==(const OrdSequence &s) const; + int operator[](int i) const; + bool operator<(const OrdSequence &s) const; + const vector & + getData() const + { + return data; + } + int + length() const + { + return data.size(); + } + void add(int i); + void add(const OrdSequence &s); + bool has(int i) const; + void print(const char *prefix) const; +private: + double average() const; +}; + +/* Here is the abstraction for the equivalence. It is a list of + equivalence classes. Also we remember |n|, which is a size of + underlying set $\{0, 1, \ldots, n-1\}$. + + Method |trace| ``prints'' the equivalence into the integer sequence. */ + +class Permutation; +class Equivalence +{ +private: + int n; + list classes; +public: + typedef list::const_iterator const_seqit; + typedef list::iterator seqit; + + /* The first constructor constructs $\{\{0\},\{1\},\ldots,\{n-1\}\}$. + + The second constructor constructs $\{\{0,1,\ldots,n-1\}\}$. + + The third is the copy constructor. And the fourth is the copy + constructor plus gluing |i1| and |i2| in one class. */ + Equivalence(int num); + Equivalence(int num, const char *dummy); + Equivalence(const Equivalence &e); + Equivalence(const Equivalence &e, int i1, int i2); + + const Equivalence &operator=(const Equivalence &e); + bool operator==(const Equivalence &e) const; + bool + operator!=(const Equivalence &e) const + { + return !operator==(e); + } + int + getN() const + { + return n; + } + int + numClasses() const + { + return classes.size(); + } + void trace(IntSequence &out, int n) const; + void + trace(IntSequence &out) const + { + trace(out, numClasses()); + } + void trace(IntSequence &out, const Permutation &per) const; + void print(const char *prefix) const; + seqit + begin() + { + return classes.begin(); + } + const_seqit + begin() const + { + return classes.begin(); + } + seqit + end() + { + return classes.end(); + } + const_seqit + end() const + { + return classes.end(); + } + const_seqit find(int i) const; + seqit find(int i); +protected: + /* Here we have find methods. We can find an equivalence class having a + given number or we can find an equivalence class of a given index within + the ordering. + + We have also an |insert| method which inserts a given class + according to the class ordering. */ + const_seqit findHaving(int i) const; + seqit findHaving(int i); + void insert(const OrdSequence &s); + +}; + +/* The |EquivalenceSet| is a list of equivalences. The unique + constructor constructs a set of all equivalences over $n$-element + set. The equivalences are sorted in the list so that equivalences with + fewer number of classes are in the end. + + The two methods |has| and |addParents| are useful in the constructor. */ + +class EquivalenceSet +{ + int n; + list equis; +public: + typedef list::const_iterator const_iterator; + EquivalenceSet(int num); + void print(const char *prefix) const; + const_iterator + begin() const + { + return equis.begin(); + } + const_iterator + end() const + { + return equis.end(); + } +private: + bool has(const Equivalence &e) const; + void addParents(const Equivalence &e, list &added); +}; + +/* The equivalence bundle class only encapsulates |EquivalenceSet|s + from 1 up to a given number. It is able to retrieve the equivalence set + over $n$-element set for a given $n$, and also it can generate some more + sets on request. + + It is fully responsible for storage needed for |EquivalenceSet|s. */ + +class EquivalenceBundle +{ + vector bundle; +public: + EquivalenceBundle(int nmax); + ~EquivalenceBundle(); + const EquivalenceSet&get(int n) const; + void generateUpTo(int nmax); +}; + +#endif diff --git a/dynare++/tl/cc/equivalence.hweb b/dynare++/tl/cc/equivalence.hweb deleted file mode 100644 index f7f2b5c4d..000000000 --- a/dynare++/tl/cc/equivalence.hweb +++ /dev/null @@ -1,203 +0,0 @@ -@q $Id: equivalence.hweb 148 2005-04-19 15:12:26Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@*2 Equivalences. Start of {\tt equivalence.h} file - -Here we define an equivalence of a set of integers $\{0, 1, \ldots, -k-1\}$. The purpose is clear, in the tensor library we often iterate -through all equivalences and sum matrices. We need an abstraction for -an equivalence class, equivalence and a set of all equivalences. - -The equivalence class (which is basically a set of integers) is here -implemented as ordered integer sequence. The ordered sequence is not -implemented via |IntSequence|, but via |vector| since we need -insertions. The equivalence is implemented as an ordered list of -equivalence classes, and equivalence set is a list of equivalences. - -The ordering of the equivalence classes within an equivalence is very -important. For instance, if we iterate through equivalences for $k=5$ -and pickup some equivalence class, say $\{\{0,4\},\{1,2\},\{3\}\}$, we -then evaluate something like: -$$\left[B_{y^2u^3}\right]_{\alpha_1\alpha_2\beta_1\beta_2\beta_3}= -\cdots+\left[g_{y^3}\right]_{\gamma_1\gamma_2\gamma_3} -\left[g_{yu}\right]^{\gamma_1}_{\alpha_1\beta_3} -\left[g_{yu}\right]^{\gamma_2}_{\alpha_2\beta_1} -\left[g_u\right]^{\gamma_3}_{\beta_2}+\cdots -$$ -If the tensors are unfolded, we can evaluate this expression as -$$g_{y^3}\cdot\left(g_{yu}\otimes g_{yu}\otimes g_{u}\right)\cdot P,$$ -where $P$ is a suitable permutation of columns of the expressions, -which permutes them so that the index -$(\alpha_1,\beta_3,\alpha_2,\beta_1,\beta_2)$ would go to -$(\alpha_1,\alpha_2,\beta_1,\beta_2,\beta_3)$. -The permutation $P$ can be very ineffective (copying great amount of -small chunks of data) if the equivalence class ordering is chosen -badly. However, we do not provide any heuristic minimizing a total -time spent in all permutations. We choose an ordering which orders the -classes according to their averages, and according to the smallest -equivalence class element if the averages are the same. - - - -@s OrdSequence int -@s Equivalence int -@s EquivalenceSet int - -@c -#ifndef EQUIVALENCE_H -#define EQUIVALENCE_H - -#include "int_sequence.h" - -#include -#include - -using namespace std; - -@<|OrdSequence| class declaration@>; -@<|Equivalence| class declaration@>; -@<|EquivalenceSet| class declaration@>; -@<|EquivalenceBundle| class declaration@>; - -#endif - - -@ Here is the abstraction for an equivalence class. We implement it as -|vector|. We have a constructor for empty class, copy -constructor. What is important here is the ordering operator -|operator<| and methods for addition of an integer, and addition of -another sequence. Also we provide method |has| which returns true if a -given integer is contained. - -@<|OrdSequence| class declaration@>= -class OrdSequence { - vector data; -public:@/ - OrdSequence() : data()@+ {} - OrdSequence(const OrdSequence& s) : data(s.data)@+ {} - const OrdSequence& operator=(const OrdSequence& s) - {@+ data = s.data;@+ return *this;@+} - bool operator==(const OrdSequence& s) const; - int operator[](int i) const; - bool operator<(const OrdSequence& s) const; - const vector& getData() const - {@+ return data;@+} - int length() const {@+ return data.size();@+} - void add(int i); - void add(const OrdSequence& s); - bool has(int i) const; - void print(const char* prefix) const; -private:@/ - double average() const; -}; - - -@ Here is the abstraction for the equivalence. It is a list of -equivalence classes. Also we remember |n|, which is a size of -underlying set $\{0, 1, \ldots, n-1\}$. - -Method |trace| ``prints'' the equivalence into the integer sequence. - -@<|Equivalence| class declaration@>= -class Permutation; -class Equivalence { -private: - int n; - list classes; -public:@; - typedef list::const_iterator const_seqit; - typedef list::iterator seqit; - - @<|Equivalence| constructors@>; - const Equivalence& operator=(const Equivalence& e); - bool operator==(const Equivalence& e) const; - bool operator!=(const Equivalence& e) const - {@+ return ! operator==(e);@+} - int getN() const {@+ return n;@+} - int numClasses() const {@+ return classes.size();@+} - void trace(IntSequence& out, int n) const; - void trace(IntSequence& out) const - {@+ trace(out, numClasses()); @+} - void trace(IntSequence& out, const Permutation& per) const; - void print(const char* prefix) const; - @<|Equivalence| begin and end methods@>; - const_seqit find(int i) const; - seqit find(int i); -protected:@; - @<|Equivalence| protected methods@>; -}; - -@ The |EquivalenceSet| is a list of equivalences. The unique -constructor constructs a set of all equivalences over $n$-element -set. The equivalences are sorted in the list so that equivalences with -fewer number of classes are in the end. - -The two methods |has| and |addParents| are useful in the constructor. - -@<|EquivalenceSet| class declaration@>= -class EquivalenceSet { - int n; - list equis; -public:@; - typedef list::const_iterator const_iterator; - EquivalenceSet(int num); - void print(const char* prefix) const; - const_iterator begin() const - {@+ return equis.begin();@+} - const_iterator end() const - {@+ return equis.end();@+} -private:@; - bool has(const Equivalence& e) const; - void addParents(const Equivalence& e, list& added); -}; - -@ The equivalence bundle class only encapsulates |EquivalenceSet|s -from 1 up to a given number. It is able to retrieve the equivalence set -over $n$-element set for a given $n$, and also it can generate some more -sets on request. - -It is fully responsible for storage needed for |EquivalenceSet|s. - -@<|EquivalenceBundle| class declaration@>= -class EquivalenceBundle { - vector bundle; -public:@; - EquivalenceBundle(int nmax); - ~EquivalenceBundle(); - const EquivalenceSet& get(int n) const; - void generateUpTo(int nmax); -}; - -@ The first constructor constructs $\{\{0\},\{1\},\ldots,\{n-1\}\}$. - -The second constructor constructs $\{\{0,1,\ldots,n-1\}\}$. - -The third is the copy constructor. And the fourth is the copy -constructor plus gluing |i1| and |i2| in one class. - -@<|Equivalence| constructors@>= - Equivalence(int num); - Equivalence(int num, const char* dummy); - Equivalence(const Equivalence& e); - Equivalence(const Equivalence& e, int i1, int i2); - -@ -@<|Equivalence| begin and end methods@>= - seqit begin() {@+ return classes.begin();@+} - const_seqit begin() const {@+ return classes.begin();@+} - seqit end() {@+ return classes.end();@+} - const_seqit end() const {@+ return classes.end();@+} - -@ Here we have find methods. We can find an equivalence class having a -given number or we can find an equivalence class of a given index within -the ordering. - -We have also an |insert| method which inserts a given class -according to the class ordering. - -@<|Equivalence| protected methods@>= - const_seqit findHaving(int i) const; - seqit findHaving(int i); - void insert(const OrdSequence& s); - -@ End of {\tt equivalence.h} file. \ No newline at end of file diff --git a/dynare++/tl/cc/fine_container.cc b/dynare++/tl/cc/fine_container.cc new file mode 100644 index 000000000..e886510c6 --- /dev/null +++ b/dynare++/tl/cc/fine_container.cc @@ -0,0 +1,35 @@ +// Copyright 2005, Ondra Kamenik + +#include "fine_container.hh" + +#include + +/* Here we construct the vector of new sizes of containers (before + |nc|) and copy all remaining sizes behind |nc|. */ + +SizeRefinement::SizeRefinement(const IntSequence &s, int nc, int max) +{ + new_nc = 0; + for (int i = 0; i < nc; i++) + { + int nr = s[i]/max; + if (s[i] % max != 0) + nr++; + int ss = (nr > 0) ? (int) round(((double) s[i])/nr) : 0; + for (int j = 0; j < nr - 1; j++) + { + rsizes.push_back(ss); + ind_map.push_back(i); + new_nc++; + } + rsizes.push_back(s[i]-(nr-1)*ss); + ind_map.push_back(i); + new_nc++; + } + + for (int i = nc; i < s.size(); i++) + { + rsizes.push_back(s[i]); + ind_map.push_back(i); + } +} diff --git a/dynare++/tl/cc/fine_container.cweb b/dynare++/tl/cc/fine_container.cweb deleted file mode 100644 index 8a6a868c5..000000000 --- a/dynare++/tl/cc/fine_container.cweb +++ /dev/null @@ -1,41 +0,0 @@ -@q $Id: fine_container.cweb 1833 2008-05-18 20:22:39Z kamenik $ @> -@q Copyright 2005, Ondra Kamenik @> - -@ Start of {\tt stack\_container.cpp} file. - -@c -#include "fine_container.h" - -#include - -@<|SizeRefinement| constructor code@>; - -@ Here we construct the vector of new sizes of containers (before -|nc|) and copy all remaining sizes behind |nc|. - -@<|SizeRefinement| constructor code@>= -SizeRefinement::SizeRefinement(const IntSequence& s, int nc, int max) -{ - new_nc = 0; - for (int i = 0; i < nc; i++) { - int nr = s[i]/max; - if (s[i] % max != 0) - nr++; - int ss = (nr>0) ? (int)round(((double)s[i])/nr) : 0; - for (int j = 0; j < nr - 1; j++) { - rsizes.push_back(ss); - ind_map.push_back(i); - new_nc++; - } - rsizes.push_back(s[i]-(nr-1)*ss); - ind_map.push_back(i); - new_nc++; - } - - for (int i = nc; i < s.size(); i++) { - rsizes.push_back(s[i]); - ind_map.push_back(i); - } -} - -@ End of {\tt stack\_container.cpp} file. diff --git a/dynare++/tl/cc/fine_container.hh b/dynare++/tl/cc/fine_container.hh new file mode 100644 index 000000000..fb5c01752 --- /dev/null +++ b/dynare++/tl/cc/fine_container.hh @@ -0,0 +1,162 @@ +// Copyright 2005, Ondra Kamenik + +// Refined stack of containers. + +/* This file defines a refinement of the stack container. It makes a + vertical refinement of a given stack container, it refines only matrix + items, the items which are always zero, or can be identity matrices + are not refined. + + The refinement is done by a simple construction from the stack + container being refined. A parameter is passed meaning a maximum size + of each stack in the refined container. The resulting object is stack + container, so everything works seamlessly. + + We define here a class for refinement of sizes |SizeRefinement|, this + is purely an auxiliary class allowing us to write a code more + concisely. The main class of this file is |FineContainer|, which + corresponds to refining. The two more classes |FoldedFineContainer| + and |UnfoldedFineContainer| are its specializations. + + NOTE: This code was implemented with a hope that it will help to cut + down memory allocations during the Faa Di Bruno formula + evaluation. However, it seems that this needs to be accompanied with a + similar thing for tensor multidimensional index. Thus, the abstraction + is not currently used, but it might be useful in future. */ + +#ifndef FINE_CONTAINER_H +#define FINE_CONTAINER_H + +#include "stack_container.hh" + +#include + +/* This class splits the first |nc| elements of the given sequence |s| + to a sequence not having items greater than given |max|. The remaining + elements (those behind |nc|) are left untouched. It also remembers the + mapping, i.e. for a given index in a new sequence, it is able to + return a corresponding index in old sequence. */ + +class SizeRefinement +{ + vector rsizes; + vector ind_map; + int new_nc; +public: + SizeRefinement(const IntSequence &s, int nc, int max); + int + getRefSize(int i) const + { + return rsizes[i]; + } + int + numRefinements() const + { + return rsizes.size(); + } + int + getOldIndex(int i) const + { + return ind_map[i]; + } + int + getNC() const + { + return new_nc; + } +}; + +/* This main class of this class refines a given stack container, and + inherits from the stack container. It also defines the |getType| + method, which returns a type for a given stack as the type of the + corresponding (old) stack of the former stack container. */ + +template +class FineContainer : public SizeRefinement, public StackContainer<_Ttype> +{ +protected: + typedef StackContainer<_Ttype> _Stype; + typedef typename StackContainerInterface<_Ttype>::_Ctype _Ctype; + typedef typename StackContainerInterface<_Ttype>::itype itype; + _Ctype **const ref_conts; + const _Stype &stack_cont; +public: + /* Here we construct the |SizeRefinement| and allocate space for the + refined containers. Then, the containers are created and put to + |conts| array. Note that the containers do not claim any further + space, since all the tensors of the created containers are in-place + submatrices. + + Here we use a dirty trick of converting |const| pointer to non-|const| + pointer and passing it to a subtensor container constructor. The + containers are stored in |ref_conts| and then in |conts| from + |StackContainer|. However, this is safe since neither |ref_conts| nor + |conts| are used in non-|const| contexts. For example, + |StackContainer| has only a |const| method to return a member of + |conts|. */ + + FineContainer(const _Stype &sc, int max) + : SizeRefinement(sc.getStackSizes(), sc.numConts(), max), + StackContainer<_Ttype>(numRefinements(), getNC()), + ref_conts(new _Ctype *[getNC()]), + stack_cont(sc) + { + for (int i = 0; i < numRefinements(); i++) + _Stype::stack_sizes[i] = getRefSize(i); + _Stype::calculateOffsets(); + + int last_cont = -1; + int last_row = 0; + for (int i = 0; i < getNC(); i++) + { + if (getOldIndex(i) != last_cont) + { + last_cont = getOldIndex(i); + last_row = 0; + } + union {const _Ctype *c; _Ctype *n;} convert; + convert.c = stack_cont.getCont(last_cont); + ref_conts[i] = new _Ctype(last_row, _Stype::stack_sizes[i], + *(convert.n)); + _Stype::conts[i] = ref_conts[i]; + last_row += _Stype::stack_sizes[i]; + } + } + + /* Here we deallocate the refined containers, and deallocate the array of + refined containers. */ + virtual ~FineContainer() + { + for (int i = 0; i < _Stype::numConts(); i++) + delete ref_conts[i]; + delete [] ref_conts; + } + itype + getType(int i, const Symmetry &s) const + { + return stack_cont.getType(getOldIndex(i), s); + } + +}; + +/* Here is |FineContainer| specialization for folded tensors. */ +class FoldedFineContainer : public FineContainer, public FoldedStackContainer +{ +public: + FoldedFineContainer(const StackContainer &sc, int max) + : FineContainer(sc, max) + { + } +}; + +/* Here is |FineContainer| specialization for unfolded tensors. */ +class UnfoldedFineContainer : public FineContainer, public UnfoldedStackContainer +{ +public: + UnfoldedFineContainer(const StackContainer &sc, int max) + : FineContainer(sc, max) + { + } +}; + +#endif diff --git a/dynare++/tl/cc/fine_container.hweb b/dynare++/tl/cc/fine_container.hweb deleted file mode 100644 index bec478ff4..000000000 --- a/dynare++/tl/cc/fine_container.hweb +++ /dev/null @@ -1,164 +0,0 @@ -@q $Id: fine_container.hweb 332 2005-07-15 13:41:48Z kamenik $ @> -@q Copyright 2005, Ondra Kamenik @> - -@*2 Refined stack of containers. Start of {\tt fine\_container.h} file. - -This file defines a refinement of the stack container. It makes a -vertical refinement of a given stack container, it refines only matrix -items, the items which are always zero, or can be identity matrices -are not refined. - -The refinement is done by a simple construction from the stack -container being refined. A parameter is passed meaning a maximum size -of each stack in the refined container. The resulting object is stack -container, so everything works seamlessly. - -We define here a class for refinement of sizes |SizeRefinement|, this -is purely an auxiliary class allowing us to write a code more -concisely. The main class of this file is |FineContainer|, which -corresponds to refining. The two more classes |FoldedFineContainer| -and |UnfoldedFineContainer| are its specializations. - -NOTE: This code was implemented with a hope that it will help to cut -down memory allocations during the Faa Di Bruno formula -evaluation. However, it seems that this needs to be accompanied with a -similar thing for tensor multidimensional index. Thus, the abstraction -is not currently used, but it might be useful in future. - -@s SizeRefinement int -@s FineContainer int -@s FoldedFineContainer int -@s UnfoldedFineContainer int - -@c -#ifndef FINE_CONTAINER_H -#define FINE_CONTAINER_H - -#include "stack_container.h" - -#include - -@<|SizeRefinement| class declaration@>; -@<|FineContainer| class declaration@>; -@<|FoldedFineContainer| class declaration@>; -@<|UnfoldedFineContainer| class declaration@>; - -#endif - -@ This class splits the first |nc| elements of the given sequence |s| -to a sequence not having items greater than given |max|. The remaining -elements (those behind |nc|) are left untouched. It also remembers the -mapping, i.e. for a given index in a new sequence, it is able to -return a corresponding index in old sequence. - -@<|SizeRefinement| class declaration@>= -class SizeRefinement { - vector rsizes; - vector ind_map; - int new_nc; -public:@; - SizeRefinement(const IntSequence& s, int nc, int max); - int getRefSize(int i) const - {@+ return rsizes[i];@+} - int numRefinements() const - {@+ return rsizes.size();@+} - int getOldIndex(int i) const - {@+ return ind_map[i];@+} - int getNC() const - {@+ return new_nc;@+} -}; - - -@ This main class of this class refines a given stack container, and -inherits from the stack container. It also defines the |getType| -method, which returns a type for a given stack as the type of the -corresponding (old) stack of the former stack container. - -@<|FineContainer| class declaration@>= -template @; -class FineContainer : public SizeRefinement, public StackContainer<_Ttype> { -protected:@; - typedef StackContainer<_Ttype> _Stype; - typedef typename StackContainerInterface<_Ttype>::_Ctype _Ctype; - typedef typename StackContainerInterface<_Ttype>::itype itype; - _Ctype** const ref_conts; - const _Stype& stack_cont; -public:@; - @<|FineContainer| constructor@>; - @<|FineContainer| destructor@>; - itype getType(int i, const Symmetry& s) const - {@+ return stack_cont.getType(getOldIndex(i), s);@+} - -}; - - -@ Here we construct the |SizeRefinement| and allocate space for the -refined containers. Then, the containers are created and put to -|conts| array. Note that the containers do not claim any further -space, since all the tensors of the created containers are in-place -submatrices. - -Here we use a dirty trick of converting |const| pointer to non-|const| -pointer and passing it to a subtensor container constructor. The -containers are stored in |ref_conts| and then in |conts| from -|StackContainer|. However, this is safe since neither |ref_conts| nor -|conts| are used in non-|const| contexts. For example, -|StackContainer| has only a |const| method to return a member of -|conts|. - -@<|FineContainer| constructor@>= -FineContainer(const _Stype& sc, int max) - : SizeRefinement(sc.getStackSizes(), sc.numConts(), max), - StackContainer<_Ttype>(numRefinements(), getNC()), - ref_conts(new _Ctype*[getNC()]), - stack_cont(sc) -{ - for (int i = 0; i < numRefinements(); i++) - _Stype::stack_sizes[i] = getRefSize(i); - _Stype::calculateOffsets(); - - int last_cont = -1; - int last_row = 0; - for (int i = 0; i < getNC(); i++) { - if (getOldIndex(i) != last_cont) { - last_cont = getOldIndex(i); - last_row = 0; - } - union {const _Ctype* c; _Ctype* n;} convert; - convert.c = stack_cont.getCont(last_cont); - ref_conts[i] = new _Ctype(last_row, _Stype::stack_sizes[i], - *(convert.n)); - _Stype::conts[i] = ref_conts[i]; - last_row += _Stype::stack_sizes[i]; - } -} - -@ Here we deallocate the refined containers, and deallocate the array of refined containers. -@<|FineContainer| destructor@>= -virtual ~FineContainer() -{ - for (int i = 0; i < _Stype::numConts(); i++) - delete ref_conts[i]; - delete [] ref_conts; -} - - - -@ Here is |FineContainer| specialization for folded tensors. -@<|FoldedFineContainer| class declaration@>= -class FoldedFineContainer : public FineContainer, public FoldedStackContainer { -public:@; - FoldedFineContainer(const StackContainer& sc, int max) - : FineContainer(sc, max) @+ {} -}; - -@ Here is |FineContainer| specialization for unfolded tensors. -@<|UnfoldedFineContainer| class declaration@>= -class UnfoldedFineContainer : public FineContainer, public UnfoldedStackContainer { -public:@; - UnfoldedFineContainer(const StackContainer& sc, int max) - : FineContainer(sc, max) @+ {} -}; - - -@ End of {\tt fine\_container.h} file. diff --git a/dynare++/tl/cc/fs_tensor.cc b/dynare++/tl/cc/fs_tensor.cc new file mode 100644 index 000000000..8e7db4d62 --- /dev/null +++ b/dynare++/tl/cc/fs_tensor.cc @@ -0,0 +1,290 @@ +// Copyright 2004, Ondra Kamenik + +#include "fs_tensor.hh" +#include "gs_tensor.hh" +#include "sparse_tensor.hh" +#include "rfs_tensor.hh" +#include "tl_exception.hh" + +/* This constructs a fully symmetric tensor as given by the contraction: + $$\left[g_{y^n}\right]_{\alpha_1\ldots\alpha_n}= + \left[t_{y^{n+1}}\right]_{\alpha_1\ldots\alpha_n\beta}[x]^\beta$$ + + We go through all columns of output tensor $[g]$ and for each column + we cycle through all variables, insert a variable to the column + coordinates obtaining a column of tensor $[t]$. the column is multiplied + by an appropriate item of |x| and added to the column of $[g]$ tensor. */ + +FFSTensor::FFSTensor(const FFSTensor &t, const ConstVector &x) + : FTensor(along_col, IntSequence(t.dimen()-1, t.nvar()), + t.nrows(), calcMaxOffset(t.nvar(), t.dimen()-1), t.dimen()-1), + nv(t.nvar()) +{ + TL_RAISE_IF(t.dimen() < 1, + "Wrong dimension for tensor contraction of FFSTensor"); + TL_RAISE_IF(t.nvar() != x.length(), + "Wrong number of variables for tensor contraction of FFSTensor"); + + zeros(); + + for (Tensor::index to = begin(); to != end(); ++to) + { + for (int i = 0; i < nvar(); i++) + { + IntSequence from_ind(i, to.getCoor()); + Tensor::index from(&t, from_ind); + addColumn(x[i], t, *from, *to); + } + } +} + +/* This returns number of indices for folded tensor with full + symmetry. Let $n$ be a number of variables |nvar| and $d$ the + dimension |dim|. Then the number of indices is $\pmatrix{n+d-1\cr d}$. */ + +int +FFSTensor::calcMaxOffset(int nvar, int d) +{ + if (nvar == 0 && d == 0) + return 1; + if (nvar == 0 && d > 0) + return 0; + return noverk(nvar + d - 1, d); +} + +/* The conversion from sparse tensor is clear. We go through all the + tensor and write to the dense what is found. */ +FFSTensor::FFSTensor(const FSSparseTensor &t) + : FTensor(along_col, IntSequence(t.dimen(), t.nvar()), + t.nrows(), calcMaxOffset(t.nvar(), t.dimen()), t.dimen()), + nv(t.nvar()) +{ + zeros(); + for (FSSparseTensor::const_iterator it = t.getMap().begin(); + it != t.getMap().end(); ++it) + { + index ind(this, (*it).first); + get((*it).second.first, *ind) = (*it).second.second; + } +} + +/* The conversion from unfolded copies only columns of respective + coordinates. So we go through all the columns in the folded tensor + (this), make an index of the unfolded vector from coordinates, and + copy the column. */ + +FFSTensor::FFSTensor(const UFSTensor &ut) + : FTensor(along_col, IntSequence(ut.dimen(), ut.nvar()), + ut.nrows(), calcMaxOffset(ut.nvar(), ut.dimen()), ut.dimen()), + nv(ut.nvar()) +{ + for (index in = begin(); in != end(); ++in) + { + index src(&ut, in.getCoor()); + copyColumn(ut, *src, *in); + } +} + +/* Here just make a new instance and return the reference. */ +UTensor & +FFSTensor::unfold() const +{ + return *(new UFSTensor(*this)); +} + +/* Incrementing is easy. We have to increment by calling static method + |UTensor::increment| first. In this way, we have coordinates of + unfolded tensor. Then we have to skip to the closest folded index + which corresponds to monotonizeing the integer sequence. */ + +void +FFSTensor::increment(IntSequence &v) const +{ + TL_RAISE_IF(v.size() != dimen(), + "Wrong input/output vector size in FFSTensor::increment"); + + UTensor::increment(v, nv); + v.monotone(); +} + +/* Decrement calls static |FTensor::decrement|. */ + +void +FFSTensor::decrement(IntSequence &v) const +{ + TL_RAISE_IF(v.size() != dimen(), + "Wrong input/output vector size in FFSTensor::decrement"); + + FTensor::decrement(v, nv); +} + +int +FFSTensor::getOffset(const IntSequence &v) const +{ + TL_RAISE_IF(v.size() != dimen(), + "Wrong input vector size in FFSTensor::getOffset"); + + return FTensor::getOffset(v, nv); +} + +/* Here we add a general symmetry tensor to the (part of) full symmetry + tensor provided that the unique variable of the full symmetry tensor + is a stack of variables from the general symmetry tensor. + + We check for the dimensions and number of variables. Then we calculate + a shift of coordinates when going from the general symmetry tensor to + full symmetry (it corresponds to shift of coordinates induces by + stacking the variables). Then we add the appropriate columns by going + through the columns in general symmetry, adding the shift and sorting. */ + +void +FFSTensor::addSubTensor(const FGSTensor &t) +{ + TL_RAISE_IF(dimen() != t.getDims().dimen(), + "Wrong dimensions for FFSTensor::addSubTensor"); + TL_RAISE_IF(nvar() != t.getDims().getNVS().sum(), + "Wrong nvs for FFSTensor::addSubTensor"); + + // set shift for |addSubTensor| + /* Code shared with UFSTensor::addSubTensor() */ + IntSequence shift_pre(t.getSym().num(), 0); + for (int i = 1; i < t.getSym().num(); i++) + shift_pre[i] = shift_pre[i-1]+t.getDims().getNVS()[i-1]; + IntSequence shift(t.getSym(), shift_pre); + + for (Tensor::index ind = t.begin(); ind != t.end(); ++ind) + { + IntSequence c(ind.getCoor()); + c.add(1, shift); + c.sort(); + Tensor::index tar(this, c); + addColumn(t, *ind, *tar); + } +} + +// |UFSTensor| contraction constructor +/* This is a bit more straightforward than |@<|FFSTensor| contraction constructor@>|. + We do not add column by column but we do it by submatrices due to + regularity of the unfolded tensor. */ + +UFSTensor::UFSTensor(const UFSTensor &t, const ConstVector &x) + : UTensor(along_col, IntSequence(t.dimen()-1, t.nvar()), + t.nrows(), calcMaxOffset(t.nvar(), t.dimen()-1), t.dimen()-1), + nv(t.nvar()) +{ + TL_RAISE_IF(t.dimen() < 1, + "Wrong dimension for tensor contraction of UFSTensor"); + TL_RAISE_IF(t.nvar() != x.length(), + "Wrong number of variables for tensor contraction of UFSTensor"); + + zeros(); + + for (int i = 0; i < ncols(); i++) + { + ConstTwoDMatrix tpart(t, i *nvar(), nvar()); + Vector outcol(*this, i); + tpart.multaVec(outcol, x); + } +} + +/* Here we convert folded full symmetry tensor to unfolded. We copy all + columns of folded tensor, and then call |unfoldData()|. */ + +UFSTensor::UFSTensor(const FFSTensor &ft) + : UTensor(along_col, IntSequence(ft.dimen(), ft.nvar()), + ft.nrows(), calcMaxOffset(ft.nvar(), ft.dimen()), ft.dimen()), + nv(ft.nvar()) +{ + for (index src = ft.begin(); src != ft.end(); ++src) + { + index in(this, src.getCoor()); + copyColumn(ft, *src, *in); + } + unfoldData(); +} + +/* Here we just return a reference to new instance of folded tensor. */ +FTensor & +UFSTensor::fold() const +{ + return *(new FFSTensor(*this)); +} + +// |UFSTensor| increment and decrement +/* Here we just call |UTensor| respective static methods. */ +void +UFSTensor::increment(IntSequence &v) const +{ + TL_RAISE_IF(v.size() != dimen(), + "Wrong input/output vector size in UFSTensor::increment"); + + UTensor::increment(v, nv); +} + +void +UFSTensor::decrement(IntSequence &v) const +{ + TL_RAISE_IF(v.size() != dimen(), + "Wrong input/output vector size in UFSTensor::decrement"); + + UTensor::decrement(v, nv); +} + +int +UFSTensor::getOffset(const IntSequence &v) const +{ + TL_RAISE_IF(v.size() != dimen(), + "Wrong input vector size in UFSTensor::getOffset"); + + return UTensor::getOffset(v, nv); +} + +/* This is very similar to |@<|FFSTensor::addSubTensor| code@>|. The + only difference is the addition. We go through all columns in the full + symmetry tensor and cancel the shift. If the coordinates after the + cancellation are positive, we find the column in the general symmetry + tensor, and add it. */ + +void +UFSTensor::addSubTensor(const UGSTensor &t) +{ + TL_RAISE_IF(dimen() != t.getDims().dimen(), + "Wrong dimensions for UFSTensor::addSubTensor"); + TL_RAISE_IF(nvar() != t.getDims().getNVS().sum(), + "Wrong nvs for UFSTensor::addSubTensor"); + + // set shift for |addSubTensor| + /* Code shared with FFSTensor::addSubTensor() */ + IntSequence shift_pre(t.getSym().num(), 0); + for (int i = 1; i < t.getSym().num(); i++) + shift_pre[i] = shift_pre[i-1]+t.getDims().getNVS()[i-1]; + IntSequence shift(t.getSym(), shift_pre); + + for (Tensor::index tar = begin(); tar != end(); ++tar) + { + IntSequence c(tar.getCoor()); + c.sort(); + c.add(-1, shift); + if (c.isPositive() && c.less(t.getDims().getNVX())) + { + Tensor::index from(&t, c); + addColumn(t, *from, *tar); + } + } +} + +/* Here we go through all columns, find a column of folded index, and + then copy the column data. Finding the index is done by sorting the + integer sequence. */ + +void +UFSTensor::unfoldData() +{ + for (index in = begin(); in != end(); ++in) + { + IntSequence v(in.getCoor()); + v.sort(); + index tmp(this, v); + copyColumn(*tmp, *in); + } +} diff --git a/dynare++/tl/cc/fs_tensor.cweb b/dynare++/tl/cc/fs_tensor.cweb deleted file mode 100644 index 1635dffcc..000000000 --- a/dynare++/tl/cc/fs_tensor.cweb +++ /dev/null @@ -1,306 +0,0 @@ -@q $Id: fs_tensor.cweb 280 2005-06-13 09:40:02Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@ Start of {\tt fs\_tensor.cpp} file. - -@c -#include "fs_tensor.h" -#include "gs_tensor.h" -#include "sparse_tensor.h" -#include "rfs_tensor.h" -#include "tl_exception.h" - -@<|FFSTensor| contraction constructor@>; -@<|FFSTensor::calcMaxOffset| code@>; -@<|FFSTensor| conversion from sparse@>; -@<|FFSTensor| conversion from unfolded@>; -@<|FFSTensor::unfold| code@>; -@<|FFSTensor::increment| code@>; -@<|FFSTensor::decrement| code@>; -@<|FFSTensor::getOffset| code@>; -@<|FFSTensor::addSubTensor| code@>; -@<|UFSTensor| contraction constructor@>; -@<|UFSTensor| conversion from folded@>; -@<|UFSTensor::fold| code@>; -@<|UFSTensor| increment and decrement@>; -@<|UFSTensor::getOffset| code@>; -@<|UFSTensor::addSubTensor| code@>; -@<|UFSTensor::unfoldData| code@>; - -@ This constructs a fully symmetric tensor as given by the contraction: -$$\left[g_{y^n}\right]_{\alpha_1\ldots\alpha_n}= -\left[t_{y^{n+1}}\right]_{\alpha_1\ldots\alpha_n\beta}[x]^\beta$$ - -We go through all columns of output tensor $[g]$ and for each column -we cycle through all variables, insert a variable to the column -coordinates obtaining a column of tensor $[t]$. the column is multiplied -by an appropriate item of |x| and added to the column of $[g]$ tensor. - -@<|FFSTensor| contraction constructor@>= -FFSTensor::FFSTensor(const FFSTensor& t, const ConstVector& x) - : FTensor(along_col, IntSequence(t.dimen()-1, t.nvar()), - t.nrows(), calcMaxOffset(t.nvar(), t.dimen()-1), t.dimen()-1), - nv(t.nvar()) -{ - TL_RAISE_IF(t.dimen() < 1, - "Wrong dimension for tensor contraction of FFSTensor"); - TL_RAISE_IF(t.nvar() != x.length(), - "Wrong number of variables for tensor contraction of FFSTensor"); - - zeros(); - - for (Tensor::index to = begin(); to != end(); ++to) { - for (int i = 0; i < nvar(); i++) { - IntSequence from_ind(i, to.getCoor()); - Tensor::index from(&t, from_ind); - addColumn(x[i], t, *from, *to); - } - } -} - - -@ This returns number of indices for folded tensor with full -symmetry. Let $n$ be a number of variables |nvar| and $d$ the -dimension |dim|. Then the number of indices is $\pmatrix{n+d-1\cr d}$. - -@<|FFSTensor::calcMaxOffset| code@>= -int FFSTensor::calcMaxOffset(int nvar, int d) -{ - if (nvar == 0 && d == 0) - return 1; - if (nvar == 0 && d > 0) - return 0; - return noverk(nvar + d - 1, d); -} - -@ The conversion from sparse tensor is clear. We go through all the -tensor and write to the dense what is found. -@<|FFSTensor| conversion from sparse@>= -FFSTensor::FFSTensor(const FSSparseTensor& t) - : FTensor(along_col, IntSequence(t.dimen(), t.nvar()), - t.nrows(), calcMaxOffset(t.nvar(), t.dimen()), t.dimen()), - nv(t.nvar()) -{ - zeros(); - for (FSSparseTensor::const_iterator it = t.getMap().begin(); - it != t.getMap().end(); ++it) { - index ind(this, (*it).first); - get((*it).second.first, *ind) = (*it).second.second; - } -} - - -@ The conversion from unfolded copies only columns of respective -coordinates. So we go through all the columns in the folded tensor -(this), make an index of the unfolded vector from coordinates, and -copy the column. - -@<|FFSTensor| conversion from unfolded@>= -FFSTensor::FFSTensor(const UFSTensor& ut) - : FTensor(along_col, IntSequence(ut.dimen(), ut.nvar()), - ut.nrows(), calcMaxOffset(ut.nvar(), ut.dimen()), ut.dimen()), - nv(ut.nvar()) -{ - for (index in = begin(); in != end(); ++in) { - index src(&ut, in.getCoor()); - copyColumn(ut, *src, *in); - } -} - -@ Here just make a new instance and return the reference. -@<|FFSTensor::unfold| code@>= -UTensor& FFSTensor::unfold() const -{ - return *(new UFSTensor(*this)); -} - -@ Incrementing is easy. We have to increment by calling static method -|UTensor::increment| first. In this way, we have coordinates of -unfolded tensor. Then we have to skip to the closest folded index -which corresponds to monotonizeing the integer sequence. - -@<|FFSTensor::increment| code@>= -void FFSTensor::increment(IntSequence& v) const -{ - TL_RAISE_IF(v.size() != dimen(), - "Wrong input/output vector size in FFSTensor::increment"); - - UTensor::increment(v, nv); - v.monotone(); -} - -@ Decrement calls static |FTensor::decrement|. - -@<|FFSTensor::decrement| code@>= -void FFSTensor::decrement(IntSequence& v) const -{ - TL_RAISE_IF(v.size() != dimen(), - "Wrong input/output vector size in FFSTensor::decrement"); - - FTensor::decrement(v, nv); -} - -@ -@<|FFSTensor::getOffset| code@>= -int FFSTensor::getOffset(const IntSequence& v) const -{ - TL_RAISE_IF(v.size() != dimen(), - "Wrong input vector size in FFSTensor::getOffset"); - - return FTensor::getOffset(v, nv); -} - -@ Here we add a general symmetry tensor to the (part of) full symmetry -tensor provided that the unique variable of the full symmetry tensor -is a stack of variables from the general symmetry tensor. - -We check for the dimensions and number of variables. Then we calculate -a shift of coordinates when going from the general symmetry tensor to -full symmetry (it corresponds to shift of coordinates induces by -stacking the variables). Then we add the appropriate columns by going -through the columns in general symmetry, adding the shift and sorting. - -@<|FFSTensor::addSubTensor| code@>= -void FFSTensor::addSubTensor(const FGSTensor& t) -{ - TL_RAISE_IF(dimen() != t.getDims().dimen(), - "Wrong dimensions for FFSTensor::addSubTensor"); - TL_RAISE_IF(nvar() != t.getDims().getNVS().sum(), - "Wrong nvs for FFSTensor::addSubTensor"); - - @; - for (Tensor::index ind = t.begin(); ind != t.end(); ++ind) { - IntSequence c(ind.getCoor()); - c.add(1, shift); - c.sort(); - Tensor::index tar(this, c); - addColumn(t, *ind, *tar); - } -} - -@ -@= - IntSequence shift_pre(t.getSym().num(), 0); - for (int i = 1; i < t.getSym().num(); i++) - shift_pre[i] = shift_pre[i-1]+t.getDims().getNVS()[i-1]; - IntSequence shift(t.getSym(), shift_pre); - -@ This is a bit more straightforward than |@<|FFSTensor| contraction constructor@>|. -We do not add column by column but we do it by submatrices due to -regularity of the unfolded tensor. - -@<|UFSTensor| contraction constructor@>= -UFSTensor::UFSTensor(const UFSTensor& t, const ConstVector& x) - : UTensor(along_col, IntSequence(t.dimen()-1, t.nvar()), - t.nrows(), calcMaxOffset(t.nvar(), t.dimen()-1), t.dimen()-1), - nv(t.nvar()) -{ - TL_RAISE_IF(t.dimen() < 1, - "Wrong dimension for tensor contraction of UFSTensor"); - TL_RAISE_IF(t.nvar() != x.length(), - "Wrong number of variables for tensor contraction of UFSTensor"); - - zeros(); - - for (int i = 0; i < ncols(); i++) { - ConstTwoDMatrix tpart(t, i*nvar(), nvar()); - Vector outcol(*this, i); - tpart.multaVec(outcol, x); - } -} - -@ Here we convert folded full symmetry tensor to unfolded. We copy all -columns of folded tensor, and then call |unfoldData()|. - -@<|UFSTensor| conversion from folded@>= -UFSTensor::UFSTensor(const FFSTensor& ft) - : UTensor(along_col, IntSequence(ft.dimen(), ft.nvar()), - ft.nrows(), calcMaxOffset(ft.nvar(), ft.dimen()), ft.dimen()), - nv(ft.nvar()) -{ - for (index src = ft.begin(); src != ft.end(); ++src) { - index in(this, src.getCoor()); - copyColumn(ft, *src, *in); - } - unfoldData(); -} - -@ Here we just return a reference to new instance of folded tensor. -@<|UFSTensor::fold| code@>= -FTensor& UFSTensor::fold() const -{ - return *(new FFSTensor(*this)); -} - -@ Here we just call |UTensor| respective static methods. -@<|UFSTensor| increment and decrement@>= -void UFSTensor::increment(IntSequence& v) const -{ - TL_RAISE_IF(v.size() != dimen(), - "Wrong input/output vector size in UFSTensor::increment"); - - UTensor::increment(v, nv); -} - -void UFSTensor::decrement(IntSequence& v) const -{ - TL_RAISE_IF(v.size() != dimen(), - "Wrong input/output vector size in UFSTensor::decrement"); - - UTensor::decrement(v, nv); -} - -@ -@<|UFSTensor::getOffset| code@>= -int UFSTensor::getOffset(const IntSequence& v) const -{ - TL_RAISE_IF(v.size() != dimen(), - "Wrong input vector size in UFSTensor::getOffset"); - - return UTensor::getOffset(v, nv); -} - -@ This is very similar to |@<|FFSTensor::addSubTensor| code@>|. The -only difference is the addition. We go through all columns in the full -symmetry tensor and cancel the shift. If the coordinates after the -cancellation are positive, we find the column in the general symmetry -tensor, and add it. - -@<|UFSTensor::addSubTensor| code@>= -void UFSTensor::addSubTensor(const UGSTensor& t) -{ - TL_RAISE_IF(dimen() != t.getDims().dimen(), - "Wrong dimensions for UFSTensor::addSubTensor"); - TL_RAISE_IF(nvar() != t.getDims().getNVS().sum(), - "Wrong nvs for UFSTensor::addSubTensor"); - - @; - for (Tensor::index tar = begin(); tar != end(); ++tar) { - IntSequence c(tar.getCoor()); - c.sort(); - c.add(-1, shift); - if (c.isPositive() && c.less(t.getDims().getNVX())) { - Tensor::index from(&t, c); - addColumn(t, *from, *tar); - } - } -} - - -@ Here we go through all columns, find a column of folded index, and -then copy the column data. Finding the index is done by sorting the -integer sequence. - -@<|UFSTensor::unfoldData| code@>= -void UFSTensor::unfoldData() -{ - for (index in = begin(); in != end(); ++in) { - IntSequence v(in.getCoor()); - v.sort(); - index tmp(this, v); - copyColumn(*tmp, *in); - } -} - - -@ End of {\tt fs\_tensor.cpp} file. \ No newline at end of file diff --git a/dynare++/tl/cc/fs_tensor.hh b/dynare++/tl/cc/fs_tensor.hh new file mode 100644 index 000000000..2890cc02b --- /dev/null +++ b/dynare++/tl/cc/fs_tensor.hh @@ -0,0 +1,141 @@ +// Copyright 2004, Ondra Kamenik + +// Full symmetry tensor. + +/* Here we define folded and unfolded tensors for full symmetry. All + tensors from here are identifying the multidimensional index with + columns. */ + +#ifndef FS_TENSOR_H +#define FS_TENSOR_H + +#include "tensor.hh" +#include "symmetry.hh" + +class FGSTensor; +class UGSTensor; +class FRSingleTensor; +class FSSparseTensor; + +/* Folded tensor with full symmetry maintains only information about + number of symmetrical variables |nv|. Further, we implement what is + left from the super class |FTensor|. + + We implement |getOffset| which should be used with care since + its complexity. + + We implement a method adding a given general symmetry tensor to the + full symmetry tensor supposing the variables of the general symmetry + tensor are stacked giving only one variable of the full symmetry + tensor. For instance, if $x=[y^T, u^T]^T$, then we can add tensor + $\left[g_{y^2u}\right]$ to tensor $g_{x^3}$. This is done in method + |addSubTensor|. Consult |@<|FGSTensor| class declaration@>| to know + what is general symmetry tensor. */ + +class UFSTensor; +class FFSTensor : public FTensor +{ + int nv; +public: + /* Here are the constructors. The second constructor constructs a + tensor by one-dimensional contraction from the higher dimensional + tensor |t|. This is, it constructs a tensor + $$\left[g_{y^n}\right]_{\alpha_1\ldots\alpha_n}= + \left[t_{y^{n+1}}\right]_{\alpha_1\ldots\alpha_n\beta}[x]^\beta$$ + See implementation |@<|FFSTensor| contraction constructor@>| for details. + + The next constructor converts from sparse tensor (which is fully + symmetric and folded by nature). + + The fourth constructs object from unfolded fully symmetric. + + The fifth constructs a subtensor of selected rows. */ + + FFSTensor(int r, int nvar, int d) + : FTensor(along_col, IntSequence(d, nvar), + r, calcMaxOffset(nvar, d), d), nv(nvar) + { + } + FFSTensor(const FFSTensor &t, const ConstVector &x); + FFSTensor(const FSSparseTensor &t); + FFSTensor(const FFSTensor &ft) + : FTensor(ft), nv(ft.nv) + { + } + FFSTensor(const UFSTensor &ut); + FFSTensor(int first_row, int num, FFSTensor &t) + : FTensor(first_row, num, t), nv(t.nv) + { + } + + void increment(IntSequence &v) const; + void decrement(IntSequence &v) const; + UTensor&unfold() const; + Symmetry + getSym() const + { + return Symmetry(dimen()); + } + + int getOffset(const IntSequence &v) const; + void addSubTensor(const FGSTensor &t); + int + nvar() const + { + return nv; + } + static int calcMaxOffset(int nvar, int d); +}; + +/* Unfolded fully symmetric tensor is almost the same in structure as + |FFSTensor|, but the method |unfoldData|. It takes columns which also + exist in folded version and copies them to all their symmetrical + locations. This is useful when constructing unfolded tensor from + folded one. */ + +class UFSTensor : public UTensor +{ + int nv; +public: + UFSTensor(int r, int nvar, int d) + : UTensor(along_col, IntSequence(d, nvar), + r, calcMaxOffset(nvar, d), d), nv(nvar) + { + } + UFSTensor(const UFSTensor &t, const ConstVector &x); + UFSTensor(const UFSTensor &ut) + : UTensor(ut), nv(ut.nv) + { + } + UFSTensor(const FFSTensor &ft); + UFSTensor(int first_row, int num, UFSTensor &t) + : UTensor(first_row, num, t), nv(t.nv) + { + } + + void increment(IntSequence &v) const; + void decrement(IntSequence &v) const; + FTensor&fold() const; + Symmetry + getSym() const + { + return Symmetry(dimen()); + } + + int getOffset(const IntSequence &v) const; + void addSubTensor(const UGSTensor &t); + int + nvar() const + { + return nv; + } + static int + calcMaxOffset(int nvar, int d) + { + return power(nvar, d); + } +private: + void unfoldData(); +}; + +#endif diff --git a/dynare++/tl/cc/fs_tensor.hweb b/dynare++/tl/cc/fs_tensor.hweb deleted file mode 100644 index 134d0b066..000000000 --- a/dynare++/tl/cc/fs_tensor.hweb +++ /dev/null @@ -1,129 +0,0 @@ -@q $Id: fs_tensor.hweb 741 2006-05-09 11:12:46Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@*2 Full symmetry tensor. Start of {\tt fs\_tensor.h} file. - -Here we define folded and unfolded tensors for full symmetry. All -tensors from here are identifying the multidimensional index with -columns. - -@c -#ifndef FS_TENSOR_H -#define FS_TENSOR_H - -#include "tensor.h" -#include "symmetry.h" - -class FGSTensor; -class UGSTensor; -class FRSingleTensor; -class FSSparseTensor; -@<|FFSTensor| class declaration@>; -@<|UFSTensor| class declaration@>; - -#endif - - -@ Folded tensor with full symmetry maintains only information about -number of symmetrical variables |nv|. Further, we implement what is -left from the super class |FTensor|. - -We implement |getOffset| which should be used with care since -its complexity. - -We implement a method adding a given general symmetry tensor to the -full symmetry tensor supposing the variables of the general symmetry -tensor are stacked giving only one variable of the full symmetry -tensor. For instance, if $x=[y^T, u^T]^T$, then we can add tensor -$\left[g_{y^2u}\right]$ to tensor $g_{x^3}$. This is done in method -|addSubTensor|. Consult |@<|FGSTensor| class declaration@>| to know -what is general symmetry tensor. - -@<|FFSTensor| class declaration@>= -class UFSTensor; -class FFSTensor : public FTensor { - int nv; -public:@; - @<|FFSTensor| constructor declaration@>; - - void increment(IntSequence& v) const; - void decrement(IntSequence& v) const; - UTensor& unfold() const; - Symmetry getSym() const - {@+ return Symmetry(dimen());@+} - - int getOffset(const IntSequence& v) const; - void addSubTensor(const FGSTensor& t); - int nvar() const - {@+return nv;@+} - static int calcMaxOffset(int nvar, int d); -}; - -@ Here are the constructors. The second constructor constructs a -tensor by one-dimensional contraction from the higher dimensional -tensor |t|. This is, it constructs a tensor -$$\left[g_{y^n}\right]_{\alpha_1\ldots\alpha_n}= -\left[t_{y^{n+1}}\right]_{\alpha_1\ldots\alpha_n\beta}[x]^\beta$$ -See implementation |@<|FFSTensor| contraction constructor@>| for details. - -The next constructor converts from sparse tensor (which is fully -symmetric and folded by nature). - -The fourth constructs object from unfolded fully symmetric. - -The fifth constructs a subtensor of selected rows. - -@<|FFSTensor| constructor declaration@>= - FFSTensor(int r, int nvar, int d) - : FTensor(along_col, IntSequence(d, nvar), - r, calcMaxOffset(nvar, d), d), nv(nvar)@+ {} - FFSTensor(const FFSTensor& t, const ConstVector& x); - FFSTensor(const FSSparseTensor& t); - FFSTensor(const FFSTensor& ft) - : FTensor(ft), nv(ft.nv)@+ {} - FFSTensor(const UFSTensor& ut); - FFSTensor(int first_row, int num, FFSTensor& t) - : FTensor(first_row, num, t), nv(t.nv)@+ {} - - -@ Unfolded fully symmetric tensor is almost the same in structure as -|FFSTensor|, but the method |unfoldData|. It takes columns which also -exist in folded version and copies them to all their symmetrical -locations. This is useful when constructing unfolded tensor from -folded one. - -@<|UFSTensor| class declaration@>= -class UFSTensor : public UTensor { - int nv; -public:@; - @<|UFSTensor| constructor declaration@>; - - void increment(IntSequence& v) const; - void decrement(IntSequence& v) const; - FTensor& fold() const; - Symmetry getSym() const - {@+ return Symmetry(dimen());@+} - - int getOffset(const IntSequence& v) const; - void addSubTensor(const UGSTensor& t); - int nvar() const - {@+ return nv;@+} - static int calcMaxOffset(int nvar, int d) - {@+ return power(nvar, d);@+} -private:@; - void unfoldData(); -}; - -@ -@<|UFSTensor| constructor declaration@>= - UFSTensor(int r, int nvar, int d) - : UTensor(along_col, IntSequence(d, nvar), - r, calcMaxOffset(nvar, d), d), nv(nvar)@+ {} - UFSTensor(const UFSTensor& t, const ConstVector& x); - UFSTensor(const UFSTensor& ut) - : UTensor(ut), nv(ut.nv)@+ {} - UFSTensor(const FFSTensor& ft); - UFSTensor(int first_row, int num, UFSTensor& t) - : UTensor(first_row, num, t), nv(t.nv)@+ {} - -@ End of {\tt fs\_tensor.h} file. diff --git a/dynare++/tl/cc/gs_tensor.cc b/dynare++/tl/cc/gs_tensor.cc new file mode 100644 index 000000000..100788b8d --- /dev/null +++ b/dynare++/tl/cc/gs_tensor.cc @@ -0,0 +1,490 @@ +// Copyright 2004, Ondra Kamenik + +#include "gs_tensor.hh" +#include "sparse_tensor.hh" +#include "tl_exception.hh" +#include "kron_prod.hh" + +/* This constructs the tensor dimensions for slicing. See + |@<|TensorDimens| class declaration@>| for details. */ +TensorDimens::TensorDimens(const IntSequence &ss, const IntSequence &coor) + : nvs(ss), + sym(ss.size(), ""), + nvmax(coor.size(), 0) +{ + TL_RAISE_IF(!coor.isSorted(), + "Coordinates not sorted in TensorDimens slicing constructor"); + TL_RAISE_IF(coor[0] < 0 || coor[coor.size()-1] >= ss.size(), + "A coordinate out of stack range in TensorDimens slicing constructor"); + + for (int i = 0; i < coor.size(); i++) + { + sym[coor[i]]++; + nvmax[i] = ss[coor[i]]; + } +} + +/* Number of unfold offsets is a product of all members of |nvmax|. */ +int +TensorDimens::calcUnfoldMaxOffset() const +{ + return nvmax.mult(); +} + +/* Number of folded offsets is a product of all unfold offsets within + each equivalence class of the symmetry. */ + +int +TensorDimens::calcFoldMaxOffset() const +{ + int res = 1; + for (int i = 0; i < nvs.size(); i++) + { + if (nvs[i] == 0 && sym[i] > 0) + return 0; + if (sym[i] > 0) + res *= Tensor::noverk(nvs[i]+sym[i]-1, sym[i]); + } + return res; +} + +/* Here we implement offset calculation for folded general symmetry + tensor. The offset of a given sequence is calculated by breaking the + sequence to subsequences according to the symmetry. The offset is + orthogonal with respect to the blocks, this means that indexing within + the blocks is independent. If there are two blocks, for instance, then + the offset will be an offset within the outer block (the first) + multiplied with all offsets of the inner block (last) plus an offset + within the second block. + + Generally, the resulting offset $r$ will be + $$\sum_{i=1}^s r_i\cdot\left(\prod_{j=i+1}^sn_j\right),$$ + where $s$ is a number of blocks (|getSym().num()|), $r_i$ is an offset + within $i$-th block, and $n_j$ is a number of all offsets in $j$-th + block. + + In the code, we go from the innermost to the outermost, maintaining the + product in |pow|. */ + +int +TensorDimens::calcFoldOffset(const IntSequence &v) const +{ + TL_RAISE_IF(v.size() != dimen(), + "Wrong input vector size in TensorDimens::getFoldOffset"); + + int res = 0; + int pow = 1; + int blstart = v.size(); + for (int ibl = getSym().num()-1; ibl >= 0; ibl--) + { + int bldim = getSym()[ibl]; + if (bldim > 0) + { + blstart -= bldim; + int blnvar = getNVX()[blstart]; + IntSequence subv(v, blstart, blstart+bldim); + res += FTensor::getOffset(subv, blnvar)*pow; + pow *= FFSTensor::calcMaxOffset(blnvar, bldim); + } + } + TL_RAISE_IF(blstart != 0, + "Error in tracing symmetry in TensorDimens::getFoldOffset"); + return res; +} + +/* In order to find the predecessor of index within folded generally + symmetric tensor, note, that a decrease action in $i$-th partition of + symmetric indices can happen only if all indices in all subsequent + partitions are zero. Then the decrease action of whole the index + consists of decrease action of the first nonzero partition from the + right, and setting these trailing zero partitions to their maximum + indices. + + So we set |iblock| to the number of last partitions. During the + execution, |block_first|, and |block_last| will point to the first + element of |iblock| and, first element of following block. + + Then we check for all trailing zero partitions, set them to their + maximums and return |iblock| to point to the first non-zero partition + (or the first partition). Then for this partition, we decrease the + index (fully symmetric within that partition). */ + +void +TensorDimens::decrement(IntSequence &v) const +{ + TL_RAISE_IF(getNVX().size() != v.size(), + "Wrong size of input/output sequence in TensorDimens::decrement"); + + int iblock = getSym().num()-1; + int block_last = v.size(); + int block_first = block_last-getSym()[iblock]; + + // check for zero trailing blocks + while (iblock > 0 && v[block_last-1] == 0) + { + for (int i = block_first; i < block_last; i++) + v[i] = getNVX(i); // equivalent to |nvs[iblock]| + iblock--; + block_last = block_first; + block_first -= getSym()[iblock]; + } + + // decrease the non-zero block + IntSequence vtmp(v, block_first, block_last); + FTensor::decrement(vtmp, getNVX(block_first)); +} + +// |FGSTensor| conversion from |UGSTensor| +/* Here we go through columns of folded, calculate column of unfolded, + and copy data. */ +FGSTensor::FGSTensor(const UGSTensor &ut) + : FTensor(along_col, ut.tdims.getNVX(), ut.nrows(), + ut.tdims.calcFoldMaxOffset(), ut.dimen()), + tdims(ut.tdims) +{ + for (index ti = begin(); ti != end(); ++ti) + { + index ui(&ut, ti.getCoor()); + copyColumn(ut, *ui, *ti); + } +} + +// |FGSTensor| slicing from |FSSparseTensor| +/* Here is the code of slicing constructor from the sparse tensor. We + first calculate coordinates of first and last index of the slice + within the sparse tensor (these are |lb| and |ub|), and then we + iterate through all items between them (in lexicographical ordering of + sparse tensor), and check whether an item is between the |lb| and |ub| + in Cartesian ordering (this corresponds to belonging to the + slices). If it belongs, then we subtract the lower bound |lb| to + obtain coordinates in the |this| tensor and we copy the item. */ +FGSTensor::FGSTensor(const FSSparseTensor &t, const IntSequence &ss, + const IntSequence &coor, const TensorDimens &td) + : FTensor(along_col, td.getNVX(), t.nrows(), + td.calcFoldMaxOffset(), td.dimen()), + tdims(td) +{ + // set |lb| and |ub| to lower and upper bounds of indices + /* Here we first set |s_offsets| to offsets of partitions whose lengths + are given by |ss|. So |s_offsets| is a cumulative sum of |ss|. + + Then we create |lb| to be coordinates of the possibly first index from + the slice, and |ub| to be coordinates of possibly last index of the + slice. */ + IntSequence s_offsets(ss.size(), 0); + for (int i = 1; i < ss.size(); i++) + s_offsets[i] = s_offsets[i-1] + ss[i-1]; + + IntSequence lb(coor.size()); + IntSequence ub(coor.size()); + for (int i = 0; i < coor.size(); i++) + { + lb[i] = s_offsets[coor[i]]; + ub[i] = s_offsets[coor[i]] + ss[coor[i]] - 1; + } + + zeros(); + FSSparseTensor::const_iterator lbi = t.getMap().lower_bound(lb); + FSSparseTensor::const_iterator ubi = t.getMap().upper_bound(ub); + for (FSSparseTensor::const_iterator run = lbi; run != ubi; ++run) + { + if (lb.lessEq((*run).first) && (*run).first.lessEq(ub)) + { + IntSequence c((*run).first); + c.add(-1, lb); + Tensor::index ind(this, c); + TL_RAISE_IF(*ind < 0 || *ind >= ncols(), + "Internal error in slicing constructor of FGSTensor"); + get((*run).second.first, *ind) = (*run).second.second; + } + } +} + +// |FGSTensor| slicing from |FFSTensor| +/* The code is similar to |@<|FGSTensor| slicing from |FSSparseTensor|@>|. */ +FGSTensor::FGSTensor(const FFSTensor &t, const IntSequence &ss, + const IntSequence &coor, const TensorDimens &td) + : FTensor(along_col, td.getNVX(), t.nrows(), + td.calcFoldMaxOffset(), td.dimen()), + tdims(td) +{ + if (ncols() == 0) + return; + + // set |lb| and |ub| to lower and upper bounds of indices + /* Same code as in the previous converting constructor */ + IntSequence s_offsets(ss.size(), 0); + for (int i = 1; i < ss.size(); i++) + s_offsets[i] = s_offsets[i-1] + ss[i-1]; + + IntSequence lb(coor.size()); + IntSequence ub(coor.size()); + for (int i = 0; i < coor.size(); i++) + { + lb[i] = s_offsets[coor[i]]; + ub[i] = s_offsets[coor[i]] + ss[coor[i]] - 1; + } + + zeros(); + Tensor::index lbi(&t, lb); + Tensor::index ubi(&t, ub); + ++ubi; + for (Tensor::index run = lbi; run != ubi; ++run) + { + if (lb.lessEq(run.getCoor()) && run.getCoor().lessEq(ub)) + { + IntSequence c(run.getCoor()); + c.add(-1, lb); + Tensor::index ind(this, c); + TL_RAISE_IF(*ind < 0 || *ind >= ncols(), + "Internal error in slicing constructor of FGSTensor"); + copyColumn(t, *run, *ind); + } + } +} + +// |FGSTensor| conversion from |GSSparseTensor| +FGSTensor::FGSTensor(const GSSparseTensor &t) + : FTensor(along_col, t.getDims().getNVX(), t.nrows(), + t.getDims().calcFoldMaxOffset(), t.dimen()), tdims(t.getDims()) +{ + zeros(); + for (FSSparseTensor::const_iterator it = t.getMap().begin(); + it != t.getMap().end(); ++it) + { + index ind(this, (*it).first); + get((*it).second.first, *ind) = (*it).second.second; + } +} + +/* First we increment as unfolded, then we must monotonize within + partitions defined by the symmetry. This is done by + |IntSequence::pmonotone|. */ + +void +FGSTensor::increment(IntSequence &v) const +{ + TL_RAISE_IF(v.size() != dimen(), + "Wrong input/output vector size in FGSTensor::increment"); + + UTensor::increment(v, tdims.getNVX()); + v.pmonotone(tdims.getSym()); +} + +/* Return unfolded version of the tensor. */ +UTensor & +FGSTensor::unfold() const +{ + return *(new UGSTensor(*this)); +} + +/* Here we implement the contraction + $$\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} + $$ + More generally, $x^i$ and $z^k$ can represent also general symmetries. + + The operation can be rewritten as a matrix product + $$\left[t_{x^iy^jz^k}\right]\cdot\left(I_l\otimes c\otimes I_r\right)$$ + where $l$ is a number of columns in tensor with symmetry on the left + (i.e. $x^i$), and $r$ is a number of columns in tensor with a symmetry + on the right (i.e. $z^k$). The code proceeds accordingly. We first + form two symmetries |sym_left| and |sym_right|, then calculate the + number of columns |dleft|$=l$ and |dright|$=r$, form the Kronecker + product and multiply and add. + + The input parameter |i| is the order of a variable being contracted + starting from 0. */ + +void +FGSTensor::contractAndAdd(int i, FGSTensor &out, + const FRSingleTensor &col) const +{ + TL_RAISE_IF(i < 0 || i >= getSym().num(), + "Wrong index for FGSTensor::contractAndAdd"); + + TL_RAISE_IF(getSym()[i] != col.dimen() || tdims.getNVS()[i] != col.nvar(), + "Wrong dimensions for FGSTensor::contractAndAdd"); + + // set |sym_left| and |sym_right| to symmetries around |i| + /* Here we have a symmetry of |this| tensor and we have to set + |sym_left| to the subsymmetry left from the |i|-th variable and + |sym_right| to the subsymmetry right from the |i|-th variable. So we + copy first all the symmetry and then put zeros to the left for + |sym_right| and to the right for |sym_left|. */ + Symmetry sym_left(getSym()); + Symmetry sym_right(getSym()); + for (int j = 0; j < getSym().num(); j++) + { + if (j <= i) + sym_right[j] = 0; + if (j >= i) + sym_left[j] = 0; + } + + int dleft = TensorDimens(sym_left, tdims.getNVS()).calcFoldMaxOffset(); + int dright = TensorDimens(sym_right, tdims.getNVS()).calcFoldMaxOffset(); + KronProdAll kp(3); + kp.setUnit(0, dleft); + kp.setMat(1, col); + kp.setUnit(2, dright); + FGSTensor tmp(out.nrows(), out.getDims()); + kp.mult(*this, tmp); + out.add(1.0, tmp); +} + +/* Here we go through folded tensor, and each index we convert to index + of the unfolded tensor and copy the data to the unfolded. Then we + unfold data within the unfolded tensor. */ + +UGSTensor::UGSTensor(const FGSTensor &ft) + : UTensor(along_col, ft.tdims.getNVX(), ft.nrows(), + ft.tdims.calcUnfoldMaxOffset(), ft.dimen()), + tdims(ft.tdims) +{ + for (index fi = ft.begin(); fi != ft.end(); ++fi) + { + index ui(this, fi.getCoor()); + copyColumn(ft, *fi, *ui); + } + unfoldData(); +} + +// |UGSTensor| slicing from |FSSparseTensor| +/* This makes a folded slice from the sparse tensor and unfolds it. */ +UGSTensor::UGSTensor(const FSSparseTensor &t, const IntSequence &ss, + const IntSequence &coor, const TensorDimens &td) + : UTensor(along_col, td.getNVX(), t.nrows(), + td.calcUnfoldMaxOffset(), td.dimen()), + tdims(td) +{ + if (ncols() == 0) + return; + + FGSTensor ft(t, ss, coor, td); + for (index fi = ft.begin(); fi != ft.end(); ++fi) + { + index ui(this, fi.getCoor()); + copyColumn(ft, *fi, *ui); + } + unfoldData(); +} + +// |UGSTensor| slicing from |UFSTensor| +/* This makes a folded slice from dense and unfolds it. */ +UGSTensor::UGSTensor(const UFSTensor &t, const IntSequence &ss, + const IntSequence &coor, const TensorDimens &td) + : UTensor(along_col, td.getNVX(), t.nrows(), + td.calcUnfoldMaxOffset(), td.dimen()), + tdims(td) +{ + FFSTensor folded(t); + FGSTensor ft(folded, ss, coor, td); + for (index fi = ft.begin(); fi != ft.end(); ++fi) + { + index ui(this, fi.getCoor()); + copyColumn(ft, *fi, *ui); + } + unfoldData(); +} + +// |UGSTensor| increment and decrement codes +/* Clear, just call |UTensor| static methods. */ +void +UGSTensor::increment(IntSequence &v) const +{ + TL_RAISE_IF(v.size() != dimen(), + "Wrong input/output vector size in UGSTensor::increment"); + + UTensor::increment(v, tdims.getNVX()); +} + +void +UGSTensor::decrement(IntSequence &v) const +{ + TL_RAISE_IF(v.size() != dimen(), + "Wrong input/output vector size in UGSTensor::decrement"); + + UTensor::decrement(v, tdims.getNVX()); +} + +/* Return a new instance of folded version. */ +FTensor & +UGSTensor::fold() const +{ + return *(new FGSTensor(*this)); +} + +/* Return an offset of a given index. */ +int +UGSTensor::getOffset(const IntSequence &v) const +{ + TL_RAISE_IF(v.size() != dimen(), + "Wrong input vector size in UGSTensor::getOffset"); + + return UTensor::getOffset(v, tdims.getNVX()); +} + +/* Unfold all data. We go through all the columns and for each we + obtain an index of the first equivalent, and copy the data. */ + +void +UGSTensor::unfoldData() +{ + for (index in = begin(); in != end(); ++in) + copyColumn(*(getFirstIndexOf(in)), *in); +} + +/* Here we return the first index which is equivalent in the symmetry + to the given index. It is a matter of sorting all the symmetry + partitions of the index. */ + +Tensor::index +UGSTensor::getFirstIndexOf(const index &in) const +{ + IntSequence v(in.getCoor()); + int last = 0; + for (int i = 0; i < tdims.getSym().num(); i++) + { + IntSequence vtmp(v, last, last+tdims.getSym()[i]); + vtmp.sort(); + last += tdims.getSym()[i]; + } + return index(this, v); +} + +/* Here is perfectly same code with the same semantics as in + |@<|FGSTensor::contractAndAdd| code@>|. */ + +void +UGSTensor::contractAndAdd(int i, UGSTensor &out, + const URSingleTensor &col) const +{ + TL_RAISE_IF(i < 0 || i >= getSym().num(), + "Wrong index for UGSTensor::contractAndAdd"); + TL_RAISE_IF(getSym()[i] != col.dimen() || tdims.getNVS()[i] != col.nvar(), + "Wrong dimensions for UGSTensor::contractAndAdd"); + + // set |sym_left| and |sym_right| to symmetries around |i| + /* Same code as in FGSTensor::contractAndAdd */ + Symmetry sym_left(getSym()); + Symmetry sym_right(getSym()); + for (int j = 0; j < getSym().num(); j++) + { + if (j <= i) + sym_right[j] = 0; + if (j >= i) + sym_left[j] = 0; + } + + int dleft = TensorDimens(sym_left, tdims.getNVS()).calcUnfoldMaxOffset(); + int dright = TensorDimens(sym_right, tdims.getNVS()).calcUnfoldMaxOffset(); + KronProdAll kp(3); + kp.setUnit(0, dleft); + kp.setMat(1, col); + kp.setUnit(2, dright); + UGSTensor tmp(out.nrows(), out.getDims()); + kp.mult(*this, tmp); + out.add(1.0, tmp); +} diff --git a/dynare++/tl/cc/gs_tensor.cweb b/dynare++/tl/cc/gs_tensor.cweb deleted file mode 100644 index 13e1a2a74..000000000 --- a/dynare++/tl/cc/gs_tensor.cweb +++ /dev/null @@ -1,501 +0,0 @@ -@q $Id: gs_tensor.cweb 425 2005-08-16 15:18:01Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@ Start of {\tt gs\_tensor.cpp} file. - -@c -#include "gs_tensor.h" -#include "sparse_tensor.h" -#include "tl_exception.h" -#include "kron_prod.h" - -@<|TensorDimens| constructor code@>; -@<|TensorDimens::calcUnfoldMaxOffset| code@>; -@<|TensorDimens::calcFoldMaxOffset| code@>; -@<|TensorDimens::calcFoldOffset| code@>; -@<|TensorDimens::decrement| code@>; -@<|FGSTensor| conversion from |UGSTensor|@>; -@<|FGSTensor| slicing from |FSSparseTensor|@>; -@<|FGSTensor| slicing from |FFSTensor|@>; -@<|FGSTensor| conversion from |GSSparseTensor|@>; -@<|FGSTensor::increment| code@>; -@<|FGSTensor::unfold| code@>; -@<|FGSTensor::contractAndAdd| code@>; -@<|UGSTensor| conversion from |FGSTensor|@>; -@<|UGSTensor| slicing from |FSSparseTensor|@>; -@<|UGSTensor| slicing from |UFSTensor|@>; -@<|UGSTensor| increment and decrement codes@>; -@<|UGSTensor::fold| code@>; -@<|UGSTensor::getOffset| code@>; -@<|UGSTensor::unfoldData| code@>; -@<|UGSTensor::getFirstIndexOf| code@>; -@<|UGSTensor::contractAndAdd| code@>; - -@ This constructs the tensor dimensions for slicing. See -|@<|TensorDimens| class declaration@>| for details. -@<|TensorDimens| constructor code@>= -TensorDimens::TensorDimens(const IntSequence& ss, const IntSequence& coor) - : nvs(ss), - sym(ss.size(), ""), - nvmax(coor.size(), 0) -{ - TL_RAISE_IF(! coor.isSorted(), - "Coordinates not sorted in TensorDimens slicing constructor"); - TL_RAISE_IF(coor[0] < 0 || coor[coor.size()-1] >= ss.size(), - "A coordinate out of stack range in TensorDimens slicing constructor"); - - for (int i = 0; i < coor.size(); i++) { - sym[coor[i]]++; - nvmax[i] = ss[coor[i]]; - } -} - - -@ Number of unfold offsets is a product of all members of |nvmax|. -@<|TensorDimens::calcUnfoldMaxOffset| code@>= -int TensorDimens::calcUnfoldMaxOffset() const -{ - return nvmax.mult(); -} - -@ Number of folded offsets is a product of all unfold offsets within -each equivalence class of the symmetry. - -@<|TensorDimens::calcFoldMaxOffset| code@>= -int TensorDimens::calcFoldMaxOffset() const -{ - int res = 1; - for (int i = 0; i < nvs.size(); i++) { - if (nvs[i] == 0 && sym[i] > 0) - return 0; - if (sym[i] > 0) - res *= Tensor::noverk(nvs[i]+sym[i]-1, sym[i]); - } - return res; -} - -@ Here we implement offset calculation for folded general symmetry -tensor. The offset of a given sequence is calculated by breaking the -sequence to subsequences according to the symmetry. The offset is -orthogonal with respect to the blocks, this means that indexing within -the blocks is independent. If there are two blocks, for instance, then -the offset will be an offset within the outer block (the first) -multiplied with all offsets of the inner block (last) plus an offset -within the second block. - -Generally, the resulting offset $r$ will be -$$\sum_{i=1}^s r_i\cdot\left(\prod_{j=i+1}^sn_j\right),$$ -where $s$ is a number of blocks (|getSym().num()|), $r_i$ is an offset -within $i$-th block, and $n_j$ is a number of all offsets in $j$-th -block. - -In the code, we go from the innermost to the outermost, maintaining the -product in |pow|. - -@<|TensorDimens::calcFoldOffset| code@>= -int TensorDimens::calcFoldOffset(const IntSequence& v) const -{ - TL_RAISE_IF(v.size() != dimen(), - "Wrong input vector size in TensorDimens::getFoldOffset"); - - int res = 0; - int pow = 1; - int blstart = v.size(); - for (int ibl = getSym().num()-1; ibl >= 0; ibl--) { - int bldim = getSym()[ibl]; - if (bldim > 0) { - blstart -= bldim; - int blnvar = getNVX()[blstart]; - IntSequence subv(v, blstart, blstart+bldim); - res += FTensor::getOffset(subv, blnvar)*pow; - pow *= FFSTensor::calcMaxOffset(blnvar, bldim); - } - } - TL_RAISE_IF(blstart != 0, - "Error in tracing symmetry in TensorDimens::getFoldOffset"); - return res; -} - -@ In order to find the predecessor of index within folded generally -symmetric tensor, note, that a decrease action in $i$-th partition of -symmetric indices can happen only if all indices in all subsequent -partitions are zero. Then the decrease action of whole the index -consists of decrease action of the first nonzero partition from the -right, and setting these trailing zero partitions to their maximum -indices. - -So we set |iblock| to the number of last partitions. During the -execution, |block_first|, and |block_last| will point to the first -element of |iblock| and, first element of following block. - -Then we check for all trailing zero partitions, set them to their -maximums and return |iblock| to point to the first non-zero partition -(or the first partition). Then for this partition, we decrease the -index (fully symmetric within that partition). - -@<|TensorDimens::decrement| code@>= -void TensorDimens::decrement(IntSequence& v) const -{ - TL_RAISE_IF(getNVX().size() != v.size(), - "Wrong size of input/output sequence in TensorDimens::decrement"); - - int iblock = getSym().num()-1; - int block_last = v.size(); - int block_first = block_last-getSym()[iblock]; - @; - @; -} - -@ -@= - while (iblock > 0 && v[block_last-1] == 0) { - for (int i = block_first; i < block_last; i++) - v[i] = getNVX(i); // equivalent to |nvs[iblock]| - iblock--; - block_last = block_first; - block_first -= getSym()[iblock]; - } - -@ -@= - IntSequence vtmp(v, block_first, block_last); - FTensor::decrement(vtmp, getNVX(block_first)); - - - -@ Here we go through columns of folded, calculate column of unfolded, -and copy data. - -@<|FGSTensor| conversion from |UGSTensor|@>= -FGSTensor::FGSTensor(const UGSTensor& ut) - : FTensor(along_col, ut.tdims.getNVX(), ut.nrows(), - ut.tdims.calcFoldMaxOffset(), ut.dimen()), - tdims(ut.tdims) -{ - for (index ti = begin(); ti != end(); ++ti) { - index ui(&ut, ti.getCoor()); - copyColumn(ut, *ui, *ti); - } -} - -@ Here is the code of slicing constructor from the sparse tensor. We -first calculate coordinates of first and last index of the slice -within the sparse tensor (these are |lb| and |ub|), and then we -iterate through all items between them (in lexicographical ordering of -sparse tensor), and check whether an item is between the |lb| and |ub| -in Cartesian ordering (this corresponds to belonging to the -slices). If it belongs, then we subtract the lower bound |lb| to -obtain coordinates in the |this| tensor and we copy the item. - -@<|FGSTensor| slicing from |FSSparseTensor|@>= -FGSTensor::FGSTensor(const FSSparseTensor& t, const IntSequence& ss, - const IntSequence& coor, const TensorDimens& td) - : FTensor(along_col, td.getNVX(), t.nrows(), - td.calcFoldMaxOffset(), td.dimen()), - tdims(td) -{ - @; - - zeros(); - FSSparseTensor::const_iterator lbi = t.getMap().lower_bound(lb); - FSSparseTensor::const_iterator ubi = t.getMap().upper_bound(ub); - for (FSSparseTensor::const_iterator run = lbi; run != ubi; ++run) { - if (lb.lessEq((*run).first) && (*run).first.lessEq(ub)) { - IntSequence c((*run).first); - c.add(-1, lb); - Tensor::index ind(this, c); - TL_RAISE_IF(*ind < 0 || *ind >= ncols(), - "Internal error in slicing constructor of FGSTensor"); - get((*run).second.first, *ind) = (*run).second.second; - } - } -} - -@ Here we first set |s_offsets| to offsets of partitions whose lengths -are given by |ss|. So |s_offsets| is a cumulative sum of |ss|. - -Then we create |lb| to be coordinates of the possibly first index from -the slice, and |ub| to be coordinates of possibly last index of the -slice. - -@= - IntSequence s_offsets(ss.size(), 0); - for (int i = 1; i < ss.size(); i++) - s_offsets[i] = s_offsets[i-1] + ss[i-1]; - - IntSequence lb(coor.size()); - IntSequence ub(coor.size()); - for (int i = 0; i < coor.size(); i++) { - lb[i] = s_offsets[coor[i]]; - ub[i] = s_offsets[coor[i]] + ss[coor[i]] - 1; - } - - -@ The code is similar to |@<|FGSTensor| slicing from |FSSparseTensor|@>|. -@<|FGSTensor| slicing from |FFSTensor|@>= -FGSTensor::FGSTensor(const FFSTensor& t, const IntSequence& ss, - const IntSequence& coor, const TensorDimens& td) - : FTensor(along_col, td.getNVX(), t.nrows(), - td.calcFoldMaxOffset(), td.dimen()), - tdims(td) -{ - if (ncols() == 0) - return; - - @; - - zeros(); - Tensor::index lbi(&t, lb); - Tensor::index ubi(&t, ub); - ++ubi; - for (Tensor::index run = lbi; run != ubi; ++run) { - if (lb.lessEq(run.getCoor()) && run.getCoor().lessEq(ub)) { - IntSequence c(run.getCoor()); - c.add(-1, lb); - Tensor::index ind(this, c); - TL_RAISE_IF(*ind < 0 || *ind >= ncols(), - "Internal error in slicing constructor of FGSTensor"); - copyColumn(t, *run, *ind); - } - } -} - -@ -@<|FGSTensor| conversion from |GSSparseTensor|@>= -FGSTensor::FGSTensor(const GSSparseTensor& t) - : FTensor(along_col, t.getDims().getNVX(), t.nrows(), - t.getDims().calcFoldMaxOffset(), t.dimen()), tdims(t.getDims()) -{ - zeros(); - for (FSSparseTensor::const_iterator it = t.getMap().begin(); - it != t.getMap().end(); ++it) { - index ind(this, (*it).first); - get((*it).second.first, *ind) = (*it).second.second; - } -} - -@ First we increment as unfolded, then we must monotonize within -partitions defined by the symmetry. This is done by -|IntSequence::pmonotone|. - -@<|FGSTensor::increment| code@>= -void FGSTensor::increment(IntSequence& v) const -{ - TL_RAISE_IF(v.size() != dimen(), - "Wrong input/output vector size in FGSTensor::increment"); - - UTensor::increment(v, tdims.getNVX()); - v.pmonotone(tdims.getSym()); -} - - - - -@ Return unfolded version of the tensor. -@<|FGSTensor::unfold| code@>= -UTensor& FGSTensor::unfold() const -{ - return *(new UGSTensor(*this)); -} - - -@ Here we implement the contraction -$$\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} -$$ -More generally, $x^i$ and $z^k$ can represent also general symmetries. - -The operation can be rewritten as a matrix product -$$\left[t_{x^iy^jz^k}\right]\cdot\left(I_l\otimes c\otimes I_r\right)$$ -where $l$ is a number of columns in tensor with symmetry on the left -(i.e. $x^i$), and $r$ is a number of columns in tensor with a symmetry -on the right (i.e. $z^k$). The code proceeds accordingly. We first -form two symmetries |sym_left| and |sym_right|, then calculate the -number of columns |dleft|$=l$ and |dright|$=r$, form the Kronecker -product and multiply and add. - -The input parameter |i| is the order of a variable being contracted -starting from 0. - -@<|FGSTensor::contractAndAdd| code@>= -void FGSTensor::contractAndAdd(int i, FGSTensor& out, - const FRSingleTensor& col) const -{ - TL_RAISE_IF(i < 0 || i >= getSym().num(), - "Wrong index for FGSTensor::contractAndAdd"); - - TL_RAISE_IF(getSym()[i] != col.dimen() || tdims.getNVS()[i] != col.nvar(), - "Wrong dimensions for FGSTensor::contractAndAdd"); - - @; - int dleft = TensorDimens(sym_left, tdims.getNVS()).calcFoldMaxOffset(); - int dright = TensorDimens(sym_right, tdims.getNVS()).calcFoldMaxOffset(); - KronProdAll kp(3); - kp.setUnit(0, dleft); - kp.setMat(1, col); - kp.setUnit(2, dright); - FGSTensor tmp(out.nrows(), out.getDims()); - kp.mult(*this, tmp); - out.add(1.0, tmp); -} - -@ Here we have a symmetry of |this| tensor and we have to set -|sym_left| to the subsymmetry left from the |i|-th variable and -|sym_right| to the subsymmetry right from the |i|-th variable. So we -copy first all the symmetry and then put zeros to the left for -|sym_right| and to the right for |sym_left|. - -@= - Symmetry sym_left(getSym()); - Symmetry sym_right(getSym()); - for (int j = 0; j < getSym().num(); j++) { - if (j <= i) - sym_right[j] = 0; - if (j >= i) - sym_left[j] = 0; - } - - -@ Here we go through folded tensor, and each index we convert to index -of the unfolded tensor and copy the data to the unfolded. Then we -unfold data within the unfolded tensor. - -@<|UGSTensor| conversion from |FGSTensor|@>= -UGSTensor::UGSTensor(const FGSTensor& ft) - : UTensor(along_col, ft.tdims.getNVX(), ft.nrows(), - ft.tdims.calcUnfoldMaxOffset(), ft.dimen()), - tdims(ft.tdims) -{ - for (index fi = ft.begin(); fi != ft.end(); ++fi) { - index ui(this, fi.getCoor()); - copyColumn(ft, *fi, *ui); - } - unfoldData(); -} - -@ This makes a folded slice from the sparse tensor and unfolds it. -@<|UGSTensor| slicing from |FSSparseTensor|@>= -UGSTensor::UGSTensor(const FSSparseTensor& t, const IntSequence& ss, - const IntSequence& coor, const TensorDimens& td) - : UTensor(along_col, td.getNVX(), t.nrows(), - td.calcUnfoldMaxOffset(), td.dimen()), - tdims(td) -{ - if (ncols() == 0) - return; - - FGSTensor ft(t, ss, coor, td); - for (index fi = ft.begin(); fi != ft.end(); ++fi) { - index ui(this, fi.getCoor()); - copyColumn(ft, *fi, *ui); - } - unfoldData(); -} - -@ This makes a folded slice from dense and unfolds it. -@<|UGSTensor| slicing from |UFSTensor|@>= -UGSTensor::UGSTensor(const UFSTensor& t, const IntSequence& ss, - const IntSequence& coor, const TensorDimens& td) - : UTensor(along_col, td.getNVX(), t.nrows(), - td.calcUnfoldMaxOffset(), td.dimen()), - tdims(td) -{ - FFSTensor folded(t); - FGSTensor ft(folded, ss, coor, td); - for (index fi = ft.begin(); fi != ft.end(); ++fi) { - index ui(this, fi.getCoor()); - copyColumn(ft, *fi, *ui); - } - unfoldData(); -} - - -@ Clear, just call |UTensor| static methods. -@<|UGSTensor| increment and decrement codes@>= -void UGSTensor::increment(IntSequence& v) const -{ - TL_RAISE_IF(v.size() != dimen(), - "Wrong input/output vector size in UGSTensor::increment"); - - UTensor::increment(v, tdims.getNVX()); -} - -void UGSTensor::decrement(IntSequence& v) const -{ - TL_RAISE_IF(v.size() != dimen(), - "Wrong input/output vector size in UGSTensor::decrement"); - - UTensor::decrement(v, tdims.getNVX()); -} - - -@ Return a new instance of folded version. -@<|UGSTensor::fold| code@>= -FTensor& UGSTensor::fold() const -{ - return *(new FGSTensor(*this)); -} - -@ Return an offset of a given index. -@<|UGSTensor::getOffset| code@>= -int UGSTensor::getOffset(const IntSequence& v) const -{ - TL_RAISE_IF(v.size() != dimen(), - "Wrong input vector size in UGSTensor::getOffset"); - - return UTensor::getOffset(v, tdims.getNVX()); -} - -@ Unfold all data. We go through all the columns and for each we -obtain an index of the first equivalent, and copy the data. - -@<|UGSTensor::unfoldData| code@>= -void UGSTensor::unfoldData() -{ - for (index in = begin(); in != end(); ++in) - copyColumn(*(getFirstIndexOf(in)), *in); -} - -@ Here we return the first index which is equivalent in the symmetry -to the given index. It is a matter of sorting all the symmetry -partitions of the index. - -@<|UGSTensor::getFirstIndexOf| code@>= -Tensor::index UGSTensor::getFirstIndexOf(const index& in) const -{ - IntSequence v(in.getCoor()); - int last = 0; - for (int i = 0; i < tdims.getSym().num(); i++) { - IntSequence vtmp(v, last, last+tdims.getSym()[i]); - vtmp.sort(); - last += tdims.getSym()[i]; - } - return index(this, v); -} - -@ Here is perfectly same code with the same semantics as in -|@<|FGSTensor::contractAndAdd| code@>|. - -@<|UGSTensor::contractAndAdd| code@>= -void UGSTensor::contractAndAdd(int i, UGSTensor& out, - const URSingleTensor& col) const -{ - TL_RAISE_IF(i < 0 || i >= getSym().num(), - "Wrong index for UGSTensor::contractAndAdd"); - TL_RAISE_IF(getSym()[i] != col.dimen() || tdims.getNVS()[i] != col.nvar(), - "Wrong dimensions for UGSTensor::contractAndAdd"); - - @; - int dleft = TensorDimens(sym_left, tdims.getNVS()).calcUnfoldMaxOffset(); - int dright = TensorDimens(sym_right, tdims.getNVS()).calcUnfoldMaxOffset(); - KronProdAll kp(3); - kp.setUnit(0, dleft); - kp.setMat(1, col); - kp.setUnit(2, dright); - UGSTensor tmp(out.nrows(), out.getDims()); - kp.mult(*this, tmp); - out.add(1.0, tmp); -} - -@ End of {\tt gs\_tensor.cpp} file. diff --git a/dynare++/tl/cc/gs_tensor.hh b/dynare++/tl/cc/gs_tensor.hh new file mode 100644 index 000000000..422b566bb --- /dev/null +++ b/dynare++/tl/cc/gs_tensor.hh @@ -0,0 +1,274 @@ +// Copyright 2004, Ondra Kamenik + +// General symmetry tensor. + +/* 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. */ + +#ifndef GS_TENSOR_H +#define GS_TENSOR_H + +#include "tensor.hh" +#include "fs_tensor.hh" +#include "symmetry.hh" +#include "rfs_tensor.hh" + +class FGSTensor; +class UGSTensor; +class FSSparseTensor; + +/* 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. */ + +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. */ + +class GSSparseTensor; +class FGSTensor : public FTensor +{ + friend class UGSTensor; + + const TensorDimens tdims; +public: + /* 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(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()) + { + } + + 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); + } +}; + +/* 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). */ + +class UGSTensor : public UTensor +{ + friend class FGSTensor; + + const TensorDimens tdims; +public: + /* 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(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()) + { + } + 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; +}; + +#endif diff --git a/dynare++/tl/cc/gs_tensor.hweb b/dynare++/tl/cc/gs_tensor.hweb deleted file mode 100644 index cdc6734f9..000000000 --- a/dynare++/tl/cc/gs_tensor.hweb +++ /dev/null @@ -1,222 +0,0 @@ -@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. diff --git a/dynare++/tl/cc/int_sequence.cc b/dynare++/tl/cc/int_sequence.cc new file mode 100644 index 000000000..70f98e6e7 --- /dev/null +++ b/dynare++/tl/cc/int_sequence.cc @@ -0,0 +1,312 @@ +// Copyright 2004, Ondra Kamenik + +#include "int_sequence.hh" +#include "symmetry.hh" +#include "tl_exception.hh" + +#include +#include + +/* This unfolds a given integer sequence with respect to the given + symmetry. If for example the symmetry is $(2,3)$, and the sequence is + $(a,b)$, then the result is $(a,a,b,b,b)$. */ + +IntSequence::IntSequence(const Symmetry &sy, const IntSequence &se) + : data(new int[sy.dimen()]), length(sy.dimen()), destroy(true) +{ + int k = 0; + for (int i = 0; i < sy.num(); i++) + for (int j = 0; j < sy[i]; j++, k++) + operator[](k) = se[i]; +} + +/* This constructs an implied symmetry (implemented as |IntSequence| + from a more general symmetry and equivalence class (implemented as + |vector|). For example, let the general symmetry be $y^3u^2$ and + the equivalence class is $\{0,4\}$ picking up first and fifth + variable, we calculate symmetry (at this point only |IntSequence|) + corresponding to the picked variables. These are $yu$. Thus the + constructed sequence must be $(1,1)$, meaning that we picked one $y$ + and one $u$. */ + +IntSequence::IntSequence(const Symmetry &sy, const vector &se) + : data(new int[sy.num()]), length(sy.num()), destroy(true) +{ + TL_RAISE_IF(sy.dimen() <= se[se.size()-1], + "Sequence is not reachable by symmetry in IntSequence()"); + for (int i = 0; i < length; i++) + operator[](i) = 0; + + for (unsigned int i = 0; i < se.size(); i++) + operator[](sy.findClass(se[i]))++; +} + +/* This constructs an ordered integer sequence from the given ordered + sequence inserting the given number to the sequence. */ + +IntSequence::IntSequence(int i, const IntSequence &s) + : data(new int[s.size()+1]), length(s.size()+1), destroy(true) +{ + int j = 0; + while (j < s.size() && s[j] < i) + j++; + for (int jj = 0; jj < j; jj++) + operator[](jj) = s[jj]; + operator[](j) = i; + for (int jj = j; jj < s.size(); jj++) + operator[](jj+1) = s[jj]; +} + +IntSequence::IntSequence(int i, const IntSequence &s, int pos) + : data(new int[s.size()+1]), length(s.size()+1), destroy(true) +{ + TL_RAISE_IF(pos < 0 || pos > s.size(), + "Wrong position for insertion IntSequence constructor"); + for (int jj = 0; jj < pos; jj++) + operator[](jj) = s[jj]; + operator[](pos) = i; + for (int jj = pos; jj < s.size(); jj++) + operator[](jj+1) = s[jj]; +} + +const IntSequence & +IntSequence::operator=(const IntSequence &s) +{ + TL_RAISE_IF(!destroy && length != s.length, + "Wrong length for in-place IntSequence::operator="); + if (destroy && length != s.length) + { + delete [] data; + data = new int[s.length]; + destroy = true; + length = s.length; + } + memcpy(data, s.data, sizeof(int)*length); + return *this; +} + +bool +IntSequence::operator==(const IntSequence &s) const +{ + if (size() != s.size()) + return false; + + int i = 0; + while (i < size() && operator[](i) == s[i]) + i++; + return i == size(); +} + +/* We need some linear irreflexive ordering, we implement it as + lexicographic ordering without identity. */ +bool +IntSequence::operator<(const IntSequence &s) const +{ + int len = min(size(), s.size()); + + int i = 0; + while (i < len && operator[](i) == s[i]) + i++; + return (i < s.size() && (i == size() || operator[](i) < s[i])); +} + +bool +IntSequence::lessEq(const IntSequence &s) const +{ + TL_RAISE_IF(size() != s.size(), + "Sequence with different lengths in IntSequence::lessEq"); + + int i = 0; + while (i < size() && operator[](i) <= s[i]) + i++; + return (i == size()); +} + +bool +IntSequence::less(const IntSequence &s) const +{ + TL_RAISE_IF(size() != s.size(), + "Sequence with different lengths in IntSequence::less"); + + int i = 0; + while (i < size() && operator[](i) < s[i]) + i++; + return (i == size()); +} + +/* This is a bubble sort, all sequences are usually very short, so this + sin might be forgiven. */ + +void +IntSequence::sort() +{ + for (int i = 0; i < length; i++) + { + int swaps = 0; + for (int j = 0; j < length-1; j++) + { + if (data[j] > data[j+1]) + { + int s = data[j+1]; + data[j+1] = data[j]; + data[j] = s; + swaps++; + } + } + if (swaps == 0) + return; + } +} + +/* Here we monotonize the sequence. If an item is less then its + predecessor, it is equalized. */ + +void +IntSequence::monotone() +{ + for (int i = 1; i < length; i++) + if (data[i-1] > data[i]) + data[i] = data[i-1]; +} + +/* This partially monotones the sequence. The partitioning is done by a + symmetry. So the subsequence given by the symmetry classes are + monotonized. For example, if the symmetry is $y^2u^3$, and the + |IntSequence| is $(5,3,1,6,4)$, the result is $(5,5,1,6,6)$. */ + +void +IntSequence::pmonotone(const Symmetry &s) +{ + int cum = 0; + for (int i = 0; i < s.num(); i++) + { + for (int j = cum + 1; j < cum + s[i]; j++) + if (data[j-1] > data[j]) + data[j] = data[j-1]; + cum += s[i]; + } +} + +/* This returns sum of all elements. Useful for symmetries. */ + +int +IntSequence::sum() const +{ + int res = 0; + for (int i = 0; i < length; i++) + res += operator[](i); + return res; +} + +/* This returns product of subsequent items. Useful for Kronecker product + dimensions. */ + +int +IntSequence::mult(int i1, int i2) const +{ + int res = 1; + for (int i = i1; i < i2; i++) + res *= operator[](i); + return res; +} + +/* Return a number of the same items in the beginning of the sequence. */ + +int +IntSequence::getPrefixLength() const +{ + int i = 0; + while (i+1 < size() && operator[](i+1) == operator[](0)) + i++; + return i+1; +} + +/* This returns a number of distinct items in the sequence. It supposes + that the sequence is ordered. For the empty sequence it returns zero. */ + +int +IntSequence::getNumDistinct() const +{ + int res = 0; + if (size() > 0) + res++; + for (int i = 1; i < size(); i++) + if (operator[](i) != operator[](i-1)) + res++; + return res; +} + +/* This returns a maximum of the sequence. If the sequence is empty, it + returns the least possible |int| value. */ + +int +IntSequence::getMax() const +{ + int res = INT_MIN; + for (int i = 0; i < size(); i++) + if (operator[](i) > res) + res = operator[](i); + return res; +} + +void +IntSequence::add(int i) +{ + for (int j = 0; j < size(); j++) + operator[](j) += i; +} + +void +IntSequence::add(int f, const IntSequence &s) +{ + TL_RAISE_IF(size() != s.size(), + "Wrong sequence length in IntSequence::add"); + for (int j = 0; j < size(); j++) + operator[](j) += f*s[j]; +} + +bool +IntSequence::isPositive() const +{ + int i = 0; + while (i < size() && operator[](i) >= 0) + i++; + return (i == size()); +} + +bool +IntSequence::isConstant() const +{ + bool res = true; + int i = 1; + while (res && i < size()) + { + res = res && operator[](0) == operator[](i); + i++; + } + return res; +} + +bool +IntSequence::isSorted() const +{ + bool res = true; + int i = 1; + while (res && i < size()) + { + res = res && operator[](i-1) <= operator[](i); + i++; + } + return res; +} + +/* Debug print. */ + +void +IntSequence::print() const +{ + printf("["); + for (int i = 0; i < size(); i++) + printf("%2d ", operator[](i)); + printf("]\n"); +} diff --git a/dynare++/tl/cc/int_sequence.cweb b/dynare++/tl/cc/int_sequence.cweb deleted file mode 100644 index d476293d4..000000000 --- a/dynare++/tl/cc/int_sequence.cweb +++ /dev/null @@ -1,351 +0,0 @@ -@q $Id: int_sequence.cweb 148 2005-04-19 15:12:26Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@ Start of {\tt int\_sequence.cpp} file. - -@c -#include "int_sequence.h" -#include "symmetry.h" -#include "tl_exception.h" - -#include -#include - -@<|IntSequence| constructor code 1@>; -@<|IntSequence| constructor code 2@>; -@<|IntSequence| constructor code 3@>; -@<|IntSequence| constructor code 4@>; -@<|IntSequence::operator=| code@>; -@<|IntSequence::operator==| code@>; -@<|IntSequence::operator<| code@>; -@<|IntSequence::lessEq| code@>; -@<|IntSequence::less| code@>; -@<|IntSequence::sort| code@>; -@<|IntSequence::monotone| code@>; -@<|IntSequence::pmonotone| code@>; -@<|IntSequence::sum| code@>; -@<|IntSequence::mult| code@>; -@<|IntSequence::getPrefixLength| code@>; -@<|IntSequence::getNumDistinct| code@>; -@<|IntSequence::getMax| code@>; -@<|IntSequence::add| code 1@>; -@<|IntSequence::add| code 2@>; -@<|IntSequence::isPositive| code@>; -@<|IntSequence::isConstant| code@>; -@<|IntSequence::isSorted| code@>; -@<|IntSequence::print| code@>; - -@ This unfolds a given integer sequence with respect to the given -symmetry. If for example the symmetry is $(2,3)$, and the sequence is -$(a,b)$, then the result is $(a,a,b,b,b)$. - -@<|IntSequence| constructor code 1@>= -IntSequence::IntSequence(const Symmetry& sy, const IntSequence& se) - : data(new int[sy.dimen()]), length(sy.dimen()), destroy(true) -{ - int k = 0; - for (int i = 0; i < sy.num(); i++) - for (int j = 0; j < sy[i]; j++, k++) - operator[](k) = se[i]; -} - - -@ This constructs an implied symmetry (implemented as |IntSequence| -from a more general symmetry and equivalence class (implemented as -|vector|). For example, let the general symmetry be $y^3u^2$ and -the equivalence class is $\{0,4\}$ picking up first and fifth -variable, we calculate symmetry (at this point only |IntSequence|) -corresponding to the picked variables. These are $yu$. Thus the -constructed sequence must be $(1,1)$, meaning that we picked one $y$ -and one $u$. - - -@<|IntSequence| constructor code 2@>= -IntSequence::IntSequence(const Symmetry& sy, const vector& se) - : data(new int[sy.num()]), length(sy.num()), destroy(true) -{ - TL_RAISE_IF(sy.dimen() <= se[se.size()-1], - "Sequence is not reachable by symmetry in IntSequence()"); - for (int i = 0; i < length; i++) @/ - operator[](i) = 0; - - for (unsigned int i = 0; i < se.size(); i++) @/ - operator[](sy.findClass(se[i]))++; -} - -@ This constructs an ordered integer sequence from the given ordered -sequence inserting the given number to the sequence. - -@<|IntSequence| constructor code 3@>= -IntSequence::IntSequence(int i, const IntSequence& s) - : data(new int[s.size()+1]), length(s.size()+1), destroy(true) -{ - int j = 0; - while (j < s.size() && s[j] < i) - j++; - for (int jj = 0; jj < j; jj++) - operator[](jj) = s[jj]; - operator[](j) = i; - for (int jj = j; jj < s.size(); jj++) - operator[](jj+1) = s[jj]; -} - -@ -@<|IntSequence| constructor code 4@>= -IntSequence::IntSequence(int i, const IntSequence& s, int pos) - : data(new int[s.size()+1]), length(s.size()+1), destroy(true) -{ - TL_RAISE_IF(pos < 0 || pos > s.size(), - "Wrong position for insertion IntSequence constructor"); - for (int jj = 0; jj < pos; jj++) - operator[](jj) = s[jj]; - operator[](pos) = i; - for (int jj = pos; jj < s.size(); jj++) - operator[](jj+1) = s[jj]; -} - -@ -@<|IntSequence::operator=| code@>= -const IntSequence& IntSequence::operator=(const IntSequence& s) - { - TL_RAISE_IF(!destroy && length != s.length, - "Wrong length for in-place IntSequence::operator="); - if (destroy && length != s.length) { - delete [] data; - data = new int[s.length]; - destroy = true; - length = s.length; - } - memcpy(data, s.data, sizeof(int)*length); - return *this; - } - - -@ -@<|IntSequence::operator==| code@>= -bool IntSequence::operator==(const IntSequence& s) const -{ - if (size() != s.size()) - return false; - - int i = 0; - while (i < size() && operator[](i) == s[i]) - i++; - return i == size(); -} - -@ We need some linear irreflexive ordering, we implement it as -lexicographic ordering without identity. -@<|IntSequence::operator<| code@>= -bool IntSequence::operator<(const IntSequence& s) const -{ - int len = min(size(), s.size()); - - int i = 0; - while (i < len && operator[](i) == s[i]) - i++; - return (i < s.size() && (i == size() || operator[](i) < s[i])); -} - -@ -@<|IntSequence::lessEq| code@>= -bool IntSequence::lessEq(const IntSequence& s) const -{ - TL_RAISE_IF(size() != s.size(), - "Sequence with different lengths in IntSequence::lessEq"); - - int i = 0; - while (i < size() && operator[](i) <= s[i]) - i++; - return (i == size()); -} - -@ -@<|IntSequence::less| code@>= -bool IntSequence::less(const IntSequence& s) const -{ - TL_RAISE_IF(size() != s.size(), - "Sequence with different lengths in IntSequence::less"); - - int i = 0; - while (i < size() && operator[](i) < s[i]) - i++; - return (i == size()); -} - -@ This is a bubble sort, all sequences are usually very short, so this -sin might be forgiven. - -@<|IntSequence::sort| code@>= -void IntSequence::sort() -{ - for (int i = 0; i < length; i++) { - int swaps = 0; - for (int j = 0; j < length-1; j++) { - if (data[j] > data[j+1]) { - int s = data[j+1]; - data[j+1] = data[j]; - data[j] = s; - swaps++; - } - } - if (swaps == 0) - return; - } -} - -@ Here we monotonize the sequence. If an item is less then its -predecessor, it is equalized. - -@<|IntSequence::monotone| code@>= -void IntSequence::monotone() -{ - for (int i = 1; i < length; i++) - if (data[i-1] > data[i])@/ - data[i] = data[i-1]; -} - -@ This partially monotones the sequence. The partitioning is done by a -symmetry. So the subsequence given by the symmetry classes are -monotonized. For example, if the symmetry is $y^2u^3$, and the -|IntSequence| is $(5,3,1,6,4)$, the result is $(5,5,1,6,6)$. - -@<|IntSequence::pmonotone| code@>= -void IntSequence::pmonotone(const Symmetry& s) -{ - int cum = 0; - for (int i = 0; i < s.num(); i++) { - for (int j = cum + 1; j < cum + s[i]; j++) - if (data[j-1] > data[j])@/ - data[j] = data[j-1]; - cum += s[i]; - } -} - -@ This returns sum of all elements. Useful for symmetries. -@<|IntSequence::sum| code@>= -int IntSequence::sum() const -{ - int res = 0; - for (int i = 0; i < length; i++) @/ - res += operator[](i); - return res; -} - -@ This returns product of subsequent items. Useful for Kronecker product -dimensions. - -@<|IntSequence::mult| code@>= -int IntSequence::mult(int i1, int i2) const -{ - int res = 1; - for (int i = i1; i < i2; i++)@/ - res *= operator[](i); - return res; -} - -@ Return a number of the same items in the beginning of the sequence. -@<|IntSequence::getPrefixLength| code@>= -int IntSequence::getPrefixLength() const -{ - int i = 0; - while (i+1 < size() && operator[](i+1) == operator[](0)) - i++; - return i+1; -} - -@ This returns a number of distinct items in the sequence. It supposes -that the sequence is ordered. For the empty sequence it returns zero. - -@<|IntSequence::getNumDistinct| code@>= -int IntSequence::getNumDistinct() const -{ - int res = 0; - if (size() > 0) - res++; - for (int i = 1; i < size(); i++) - if (operator[](i) != operator[](i-1)) - res++; - return res; -} - -@ This returns a maximum of the sequence. If the sequence is empty, it -returns the least possible |int| value. - -@<|IntSequence::getMax| code@>= -int IntSequence::getMax() const -{ - int res = INT_MIN; - for (int i = 0; i < size(); i++) - if (operator[](i) > res) - res = operator[](i); - return res; -} - -@ -@<|IntSequence::add| code 1@>= -void IntSequence::add(int i) -{ - for (int j = 0; j < size(); j++) - operator[](j) += i; -} - -@ -@<|IntSequence::add| code 2@>= -void IntSequence::add(int f, const IntSequence& s) -{ - TL_RAISE_IF(size() != s.size(), - "Wrong sequence length in IntSequence::add"); - for (int j = 0; j < size(); j++) - operator[](j) += f*s[j]; -} - -@ -@<|IntSequence::isPositive| code@>= -bool IntSequence::isPositive() const -{ - int i = 0; - while (i < size() && operator[](i) >= 0) - i++; - return (i == size()); -} - -@ -@<|IntSequence::isConstant| code@>= -bool IntSequence::isConstant() const -{ - bool res = true; - int i = 1; - while (res && i < size()) { - res = res && operator[](0) == operator[](i); - i++; - } - return res; -} - -@ -@<|IntSequence::isSorted| code@>= -bool IntSequence::isSorted() const -{ - bool res = true; - int i = 1; - while (res && i < size()) { - res = res && operator[](i-1) <= operator[](i); - i++; - } - return res; -} - - - -@ Debug print. -@<|IntSequence::print| code@>= -void IntSequence::print() const -{ - printf("["); - for (int i = 0; i < size(); i++)@/ - printf("%2d ",operator[](i)); - printf("]\n"); -} - -@ End of {\tt int\_sequence.cpp} file. \ No newline at end of file diff --git a/dynare++/tl/cc/int_sequence.hh b/dynare++/tl/cc/int_sequence.hh new file mode 100644 index 000000000..dd52ddbf8 --- /dev/null +++ b/dynare++/tl/cc/int_sequence.hh @@ -0,0 +1,148 @@ +// Copyright 2004, Ondra Kamenik + +// Integer sequence. + +/* Here we define an auxiliary abstraction for a sequence of integers. The + basic functionality is to hold an ordered sequence of integers with + constant length. We prefer using this simple class before STL + |vector| since it is more efficient for our purposes. + + The class is used in index of a tensor, in symmetry definition, in + Kronecker product dimensions, or as a class of an equivalence. The + latter case is not ordered, but we always order equivalence classes in + order to ensure unique representativeness. For almost all cases we + need the integer sequence to be ordered (sort), or monotonize (indices + of folded tensors), or partially monotonize (indices of folded tensors + not fully symmetric), or calculate a product of all members or only of + a part (used in Kronecker product dimensions). When we calculate + offsets in folded tensors, we need to obtain a number of the same + items in the front (|getPrefixLength|), and also to add some integer + number to all items. + + Also, we need to construct a subsequence of a sequence, so + some instances do destroy the underlying data, and some not. */ + +#ifndef INT_SEQUENCE_H +#define INT_SEQUENCE_H + +#include +#include + +using namespace std; + +/* The implementation of |IntSequence| is straightforward. It has a + pointer |data|, a |length| of the data, and a flag |destroy|, whether + the instance must destroy the underlying data. */ + +class Symmetry; +class IntSequence +{ + int *data; + int length; + bool destroy; +public: + /* We have a constructor allocating a given length of data, constructor + allocating and then initializing all members to a given number, a copy + constructor, a conversion from |vector|, a subsequence + constructor, a constructor used for calculating implied symmetry from + a more general symmetry and one equivalence class (see |Symmetry| + class). Finally we have a constructor which unfolds a sequence with + respect to a given symmetry and constructor which inserts a given + number to the ordered sequence or given number to a given position. */ + + IntSequence(int l) + : data(new int[l]), length(l), destroy(true) + { + } + IntSequence(int l, int n) + : data(new int[l]), length(l), destroy(true) + { + for (int i = 0; i < length; i++) + data[i] = n; + } + IntSequence(const IntSequence &s) + : data(new int[s.length]), length(s.length), destroy(true) + { + memcpy(data, s.data, length*sizeof(int)); + } + IntSequence(IntSequence &s, int i1, int i2) + : data(s.data+i1), length(i2-i1), destroy(false) + { + } + IntSequence(const IntSequence &s, int i1, int i2) + : data(new int[i2-i1]), length(i2-i1), destroy(true) + { + memcpy(data, s.data+i1, sizeof(int)*length); + } + IntSequence(const Symmetry &sy, const vector &se); + IntSequence(const Symmetry &sy, const IntSequence &se); + IntSequence(int i, const IntSequence &s); + IntSequence(int i, const IntSequence &s, int pos); + IntSequence(int l, const int *d) + : data(new int[l]), length(l), destroy(true) + { + memcpy(data, d, sizeof(int)*length); + } + + const IntSequence &operator=(const IntSequence &s); + virtual ~IntSequence() + { + if (destroy) + delete [] data; + } + bool operator==(const IntSequence &s) const; + bool + operator!=(const IntSequence &s) const + { + return !operator==(s); + } + int & + operator[](int i) + { + return data[i]; + } + int + operator[](int i) const + { + return data[i]; + } + int + size() const + { + return length; + } + + /* We provide two orderings. The first |operator<| is the linear + lexicographic ordering, the second |less| is the non-linear Cartesian + ordering. */ + bool operator<(const IntSequence &s) const; + bool + operator<=(const IntSequence &s) const + { + return (operator==(s) || operator<(s)); + } + bool lessEq(const IntSequence &s) const; + bool less(const IntSequence &s) const; + + void sort(); + void monotone(); + void pmonotone(const Symmetry &s); + int sum() const; + int mult(int i1, int i2) const; + int + mult() const + { + return mult(0, length); + } + void add(int i); + void add(int f, const IntSequence &s); + int getPrefixLength() const; + int getNumDistinct() const; + int getMax() const; + bool isPositive() const; + bool isConstant() const; + bool isSorted() const; + void print() const; +}; + +#endif diff --git a/dynare++/tl/cc/int_sequence.hweb b/dynare++/tl/cc/int_sequence.hweb deleted file mode 100644 index 8e3faf6e9..000000000 --- a/dynare++/tl/cc/int_sequence.hweb +++ /dev/null @@ -1,132 +0,0 @@ -@q $Id: int_sequence.hweb 758 2006-05-22 08:31:18Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@*2 Integer sequence. Start of {\tt int\_sequence.h} file. - -Here we define an auxiliary abstraction for a sequence of integers. The -basic functionality is to hold an ordered sequence of integers with -constant length. We prefer using this simple class before STL -|vector| since it is more efficient for our purposes. - -The class is used in index of a tensor, in symmetry definition, in -Kronecker product dimensions, or as a class of an equivalence. The -latter case is not ordered, but we always order equivalence classes in -order to ensure unique representativeness. For almost all cases we -need the integer sequence to be ordered (sort), or monotonize (indices -of folded tensors), or partially monotonize (indices of folded tensors -not fully symmetric), or calculate a product of all members or only of -a part (used in Kronecker product dimensions). When we calculate -offsets in folded tensors, we need to obtain a number of the same -items in the front (|getPrefixLength|), and also to add some integer -number to all items. - -Also, we need to construct a subsequence of a sequence, so -some instances do destroy the underlying data, and some not. - -@s IntSequence int -@s Symmetry int -@c -#ifndef INT_SEQUENCE_H -#define INT_SEQUENCE_H - - -#include -#include - -using namespace std; - -@<|IntSequence| class declaration@>; - -#endif - -@ The implementation of |IntSequence| is straightforward. It has a -pointer |data|, a |length| of the data, and a flag |destroy|, whether -the instance must destroy the underlying data. - -@<|IntSequence| class declaration@>= -class Symmetry; -class IntSequence { - int* data; - int length; - bool destroy; -public:@/ - @<|IntSequence| constructors@>; - @<|IntSequence| inlines and operators@>; - @<|IntSequence| orderings@>; - void sort(); - void monotone(); - void pmonotone(const Symmetry& s); - int sum() const; - int mult(int i1, int i2) const; - int mult() const - {@+return mult(0, length);@+} - void add(int i); - void add(int f, const IntSequence& s); - int getPrefixLength() const; - int getNumDistinct() const; - int getMax() const; - bool isPositive() const; - bool isConstant() const; - bool isSorted() const; - void print() const; -}; - -@ We have a constructor allocating a given length of data, constructor -allocating and then initializing all members to a given number, a copy -constructor, a conversion from |vector|, a subsequence -constructor, a constructor used for calculating implied symmetry from -a more general symmetry and one equivalence class (see |Symmetry| -class). Finally we have a constructor which unfolds a sequence with -respect to a given symmetry and constructor which inserts a given -number to the ordered sequence or given number to a given position. - -@<|IntSequence| constructors@>= - IntSequence(int l) - : data(new int[l]), length(l), destroy(true)@+ {} - IntSequence(int l, int n) - : data(new int[l]), length(l), destroy(true) - {@+ for (int i = 0; i < length; i++) data[i] = n;@+} - IntSequence(const IntSequence& s) - : data(new int[s.length]), length(s.length), destroy(true) - {@+ memcpy(data, s.data, length*sizeof(int));@+} - IntSequence(IntSequence& s, int i1, int i2) - : data(s.data+i1), length(i2-i1), destroy(false)@+ {} - IntSequence(const IntSequence& s, int i1, int i2) - : data(new int[i2-i1]), length(i2-i1), destroy(true) - {@+ memcpy(data, s.data+i1, sizeof(int)*length);@+} - IntSequence(const Symmetry& sy, const vector& se); - IntSequence(const Symmetry& sy, const IntSequence& se); - IntSequence(int i, const IntSequence& s); - IntSequence(int i, const IntSequence& s, int pos); - IntSequence(int l, const int* d) - : data(new int[l]), length(l), destroy(true) - {@+ memcpy(data, d, sizeof(int)*length);@+} - - -@ These are clear inlines and operators. -@<|IntSequence| inlines and operators@>= - const IntSequence& operator=(const IntSequence& s); - virtual ~IntSequence() - {@+ if (destroy) delete [] data;@+} - bool operator==(const IntSequence& s) const; - bool operator!=(const IntSequence& s) const - {@+ return ! operator==(s);@+} - int& operator[](int i) - {@+ return data[i];@+} - int operator[](int i) const - {@+ return data[i];@+} - int size() const - {@+ return length;@+} - -@ We provide two orderings. The first |operator<| is the linear -lexicographic ordering, the second |less| is the non-linear Cartesian -ordering. -@<|IntSequence| orderings@>= - bool operator<(const IntSequence& s) const; - bool operator<=(const IntSequence& s) const - {@+ return (operator==(s) || operator<(s));@+} - bool lessEq(const IntSequence& s) const; - bool less(const IntSequence& s) const; - - -@ End of {\tt int\_sequence.h} file. diff --git a/dynare++/tl/cc/kron_prod.cc b/dynare++/tl/cc/kron_prod.cc new file mode 100644 index 000000000..0b8f32d06 --- /dev/null +++ b/dynare++/tl/cc/kron_prod.cc @@ -0,0 +1,430 @@ +// Copyright 2004, Ondra Kamenik + +#include "kron_prod.hh" +#include "tl_exception.hh" + +#include + +/* Here we construct Kronecker product dimensions from Kronecker + product dimensions by picking a given matrix and all other set to + identity. The 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 identity matrices must fit into the described order. See + header file. + + We first decide what is a length of the resulting dimensions. Possible + length is three for $I\otimes A\otimes I$, and two for $I\otimes A$, + or $A\otimes I$. + + Then we fork according to |i|. */ + +KronProdDimens::KronProdDimens(const KronProdDimens &kd, int i) + : rows((i == 0 || i == kd.dimen()-1) ? (2) : (3)), + cols((i == 0 || i == kd.dimen()-1) ? (2) : (3)) +{ + TL_RAISE_IF(i < 0 || i >= kd.dimen(), + "Wrong index for pickup in KronProdDimens constructor"); + + int kdim = kd.dimen(); + if (i == 0) + { + // set AI dimensions + /* The first rows and cols are taken from |kd|. The dimensions of + identity matrix is a number of rows in $A_2\otimes\ldots\otimes A_n$ + since the matrix $A_1\otimes I$ is the first. */ + rows[0] = kd.rows[0]; + rows[1] = kd.rows.mult(1, kdim); + cols[0] = kd.cols[0]; + cols[1] = rows[1]; + } + else if (i == kdim-1) + { + // set IA dimensions + /* The second dimension is taken from |kd|. The dimensions of identity + matrix is a number of columns of $A_1\otimes\ldots A_{n-1}$, since the + matrix $I\otimes A_n$ is the last. */ + rows[0] = kd.cols.mult(0, kdim-1); + rows[1] = kd.rows[kdim-1]; + cols[0] = rows[0]; + cols[1] = kd.cols[kdim-1]; + } + else + { + // set IAI dimensions + /* The dimensions of the middle matrix are taken from |kd|. The + dimensions of the first identity matrix are a number of columns of + $A_1\otimes\ldots\otimes A_{i-1}$, and the dimensions of the last + identity matrix are a number of rows of $A_{i+1}\otimes\ldots\otimes + A_n$. */ + rows[0] = kd.cols.mult(0, i); + cols[0] = rows[0]; + rows[1] = kd.rows[i]; + cols[1] = kd.cols[i]; + cols[2] = kd.rows.mult(i+1, kdim); + rows[2] = cols[2]; + } +} + +/* This raises an exception if dimensions are bad for multiplication + |out = in*this|. */ + +void +KronProd::checkDimForMult(const ConstTwoDMatrix &in, const TwoDMatrix &out) const +{ + int my_rows; + int my_cols; + kpd.getRC(my_rows, my_cols); + TL_RAISE_IF(in.nrows() != out.nrows() || in.ncols() != my_rows, + "Wrong dimensions for KronProd in KronProd::checkDimForMult"); +} + +/* Here we Kronecker multiply two given vectors |v1| and |v2| and + store the result in preallocated |res|. */ + +void +KronProd::kronMult(const ConstVector &v1, const ConstVector &v2, + Vector &res) +{ + TL_RAISE_IF(res.length() != v1.length()*v2.length(), + "Wrong vector lengths in KronProd::kronMult"); + res.zeros(); + for (int i = 0; i < v1.length(); i++) + { + Vector sub(res, i *v2.length(), v2.length()); + sub.add(v1[i], v2); + } +} + +void +KronProdAll::setMat(int i, const TwoDMatrix &m) +{ + matlist[i] = &m; + kpd.setRC(i, m.nrows(), m.ncols()); +} + +void +KronProdAll::setUnit(int i, int n) +{ + matlist[i] = NULL; + kpd.setRC(i, n, n); +} + +bool +KronProdAll::isUnit() const +{ + int i = 0; + while (i < dimen() && matlist[i] == NULL) + i++; + return i == dimen(); +} + +/* Here we multiply $B\cdot(I\otimes A)$. If $m$ is a dimension of the + identity matrix, then the product is equal to + $B\cdot\hbox{diag}_m(A)$. If $B$ is partitioned accordingly, then the + result is $[B_1A, B_2A,\ldots B_mA]$. + + Here, |outi| are partitions of |out|, |ini| are const partitions of + |in|, and |id_cols| is $m$. We employ level-2 BLAS. */ + +void +KronProdIA::mult(const ConstTwoDMatrix &in, TwoDMatrix &out) const +{ + checkDimForMult(in, out); + + int id_cols = kpd.cols[0]; + ConstTwoDMatrix a(mat); + + for (int i = 0; i < id_cols; i++) + { + TwoDMatrix outi(out, i *a.ncols(), a.ncols()); + ConstTwoDMatrix ini(in, i *a.nrows(), a.nrows()); + outi.mult(ini, a); + } +} + +/* Here we construct |KronProdAI| from |KronProdIAI|. It is clear. */ +KronProdAI::KronProdAI(const KronProdIAI &kpiai) + : KronProd(KronProdDimens(2)), mat(kpiai.mat) +{ + kpd.rows[0] = mat.nrows(); + kpd.cols[0] = mat.ncols(); + kpd.rows[1] = kpiai.kpd.rows[2]; + kpd.cols[1] = kpiai.kpd.cols[2]; +} + +/* Here we multiply $B\cdot(A\otimes I)$. Let the dimension of the + matrix $A$ be $m\times n$, the dimension of $I$ be $p$, and a number + of rows of $B$ be $q$. We use the fact that $B\cdot(A\otimes + I)=\hbox{reshape}(\hbox{reshape}(B, q, mp)\cdot A, q, np)$. This works + only for matrix $B$, whose storage has leading dimension equal to + number of rows. + + For cases where the leading dimension is not equal to the number of + rows, we partition the matrix $A\otimes I$ to $m\times n$ square + partitions $a_{ij}I$. Therefore, we partition $B$ to $m$ partitions + $[B_1, B_2,\ldots,B_m]$. Each partition of $B$ has the same number of + columns as the identity matrix. If $R$ denotes the resulting matrix, + then it can be partitioned to $n$ partitions + $[R_1,R_2,\ldots,R_n]$. Each partition of $R$ has the same number of + columns as the identity matrix. Then we have $R_i=\sum a_{ji}B_j$. + + In code, |outi| is $R_i$, |ini| is $B_j$, and |id_cols| is a dimension + of the identity matrix */ + +void +KronProdAI::mult(const ConstTwoDMatrix &in, TwoDMatrix &out) const +{ + checkDimForMult(in, out); + + int id_cols = kpd.cols[1]; + ConstTwoDMatrix a(mat); + + if (in.getLD() == in.nrows()) + { + ConstTwoDMatrix in_resh(in.nrows()*id_cols, a.nrows(), in.getData().base()); + TwoDMatrix out_resh(in.nrows()*id_cols, a.ncols(), out.getData().base()); + out_resh.mult(in_resh, a); + } + else + { + out.zeros(); + for (int i = 0; i < a.ncols(); i++) + { + TwoDMatrix outi(out, i *id_cols, id_cols); + for (int j = 0; j < a.nrows(); j++) + { + ConstTwoDMatrix ini(in, j *id_cols, id_cols); + outi.add(a.get(j, i), ini); + } + } + } +} + +/* Here we multiply $B\cdot(I\otimes A\otimes I)$. If $n$ is a + dimension of the first identity matrix, then we multiply + $B\cdot\hbox{diag}_n(A\otimes I)$. So we partition $B$ and result $R$ + accordingly, and multiply $B_i\cdot(A\otimes I)$, which is in fact + |KronProdAI::mult|. Note that number of columns of partitions of $B$ + are number of rows of $A\otimes I$, and number of columns of $R$ are + number of columns of $A\otimes I$. + + In code, |id_cols| is $n$, |akronid| is a Kronecker product object of + $A\otimes I$, and |in_bl_width|, and |out_bl_width| are rows and cols of + $A\otimes I$. */ + +void +KronProdIAI::mult(const ConstTwoDMatrix &in, TwoDMatrix &out) const +{ + checkDimForMult(in, out); + + int id_cols = kpd.cols[0]; + + KronProdAI akronid(*this); + int in_bl_width; + int out_bl_width; + akronid.kpd.getRC(in_bl_width, out_bl_width); + + for (int i = 0; i < id_cols; i++) + { + TwoDMatrix outi(out, i *out_bl_width, out_bl_width); + ConstTwoDMatrix ini(in, i *in_bl_width, in_bl_width); + akronid.mult(ini, outi); + } +} + +/* Here we multiply $B\cdot(A_1\otimes\ldots\otimes A_n)$. First we + multiply $B\cdot(A_1\otimes)$, then this is multiplied by all + $I\otimes A_i\otimes I$, and finally by $I\otimes A_n$. + + If the dimension of the Kronecker product is only 1, then we multiply + two matrices in straight way and return. + + The intermediate results are stored on heap pointed by |last|. A new + result is allocated, and then the former storage is deallocated. + + We have to be careful in cases when last or first matrix is unit and + no calculations are performed in corresponding codes. The codes should + handle |last| safely also if no calcs are done. */ + +void +KronProdAll::mult(const ConstTwoDMatrix &in, TwoDMatrix &out) const +{ + // quick copy if product is unit + if (isUnit()) + { + out.zeros(); + out.add(1.0, in); + return; + } + + // quick zero if one of the matrices is zero + /* If one of the matrices is exactly zero or the |in| matrix is zero, + set out to zero and return */ + bool is_zero = false; + for (int i = 0; i < dimen() && !is_zero; i++) + is_zero = matlist[i] && matlist[i]->isZero(); + if (is_zero || in.isZero()) + { + out.zeros(); + return; + } + + // quick multiplication if dimension is 1 + if (dimen() == 1) + { + if (matlist[0]) // always true + out.mult(in, ConstTwoDMatrix(*(matlist[0]))); + return; + } + + int c; + TwoDMatrix *last = NULL; + + // perform first multiplication AI + /* Here we have to construct $A_1\otimes I$, allocate intermediate + result |last|, and perform the multiplication. */ + if (matlist[0]) + { + KronProdAI akronid(*this); + c = akronid.kpd.ncols(); + last = new TwoDMatrix(in.nrows(), c); + akronid.mult(in, *last); + } + else + { + last = new TwoDMatrix(in.nrows(), in.ncols(), in.getData().base()); + } + + // perform intermediate multiplications IAI + /* Here we go through all $I\otimes A_i\otimes I$, construct the + product, allocate new storage for result |newlast|, perform the + multiplication, deallocate old |last|, and set |last| to |newlast|. */ + for (int i = 1; i < dimen()-1; i++) + { + if (matlist[i]) + { + KronProdIAI interkron(*this, i); + c = interkron.kpd.ncols(); + TwoDMatrix *newlast = new TwoDMatrix(in.nrows(), c); + interkron.mult(*last, *newlast); + delete last; + last = newlast; + } + } + + // perform last multiplication IA + /* Here just construct $I\otimes A_n$ and perform multiplication and + deallocate |last|. */ + if (matlist[dimen()-1]) + { + KronProdIA idkrona(*this); + idkrona.mult(*last, out); + } + else + { + out = *last; + } + delete last; +} + +/* This calculates a Kornecker product of rows of matrices, the row + indices are given by the integer sequence. The result is allocated and + returned. The caller is repsonsible for its deallocation. */ + +Vector * +KronProdAll::multRows(const IntSequence &irows) const +{ + TL_RAISE_IF(irows.size() != dimen(), + "Wrong length of row indices in KronProdAll::multRows"); + + Vector *last = NULL; + ConstVector *row; + vector to_delete; + for (int i = 0; i < dimen(); i++) + { + int j = dimen()-1-i; + + // set |row| to the row of |j|-th matrix + /* If the |j|-th matrix is real matrix, then the row is constructed + from the matrix. It the matrix is unit, we construct a new vector, + fill it with zeros, than set the unit to appropriate place, and make + the |row| as ConstVector of this vector, which sheduled for + deallocation. */ + if (matlist[j]) + row = new ConstVector(irows[j], *(matlist[j])); + else + { + Vector *aux = new Vector(ncols(j)); + aux->zeros(); + (*aux)[irows[j]] = 1.0; + to_delete.push_back(aux); + row = new ConstVector(*aux); + } + + // set |last| to product of |row| and |last| + /* If the |last| is exists, we allocate new storage, Kronecker + multiply, deallocate the old storage. If the |last| does not exist, + then we only make |last| equal to |row|. */ + if (last) + { + Vector *newlast; + newlast = new Vector(last->length()*row->length()); + kronMult(*row, ConstVector(*last), *newlast); + delete last; + last = newlast; + } + else + { + last = new Vector(*row); + } + + delete row; + } + + for (unsigned int i = 0; i < to_delete.size(); i++) + delete to_delete[i]; + + return last; +} + +/* This permutes the matrices so that the new ordering would minimize + memory consumption. As shown in |@<|KronProdAllOptim| class declaration@>|, + we want ${m_k\over n_k}\leq{m_{k-1}\over n_{k-1}}\ldots\leq{m_1\over n_1}$, + where $(m_i,n_i)$ is the dimension of $A_i$. So we implement the bubble + sort. */ + +void +KronProdAllOptim::optimizeOrder() +{ + for (int i = 0; i < dimen(); i++) + { + int swaps = 0; + for (int j = 0; j < dimen()-1; j++) + { + if (((double) kpd.rows[j])/kpd.cols[j] < ((double) kpd.rows[j+1])/kpd.cols[j+1]) + { + // swap dimensions and matrices at |j| and |j+1| + int s = kpd.rows[j+1]; + kpd.rows[j+1] = kpd.rows[j]; + kpd.rows[j] = s; + s = kpd.cols[j+1]; + kpd.cols[j+1] = kpd.cols[j]; + kpd.cols[j] = s; + const TwoDMatrix *m = matlist[j+1]; + matlist[j+1] = matlist[j]; + matlist[j] = m; + + // project the swap to the permutation |oper| + s = oper.getMap()[j+1]; + oper.getMap()[j+1] = oper.getMap()[j]; + oper.getMap()[j] = s; + swaps++; + } + } + if (swaps == 0) + { + return; + } + } +} diff --git a/dynare++/tl/cc/kron_prod.cweb b/dynare++/tl/cc/kron_prod.cweb deleted file mode 100644 index 6fd43b466..000000000 --- a/dynare++/tl/cc/kron_prod.cweb +++ /dev/null @@ -1,457 +0,0 @@ -@q $Id: kron_prod.cweb 1834 2008-05-18 20:23:54Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@ Start of {\tt kron\_prod.cpp} file. -@c -#include "kron_prod.h" -#include "tl_exception.h" - -#include - -@<|KronProdDimens| constructor code@>; -@<|KronProd::checkDimForMult| code@>; -@<|KronProd::kronMult| code@>; -@<|KronProdAll::setMat| code@>; -@<|KronProdAll::setUnit| code@>; -@<|KronProdAll::isUnit| code@>; -@<|KronProdAll::multRows| code@>; -@<|KronProdIA::mult| code@>; -@<|KronProdAI| constructor code@>; -@<|KronProdAI::mult| code@>; -@<|KronProdIAI::mult| code@>; -@<|KronProdAll::mult| code@>; -@<|KronProdAllOptim::optimizeOrder| code@>; - -@ Here we construct Kronecker product dimensions from Kronecker -product dimensions by picking a given matrix and all other set to -identity. The 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 identity matrices must fit into the described order. See -header file. - -We first decide what is a length of the resulting dimensions. Possible -length is three for $I\otimes A\otimes I$, and two for $I\otimes A$, -or $A\otimes I$. - -Then we fork according to |i|. - -@<|KronProdDimens| constructor code@>= -KronProdDimens::KronProdDimens(const KronProdDimens& kd, int i) - : rows((i==0 || i==kd.dimen()-1)? (2):(3)), - cols((i==0 || i==kd.dimen()-1)? (2):(3)) -{ - TL_RAISE_IF(i < 0 || i >= kd.dimen(), - "Wrong index for pickup in KronProdDimens constructor"); - - int kdim = kd.dimen(); - if (i == 0) { - @; - } else if (i == kdim-1){ - @; - } else { - @; - } -} - -@ The first rows and cols are taken from |kd|. The dimensions of -identity matrix is a number of rows in $A_2\otimes\ldots\otimes A_n$ -since the matrix $A_1\otimes I$ is the first. - -@= -rows[0] = kd.rows[0]; -rows[1] = kd.rows.mult(1, kdim); -cols[0] = kd.cols[0]; -cols[1] = rows[1]; - -@ The second dimension is taken from |kd|. The dimensions of identity -matrix is a number of columns of $A_1\otimes\ldots A_{n-1}$, since the -matrix $I\otimes A_n$ is the last. - -@= -rows[0] = kd.cols.mult(0, kdim-1); -rows[1] = kd.rows[kdim-1]; -cols[0] = rows[0]; -cols[1] = kd.cols[kdim-1]; - -@ The dimensions of the middle matrix are taken from |kd|. The -dimensions of the first identity matrix are a number of columns of -$A_1\otimes\ldots\otimes A_{i-1}$, and the dimensions of the last -identity matrix are a number of rows of $A_{i+1}\otimes\ldots\otimes -A_n$. - -@= -rows[0] = kd.cols.mult(0, i); -cols[0] = rows[0]; -rows[1] = kd.rows[i]; -cols[1] = kd.cols[i]; -cols[2] = kd.rows.mult(i+1, kdim); -rows[2] = cols[2]; - - -@ This raises an exception if dimensions are bad for multiplication -|out = in*this|. - -@<|KronProd::checkDimForMult| code@>= -void KronProd::checkDimForMult(const ConstTwoDMatrix& in, const TwoDMatrix& out) const -{ - int my_rows; - int my_cols; - kpd.getRC(my_rows, my_cols); - TL_RAISE_IF(in.nrows() != out.nrows() || in.ncols() != my_rows, - "Wrong dimensions for KronProd in KronProd::checkDimForMult"); -} - -@ Here we Kronecker multiply two given vectors |v1| and |v2| and -store the result in preallocated |res|. - -@<|KronProd::kronMult| code@>= -void KronProd::kronMult(const ConstVector& v1, const ConstVector& v2, - Vector& res) -{ - TL_RAISE_IF(res.length() != v1.length()*v2.length(), - "Wrong vector lengths in KronProd::kronMult"); - res.zeros(); - for (int i = 0; i < v1.length(); i++) { - Vector sub(res, i*v2.length(), v2.length()); - sub.add(v1[i], v2); - } -} - - -@ -@<|KronProdAll::setMat| code@>= -void KronProdAll::setMat(int i, const TwoDMatrix& m) -{ - matlist[i] = &m; - kpd.setRC(i, m.nrows(), m.ncols()); -} - -@ -@<|KronProdAll::setUnit| code@>= -void KronProdAll::setUnit(int i, int n) -{ - matlist[i] = NULL; - kpd.setRC(i, n, n); -} - -@ -@<|KronProdAll::isUnit| code@>= -bool KronProdAll::isUnit() const -{ - int i = 0; - while (i < dimen() && matlist[i] == NULL) - i++; - return i == dimen(); -} - -@ Here we multiply $B\cdot(I\otimes A)$. If $m$ is a dimension of the -identity matrix, then the product is equal to -$B\cdot\hbox{diag}_m(A)$. If $B$ is partitioned accordingly, then the -result is $[B_1A, B_2A,\ldots B_mA]$. - -Here, |outi| are partitions of |out|, |ini| are const partitions of -|in|, and |id_cols| is $m$. We employ level-2 BLAS. - -@<|KronProdIA::mult| code@>= -void KronProdIA::mult(const ConstTwoDMatrix& in, TwoDMatrix& out) const -{ - checkDimForMult(in, out); - - int id_cols = kpd.cols[0]; - ConstTwoDMatrix a(mat); - - for (int i = 0; i < id_cols; i++) { - TwoDMatrix outi(out, i*a.ncols(), a.ncols()); - ConstTwoDMatrix ini(in, i*a.nrows(), a.nrows()); - outi.mult(ini, a); - } -} - -@ Here we construct |KronProdAI| from |KronProdIAI|. It is clear. -@<|KronProdAI| constructor code@>= -KronProdAI::KronProdAI(const KronProdIAI& kpiai) - : KronProd(KronProdDimens(2)), mat(kpiai.mat) -{ - kpd.rows[0] = mat.nrows(); - kpd.cols[0] = mat.ncols(); - kpd.rows[1] = kpiai.kpd.rows[2]; - kpd.cols[1] = kpiai.kpd.cols[2]; -} - - -@ Here we multiply $B\cdot(A\otimes I)$. Let the dimension of the -matrix $A$ be $m\times n$, the dimension of $I$ be $p$, and a number -of rows of $B$ be $q$. We use the fact that $B\cdot(A\otimes -I)=\hbox{reshape}(\hbox{reshape}(B, q, mp)\cdot A, q, np)$. This works -only for matrix $B$, whose storage has leading dimension equal to -number of rows. - -For cases where the leading dimension is not equal to the number of -rows, we partition the matrix $A\otimes I$ to $m\times n$ square -partitions $a_{ij}I$. Therefore, we partition $B$ to $m$ partitions -$[B_1, B_2,\ldots,B_m]$. Each partition of $B$ has the same number of -columns as the identity matrix. If $R$ denotes the resulting matrix, -then it can be partitioned to $n$ partitions -$[R_1,R_2,\ldots,R_n]$. Each partition of $R$ has the same number of -columns as the identity matrix. Then we have $R_i=\sum a_{ji}B_j$. - -In code, |outi| is $R_i$, |ini| is $B_j$, and |id_cols| is a dimension -of the identity matrix - -@<|KronProdAI::mult| code@>= -void KronProdAI::mult(const ConstTwoDMatrix& in, TwoDMatrix& out) const -{ - checkDimForMult(in, out); - - int id_cols = kpd.cols[1]; - ConstTwoDMatrix a(mat); - - if (in.getLD() == in.nrows()) { - ConstTwoDMatrix in_resh(in.nrows()*id_cols, a.nrows(), in.getData().base()); - TwoDMatrix out_resh(in.nrows()*id_cols, a.ncols(), out.getData().base()); - out_resh.mult(in_resh, a); - } else { - out.zeros(); - for (int i = 0; i < a.ncols(); i++) { - TwoDMatrix outi(out, i*id_cols, id_cols); - for (int j = 0; j < a.nrows(); j++) { - ConstTwoDMatrix ini(in, j*id_cols, id_cols); - outi.add(a.get(j,i), ini); - } - } - } -} - - -@ Here we multiply $B\cdot(I\otimes A\otimes I)$. If $n$ is a -dimension of the first identity matrix, then we multiply -$B\cdot\hbox{diag}_n(A\otimes I)$. So we partition $B$ and result $R$ -accordingly, and multiply $B_i\cdot(A\otimes I)$, which is in fact -|KronProdAI::mult|. Note that number of columns of partitions of $B$ -are number of rows of $A\otimes I$, and number of columns of $R$ are -number of columns of $A\otimes I$. - -In code, |id_cols| is $n$, |akronid| is a Kronecker product object of -$A\otimes I$, and |in_bl_width|, and |out_bl_width| are rows and cols of -$A\otimes I$. - - -@<|KronProdIAI::mult| code@>= -void KronProdIAI::mult(const ConstTwoDMatrix& in, TwoDMatrix& out) const -{ - checkDimForMult(in, out); - - int id_cols = kpd.cols[0]; - - KronProdAI akronid(*this); - int in_bl_width; - int out_bl_width; - akronid.kpd.getRC(in_bl_width, out_bl_width); - - for (int i = 0; i < id_cols; i++) { - TwoDMatrix outi(out, i*out_bl_width, out_bl_width); - ConstTwoDMatrix ini(in, i*in_bl_width, in_bl_width); - akronid.mult(ini, outi); - } -} - -@ Here we multiply $B\cdot(A_1\otimes\ldots\otimes A_n)$. First we -multiply $B\cdot(A_1\otimes)$, then this is multiplied by all -$I\otimes A_i\otimes I$, and finally by $I\otimes A_n$. - -If the dimension of the Kronecker product is only 1, then we multiply -two matrices in straight way and return. - -The intermediate results are stored on heap pointed by |last|. A new -result is allocated, and then the former storage is deallocated. - -We have to be careful in cases when last or first matrix is unit and -no calculations are performed in corresponding codes. The codes should -handle |last| safely also if no calcs are done. - -@<|KronProdAll::mult| code@>= -void KronProdAll::mult(const ConstTwoDMatrix& in, TwoDMatrix& out) const -{ - @; - @; - @; - int c; - TwoDMatrix* last = NULL; - @; - @; - @; -} - -@ -@= - if (isUnit()) { - out.zeros(); - out.add(1.0, in); - return; - } - -@ If one of the matrices is exactly zero or the |in| matrix is zero, -set out to zero and return - -@= - bool is_zero = false; - for (int i = 0; i < dimen() && ! is_zero; i++) - is_zero = matlist[i] && matlist[i]->isZero(); - if (is_zero || in.isZero()) { - out.zeros(); - return; - } - -@ -@= - if (dimen() == 1) { - if (matlist[0]) // always true - out.mult(in, ConstTwoDMatrix(*(matlist[0]))); - return; - } - -@ Here we have to construct $A_1\otimes I$, allocate intermediate -result |last|, and perform the multiplication. - -@= - if (matlist[0]) { - KronProdAI akronid(*this); - c = akronid.kpd.ncols(); - last = new TwoDMatrix(in.nrows(), c); - akronid.mult(in, *last); - } else { - last = new TwoDMatrix(in.nrows(), in.ncols(), in.getData().base()); - } - -@ Here we go through all $I\otimes A_i\otimes I$, construct the -product, allocate new storage for result |newlast|, perform the -multiplication, deallocate old |last|, and set |last| to |newlast|. - -@= - for (int i = 1; i < dimen()-1; i++) { - if (matlist[i]) { - KronProdIAI interkron(*this, i); - c = interkron.kpd.ncols(); - TwoDMatrix* newlast = new TwoDMatrix(in.nrows(), c); - interkron.mult(*last, *newlast); - delete last; - last = newlast; - } - } - -@ Here just construct $I\otimes A_n$ and perform multiplication and -deallocate |last|. - -@= - if (matlist[dimen()-1]) { - KronProdIA idkrona(*this); - idkrona.mult(*last, out); - } else { - out = *last; - } - delete last; - -@ This calculates a Kornecker product of rows of matrices, the row -indices are given by the integer sequence. The result is allocated and -returned. The caller is repsonsible for its deallocation. - -@<|KronProdAll::multRows| code@>= -Vector* KronProdAll::multRows(const IntSequence& irows) const -{ - TL_RAISE_IF(irows.size() != dimen(), - "Wrong length of row indices in KronProdAll::multRows"); - - Vector* last = NULL; - ConstVector* row; - vector to_delete; - for (int i = 0; i < dimen(); i++) { - int j = dimen()-1-i; - @; - @; - delete row; - } - - for (unsigned int i = 0; i < to_delete.size(); i++) - delete to_delete[i]; - - return last; -} - -@ If the |j|-th matrix is real matrix, then the row is constructed -from the matrix. It the matrix is unit, we construct a new vector, -fill it with zeros, than set the unit to appropriate place, and make -the |row| as ConstVector of this vector, which sheduled for -deallocation. - -@= - if (matlist[j]) - row = new ConstVector(irows[j], *(matlist[j])); - else { - Vector* aux = new Vector(ncols(j)); - aux->zeros(); - (*aux)[irows[j]] = 1.0; - to_delete.push_back(aux); - row = new ConstVector(*aux); - } - -@ If the |last| is exists, we allocate new storage, Kronecker -multiply, deallocate the old storage. If the |last| does not exist, -then we only make |last| equal to |row|. - -@= - if (last) { - Vector* newlast; - newlast = new Vector(last->length()*row->length()); - kronMult(*row, ConstVector(*last), *newlast); - delete last; - last = newlast; - } else { - last = new Vector(*row); - } - - -@ This permutes the matrices so that the new ordering would minimize -memory consumption. As shown in |@<|KronProdAllOptim| class declaration@>|, -we want ${m_k\over n_k}\leq{m_{k-1}\over n_{k-1}}\ldots\leq{m_1\over n_1}$, -where $(m_i,n_i)$ is the dimension of $A_i$. So we implement the bubble -sort. - -@<|KronProdAllOptim::optimizeOrder| code@>= -void KronProdAllOptim::optimizeOrder() -{ - for (int i = 0; i < dimen(); i++) { - int swaps = 0; - for (int j = 0; j < dimen()-1; j++) { - if (((double)kpd.rows[j])/kpd.cols[j] < ((double)kpd.rows[j+1])/kpd.cols[j+1]) { - @; - @; - } - } - if (swaps == 0) { - return; - } - } -} - -@ -@= - int s = kpd.rows[j+1]; - kpd.rows[j+1] = kpd.rows[j]; - kpd.rows[j] = s; - s = kpd.cols[j+1]; - kpd.cols[j+1] = kpd.cols[j]; - kpd.cols[j] = s; - const TwoDMatrix* m = matlist[j+1]; - matlist[j+1] = matlist[j]; - matlist[j] = m; - -@ -@= - s = oper.getMap()[j+1]; - oper.getMap()[j+1] = oper.getMap()[j]; - oper.getMap()[j] = s; - swaps++; - - -@ End of {\tt kron\_prod.cpp} file. \ No newline at end of file diff --git a/dynare++/tl/cc/kron_prod.hh b/dynare++/tl/cc/kron_prod.hh new file mode 100644 index 000000000..9203d482b --- /dev/null +++ b/dynare++/tl/cc/kron_prod.hh @@ -0,0 +1,348 @@ +// Copyright 2004, Ondra Kamenik + +// Kronecker product. + +/* 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$. */ + +#ifndef KRON_PROD_H +#define KRON_PROD_H + +#include "twod_matrix.hh" +#include "permutation.hh" +#include "int_sequence.hh" + +class KronProdAll; +class KronProdAllOptim; +class KronProdIA; +class KronProdIAI; +class KronProdAI; + +/* |KronProdDimens| maintains a dimension of the Kronecker product. So, + it maintains two sequences, one for rows, and one for columns. */ + +class KronProdDimens +{ + friend class KronProdAll; + friend class KronProdAllOptim; + friend class KronProdIA; + friend class KronProdIAI; + friend class KronProdAI; +private: + IntSequence rows; + IntSequence cols; +public: + /* 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(int dim) + : rows(dim, 0), cols(dim, 0) + { + } + KronProdDimens(const KronProdDimens &kd) + : rows(kd.rows), cols(kd.cols) + { + } + KronProdDimens(const KronProdDimens &kd, int i); + + 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; + } + + 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. */ + +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|. */ + +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|. */ + +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. */ + +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. */ + +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. */ + +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; +}; + +#endif diff --git a/dynare++/tl/cc/kron_prod.hweb b/dynare++/tl/cc/kron_prod.hweb deleted file mode 100644 index 69eb72718..000000000 --- a/dynare++/tl/cc/kron_prod.hweb +++ /dev/null @@ -1,296 +0,0 @@ -@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. diff --git a/dynare++/tl/cc/main.web b/dynare++/tl/cc/main.web deleted file mode 100644 index c98f23443..000000000 --- a/dynare++/tl/cc/main.web +++ /dev/null @@ -1,387 +0,0 @@ -@q $Id: main.web 2338 2009-01-14 10:40:30Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@q cwebmac.tex defines its own \ifpdf, which is incompatible with the @> -@q \ifpdf defined by eplain, so undefine it @> -\let\ifpdf\relax -\input eplain - -@q now define \ifpdf to be always false: PDF macros of cwebmac are buggy @> -\newif\ifpdf -\iffalse\fi - -\def\title{{\mainfont Tensor Library}} - - -@i ../../c++lib.w -@s const_reverse_iterator int -@s value_type int - -\titletrue -\null\vfill -\centerline{\titlefont Multidimensional Tensor Library} -\vskip\baselineskip -\centerline{\vtop{\hsize=10cm\leftskip=0pt plus 1fil - \rightskip=0pt plus 1fil\noindent - primary use in perturbation methods for Stochastic - Dynamic General Equilibrium (SDGE) models}} -\vfill\vfill -Copyright \copyright\ 2004 by Ondra Kamenik - -@*1 Library overview. - -The design of the library was driven by the needs of perturbation -methods for solving Stochastic Dynamic General Equilibrium models. The -aim of the library is not to provide an exhaustive interface to -multidimensional linear algebra. The tensor library's main purposes -include: -\unorderedlist - -\li Define types for tensors, for a multidimensional index of a -tensor, and types for folded and unfolded tensors. The tensors defined -here have only one multidimensional index and one reserved -one-dimensional index. The tensors should allow modelling of higher -order derivatives with respect to a few vectors with different sizes -(for example $\left[g_{y^2u^3}\right]$). The tensors should allow -folded and unfolded storage modes and conversion between them. A -folded tensor stores symmetric elements only once, while an unfolded -stores data as a whole multidimensional cube. - -\li Define both sparse and dense tensors. We need only one particular -type of sparse tensor. This in contrast to dense tensors, where we -need much wider family of types. - -\li Implement the Faa Di Bruno multidimensional formula. So, the main -purpose of the library is to implement the following step of Faa Di Bruno: -$$\left[B_{s^k}\right]_{\alpha_1\ldots\alpha_k} -=\left[h_{y^l}\right]_{\gamma_1\ldots\gamma_l} -\left(\sum_{c\in M_{l,k}} -\prod_{m=1}^l\left[g_{c_m}\right]^{\gamma_m}_{c_m(\alpha)}\right)$$ -where $s$ can be a compound vector of variables, $M_{l,k}$ is a set of -all equivalences of $k$ element set having $l$ classes, $c_m$ is -$m$-th class of equivalence $c$, and $c_m(\alpha)$ is a tuple of -picked indices from $\alpha$ by class $c_m$. - -Note that the sparse tensors play a role of $h$ in the Faa Di Bruno, not -of $B$ nor $g$. - -\endunorderedlist - -The following table is a road-map to various abstractions in the library. - -\def\defloc#1#2{#1\hfill\break{\tt #2}} - -\noindent -\halign to\hsize{% -\vtop{\hsize=6.6cm\rightskip=0pt plus 1fil\noindent #}& -\vtop{\advance\hsize by-6.6cm% - \raggedright\noindent\vrule width 0pt height 14pt #}\cr -Class defined in & Purpose\cr -\noalign{\hrule}\cr -\defloc{|@<|Tensor| class declaration@>|}{tensor.hweb}& -Virtual base class for all dense tensors, defines |index| as the -multidimensonal iterator -\cr -\defloc{|@<|FTensor| class declaration@>|}{tensor.hweb}& -Virtual base class for all folded tensors -\cr -\defloc{|@<|UTensor| class declaration@>|}{tensor.hweb}& -Virtual base class for all unfolded tensors -\cr -\defloc{|@<|FFSTensor| class declaration@>|}{fs\_tensor.hweb}& -Class representing folded full symmetry dense tensor, -for instance $\left[g_{y^3}\right]$ -\cr -\defloc{|@<|FGSTensor| class declaration@>|}{gs\_tensor.hweb}& -Class representing folded general symmetry dense tensor, -for instance $\left[g_{y^2u^3}\right]$ -\cr -\defloc{|@<|UFSTensor| class declaration@>|}{fs\_tensor.hweb}& -Class representing unfolded full symmetry dense tensor, -for instance $\left[g_{y^3}\right]$ -\cr -\defloc{|@<|UGSTensor| class declaration@>|}{gs\_tensor.hweb}& -Class representing unfolded general symmetry dense tensor, -for instance $\left[g_{y^2u^3}\right]$ -\cr -|@<|URTensor| class declaration@>|\hfill\break -\defloc{|@<|FRTensor| class declaration@>|}{rfs\_tensor.hweb}& -Class representing unfolded/folded full symmetry, row-orient\-ed, -dense tensor. Row-oriented tensors are used in the Faa Di Bruno -above as some part (few or one column) of a product of $g$'s. Their -fold/unfold conversions are special in such a way, that they must -yield equivalent results if multiplied with folded/unfolded -column-oriented counterparts. -\cr -|@<|URSingleTensor| class declaration@>|\hfill\break -\defloc{|@<|FRSingleTensor| class declaration@>|}{rfs\_tensor.hweb}& -Class representing unfolded/folded full symmetry, row-orient\-ed, -single column, dense tensor. Besides use in the Faa Di Bruno, the -single column row oriented tensor models also higher moments of normal -distribution. -\cr -\defloc{|@<|UPSTensor| class declaration@>|}{ps\_tensor.hweb}& -Class representing unfolded, column-orient\-ed tensor whose symmetry -is not that of the $\left[B_{y^2u^3}\right]$ but rather of something -as $\left[B_{yuuyu}\right]$. This tensor evolves during the product -operation for unfolded tensors and its basic operation is to add -itself to a tensor with nicer symmetry, here $\left[B_{y^2u^3}\right]$. -\cr -\defloc{|@<|FPSTensor| class declaration@>|}{ps\_tensor.hweb}& -Class representing partially folded, column-orient\-ed tensor who\-se -symmetry is not that of the $\left[B_{y^3u^4}\right]$ but rather -something as $\left[B_{yu\vert y^3u\vert u^4}\right]$, where the -portions of symmetries represent folded dimensions which are combined -in unfolded manner. This tensor evolves during the Faa Di Bruno -for folded tensors and its basic operation is to add itself to a -tensor with nicer symmetry, here folded $\left[B_{y^3u^4}\right]$. -\cr -\defloc{|@<|USubTensor| class declaration@>|}{pyramid\_prod.hweb}& -Class representing unfolded full symmetry, row-orient\-ed tensor which -contains a few columns of huge product -$\prod_{m=1}^l\left[g_{c_m}\right]^{\gamma_m}_{c_m(\alpha)}$. This is -needed during the Faa Di Bruno for folded matrices. -\cr -\defloc{|@<|IrregTensor| class declaration@>|}{pyramid2\_prod.hweb}& -Class representing a product of columns of derivatives -$\left[z_{y^ku^l}\right]$, where $z=[y^T,v^T,w^T]^T$. Since the first -part of $z$ is $y$, the derivatives contain many zeros, which are not -stored, hence the tensor's irregularity. The tensor is used when -calculating one step of Faa Di Bruno formula, i.e. -$\left[f_{z^l}\right]\sum\prod_{m=1}^l\left[z_{c_m}\right]^{\gamma_m}_{c_m(\alpha)}$. -\cr -\defloc{|@<|FSSparseTensor| class declaration@>|}{sparse\_tensor.hweb}& -Class representing full symmetry, column-oriented, sparse tensor. It -is able to store elements keyed by the multidimensional index, and -multiply itself with one column of row-oriented tensor. -\cr -\defloc{|@<|FGSContainer| class declaration@>|}{t\_container.hweb}& -Container of |FGSTensor|s. It implements the Faa Di Bruno with -unfolded or folded tensor $h$ yielding folded $B$. The methods are -|FGSContainer::multAndAdd|. -\cr -\defloc{|@<|UGSContainer| class declaration@>|}{t\_container.hweb}& -Container of |FGSTensor|s. It implements the Faa Di Bruno with -unfolded tensor $h$ yielding unfolded $B$. The method is -|UGSContainer::multAndAdd|. -\cr -\defloc{|@<|StackContainerInterface| class declaration@>|} -{stack\_container.hweb}&Virtual pure interface describing all logic -of stacked containers for which we will do the Faa Di Bruno operation. -\cr -\defloc{|@<|UnfoldedStackContainer| class declaration@>|} -{stack\_container.hweb}&Implements the Faa Di Bruno operation for stack of -containers of unfolded tensors. -\cr -\defloc{|@<|FoldedStackContainer| class declaration@>|}{stack\_container.hweb} -&Implements the Faa Di Bruno for stack of -containers of fold\-ed tensors. -\cr -\defloc{|@<|ZContainer| class declaration@>|}{stack\_container.hweb}& -The class implements the interface |StackContainerInterface| according -to $z$ appearing in context of SDGE models. By a simple inheritance, -we obtain |@<|UnfoldedZContainer| class declaration@>| and also -|@<|FoldedZContainer| class declaration@>|. -\cr -\defloc{|@<|GContainer| class declaration@>|}{stack\_container.hweb}& -The class implements the interface |StackContainerInterface| according -to $G$ appearing in context of SDGE models. By a simple inheritance, -we obtain |@<|UnfoldedGContainer| class declaration@>| and also -|@<|FoldedGContainer| class declaration@>|. -\cr -\defloc{|@<|Equivalence| class declaration@>|}{equivalence.hweb}& -The class represents an equivalence on $n$-element set. Useful in the -Faa Di Bruno. -\cr -\defloc{|@<|EquivalenceSet| class declaration@>|}{equivalence.hweb}& -The class representing all equivalences on $n$-element set. Useful in the -Faa Di Bruno. -\cr -\defloc{|@<|Symmetry| class declaration@>|}{symmetry.hweb}& -The class defines a symmetry of general symmetry tensor. This is it -defines a basic shape of the tensor. For $\left[B_{y^2u^3}\right]$, -the symmetry is $y^2u^3$. -\cr -\defloc{|@<|Permutation| class declaration@>|}{permutation.hweb}& -The class represents a permutation of $n$ indices. Useful in the -Faa Di Bruno. -\cr -\defloc{|@<|IntSequence| class declaration@>|}{int\_sequence.hweb}& -The class represents a sequence of integers. Useful everywhere. -\cr -|@<|TwoDMatrix| class declaration@>|\hfill\break -\defloc{|@<|ConstTwoDMatrix| class declaration@>|}{twod\_matrix.hweb}& -The class provides an interface to a code handling two-di\-men\-si\-onal -matrices. The code resides in Sylvester module, in directory {\tt -sylv/cc}. The object files from that directory need to be linked: {\tt -GeneralMatrix.o}, {\tt Vector.o} and {\tt SylvException.o}. There is -no similar interface to |Vector| and |ConstVector| classes from the -Sylvester module and they are used directly. -\cr -\defloc{|@<|KronProdAll| class declaration@>|}{kron\_prod.hweb}& -The class represents a Kronecker product of a sequence of arbitrary -matrices and is able to multiply a matrix from the right without -storing the Kronecker product in memory. -\cr -\defloc{|@<|KronProdAllOptim| class declaration@>|}{kron\_prod.hweb}& -The same as |KronProdAll| but it optimizes the order of matrices in -the product to minimize the used memory during the Faa Di Bruno -operation. Note that it is close to optimal flops. -\cr -|@<|FTensorPolynomial| class declaration@>|\hfill\break -\defloc{|@<|UTensorPolynomial| class declaration@>|}{t\_polynomial.hweb}& -Abstractions representing a polynomial whose coefficients are -folded/unfolded tensors and variable is a column vector. The classes -provide methods for traditional and horner-like polynomial -evaluation. This is useful in simulation code. -\cr -|@<|FNormalMoments| class declaration@>|\hfill\break -\defloc{|@<|UNormalMoments| class declaration@>|}{normal\_moments.hweb}& -These are containers for folded/unfolded single column tensors for -higher moments of normal distribution. The code contains an algorithm -for generating the moments for arbitrary covariance matrix. -\cr -\defloc{|@<|TLStatic| class declaration@>|}{tl\_static.hweb}& -The class encapsulates all static information needed for the -library. It includes a Pascal triangle (for quick computation of -binomial coefficients), and precalculated equivalence sets. -\cr -\defloc{|@<|TLException| class definition@>|}{tl\_exception.hweb}& -Simple class thrown as an exception. -\cr -} - -@s Tensor int -@s FTensor int -@s UTensor int -@s FFSTensor int -@s UFSTensor int -@s FGSTensor int -@s UGSTensor int -@s FRTensor int -@s URTensor int -@s FRSingleTensor int -@s URSingleTensor int -@s UPSTensor int -@s UGSContainer int -@s ZContainer int -@s GContainer int -@s StackContainerInterface int -@s FoldedStackContainer int -@s UnfoldedStackContainer int -@s FoldedZContainer int -@s UnfoldedZContainer int -@s FoldedGContainer int -@s UnfoldedGContainer int -@s Permutation int -@s KronProdAll int -@s KronProdAllOptim int -@s FTensorPolynomial int -@s UTensorPolynomial int -@s FNormalMoments int -@s UNormalMoments int -@s TLStatic int -@s FSSparseTensor int -@ The tensor library is multi-threaded. This means, if appropriate -compilation options were set, some codes are launched -concurrently. This boosts the performance on SMP machines or single -processors with hyper-threading support. The basic property of the -thread implementation in the library is that we do not allow running -more concurrent threads than the preset limit. This prevents threads -from competing for memory in such a way that the OS constantly switches -among threads with frequent I/O for swaps. This may occur since one -thread might need much own memory. The threading support allows for -detached threads, the synchronization points during the Faa Di Bruno -operation are relatively short, so the resulting load is close to the -preset maximum number parallel threads. - -@ A few words to the library's test suite. The suite resides in -directory {\tt tl/testing}. There is a file {\tt tests.cpp} which -contains all tests and {\tt main()} function. Also there are files -{\tt factory.h} and {\tt factory.cpp} implementing random generation -of various objects. The important property of these random objects is -that they are the same for all object's invocations. This is very -important in testing and debugging. Further, one can find files {\tt -monoms.h} and {\tt monoms.cpp}. See below for their explanation. - -There are a few types of tests: -\orderedlist -\li We test for tensor indices. We go through various tensors with -various symmetries, convert indices from folded to unfolded and -vice-versa. We test whether their coordinates are as expected. -\li We test the Faa Di Bruno by comparison of the results of -|FGSContainer::multAndAdd| against the results of |UGSContainer::multAndAdd|. The two - implementations are pretty different, so this is a good test. -\li We use a code in {\tt monoms.h} and {\tt monoms.cpp} to generate a - random vector function $f(x(y,u))$ along with derivatives of - $\left[f_x\right]$, $\left[x_{y^ku^l}\right]$, and - $\left[f_{y^ku^l}\right]$. Then we calculate the resulting derivatives - $\left[f_{y^ku^l}\right]$ using |multAndAdd| method of |UGSContainer| - or |FGSContainer| and compare the derivatives provided by {\tt - monoms}. The functions generated in {\tt monoms} are monomials with - integer exponents, so the implementation of {\tt monoms} is quite - easy. -\li We do a similar thing for sparse tensors. In this case the {\tt monoms} - generate a function $f(y,v(y,u),w(y,u))$, provide all the derivatives - and the result $\left[f_{y^ku^l}\right]$. Then we calculate the - derivatives with |multAndAdd| of |ZContainer| and compare. -\li We test the polynomial evaluation by evaluating a folded and - unfolded polynomial in traditional and horner-like fashion. This gives - four methods in total. The four results are compared. -\endorderedlist - - -@*1 Utilities. -@i sthread.hweb -@i sthread.cweb -@i tl_exception.hweb -@i int_sequence.hweb -@i int_sequence.cweb -@i twod_matrix.hweb -@i twod_matrix.cweb -@i kron_prod.hweb -@i kron_prod.cweb - -@*1 Combinatorics. -@i symmetry.hweb -@i symmetry.cweb -@i equivalence.hweb -@i equivalence.cweb -@i permutation.hweb -@i permutation.cweb - -@*1 Tensors. -@i tensor.hweb -@i tensor.cweb -@i fs_tensor.hweb -@i fs_tensor.cweb -@i gs_tensor.hweb -@i gs_tensor.cweb -@i rfs_tensor.hweb -@i rfs_tensor.cweb -@i ps_tensor.hweb -@i ps_tensor.cweb -@i sparse_tensor.hweb -@i sparse_tensor.cweb - -@*1 The Faa Di Bruno formula. -@i t_container.hweb -@i t_container.cweb -@i stack_container.hweb -@i stack_container.cweb -@i fine_container.hweb -@i fine_container.cweb -@i pyramid_prod.hweb -@i pyramid_prod.cweb -@i pyramid_prod2.hweb -@i pyramid_prod2.cweb - -@*1 Miscellany. -@i t_polynomial.hweb -@i t_polynomial.cweb -@i normal_moments.hweb -@i normal_moments.cweb -@i tl_static.hweb -@i tl_static.cweb - -@*1 Index. \ No newline at end of file diff --git a/dynare++/tl/cc/normal_moments.cc b/dynare++/tl/cc/normal_moments.cc new file mode 100644 index 000000000..82c257a76 --- /dev/null +++ b/dynare++/tl/cc/normal_moments.cc @@ -0,0 +1,103 @@ +// Copyright 2004, Ondra Kamenik + +#include "normal_moments.hh" +#include "permutation.hh" +#include "kron_prod.hh" +#include "tl_static.hh" + +UNormalMoments::UNormalMoments(int maxdim, const TwoDMatrix &v) + : TensorContainer(1) +{ + if (maxdim >= 2) + generateMoments(maxdim, v); +} + +/* Here we fill up the container with the tensors for $d=2,4,6,\ldots$ + up to the given dimension. Each tensor of moments is equal to + $F_n\left(\otimes^nv\right).$ This has a dimension equal to + $2n$. See the header file for proof and details. + + Here we sequentially construct the Kronecker power + $\otimes^nv$, and apply $F_n$. */ + +void +UNormalMoments::generateMoments(int maxdim, const TwoDMatrix &v) +{ + TL_RAISE_IF(v.nrows() != v.ncols(), + "Variance-covariance matrix is not square in UNormalMoments constructor"); + + int nv = v.nrows(); + URSingleTensor *mom2 = new URSingleTensor(nv, 2); + mom2->getData() = v.getData(); + insert(mom2); + URSingleTensor *kronv = new URSingleTensor(nv, 2); + kronv->getData() = v.getData(); + for (int d = 4; d <= maxdim; d += 2) + { + URSingleTensor *newkronv = new URSingleTensor(nv, d); + KronProd::kronMult(ConstVector(v.getData()), + ConstVector(kronv->getData()), + newkronv->getData()); + delete kronv; + kronv = newkronv; + URSingleTensor *mom = new URSingleTensor(nv, d); + // apply $F_n$ to |kronv| + /* Here we go through all equivalences, select only those having 2 + elements in each class, then go through all elements in |kronv| and + add to permuted location of |mom|. + + The permutation must be taken as inverse of the permutation implied by + the equivalence, since we need a permutation which after application + to identity of indices yileds indices in the equivalence classes. Note + how the |Equivalence::apply| method works. */ + mom->zeros(); + const EquivalenceSet eset = ebundle.get(d); + for (EquivalenceSet::const_iterator cit = eset.begin(); + cit != eset.end(); cit++) + { + if (selectEquiv(*cit)) + { + Permutation per(*cit); + per.inverse(); + for (Tensor::index it = kronv->begin(); it != kronv->end(); ++it) + { + IntSequence ind(kronv->dimen()); + per.apply(it.getCoor(), ind); + Tensor::index it2(mom, ind); + mom->get(*it2, 0) += kronv->get(*it, 0); + } + } + } + insert(mom); + } + delete kronv; +} + +/* We return |true| for an equivalence whose each class has 2 elements. */ + +bool +UNormalMoments::selectEquiv(const Equivalence &e) +{ + if (2*e.numClasses() != e.getN()) + return false; + for (Equivalence::const_seqit si = e.begin(); + si != e.end(); ++si) + { + if ((*si).length() != 2) + return false; + } + return true; +} + +/* Here we go through all the unfolded container, fold each tensor and + insert it. */ +FNormalMoments::FNormalMoments(const UNormalMoments &moms) + : TensorContainer(1) +{ + for (UNormalMoments::const_iterator it = moms.begin(); + it != moms.end(); ++it) + { + FRSingleTensor *fm = new FRSingleTensor(*((*it).second)); + insert(fm); + } +} diff --git a/dynare++/tl/cc/normal_moments.cweb b/dynare++/tl/cc/normal_moments.cweb deleted file mode 100644 index 6a7c81cf4..000000000 --- a/dynare++/tl/cc/normal_moments.cweb +++ /dev/null @@ -1,115 +0,0 @@ -@q $Id: normal_moments.cweb 281 2005-06-13 09:41:16Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@ Start of {\tt normal\_moments.cpp} file. - -@c -#include "normal_moments.h" -#include "permutation.h" -#include "kron_prod.h" -#include "tl_static.h" - -@<|UNormalMoments| constructor code@>; -@<|UNormalMoments::generateMoments| code@>; -@<|UNormalMoments::selectEquiv| code@>; -@<|FNormalMoments| constructor code@>; - -@ -@<|UNormalMoments| constructor code@>= -UNormalMoments::UNormalMoments(int maxdim, const TwoDMatrix& v) - : TensorContainer(1) -{ - if (maxdim >= 2) - generateMoments(maxdim, v); -} - - -@ Here we fill up the container with the tensors for $d=2,4,6,\ldots$ -up to the given dimension. Each tensor of moments is equal to -$F_n\left(\otimes^nv\right).$ This has a dimension equal to -$2n$. See the header file for proof and details. - -Here we sequentially construct the Kronecker power -$\otimes^nv$, and apply $F_n$. - -@<|UNormalMoments::generateMoments| code@>= -void UNormalMoments::generateMoments(int maxdim, const TwoDMatrix& v) -{ - TL_RAISE_IF(v.nrows() != v.ncols(), - "Variance-covariance matrix is not square in UNormalMoments constructor"); - - int nv = v.nrows(); - URSingleTensor* mom2 = new URSingleTensor(nv, 2); - mom2->getData() = v.getData(); - insert(mom2); - URSingleTensor* kronv = new URSingleTensor(nv, 2); - kronv->getData() = v.getData(); - for (int d = 4; d <= maxdim; d+=2) { - URSingleTensor* newkronv = new URSingleTensor(nv, d); - KronProd::kronMult(ConstVector(v.getData()), - ConstVector(kronv->getData()), - newkronv->getData()); - delete kronv; - kronv = newkronv; - URSingleTensor* mom = new URSingleTensor(nv, d); - @; - insert(mom); - } - delete kronv; -} - -@ Here we go through all equivalences, select only those having 2 -elements in each class, then go through all elements in |kronv| and -add to permuted location of |mom|. - -The permutation must be taken as inverse of the permutation implied by -the equivalence, since we need a permutation which after application -to identity of indices yileds indices in the equivalence classes. Note -how the |Equivalence::apply| method works. - -@= - mom->zeros(); - const EquivalenceSet eset = ebundle.get(d); - for (EquivalenceSet::const_iterator cit = eset.begin(); - cit != eset.end(); cit++) { - if (selectEquiv(*cit)) { - Permutation per(*cit); - per.inverse(); - for (Tensor::index it = kronv->begin(); it != kronv->end(); ++it) { - IntSequence ind(kronv->dimen()); - per.apply(it.getCoor(), ind); - Tensor::index it2(mom, ind); - mom->get(*it2, 0) += kronv->get(*it, 0); - } - } - } - -@ We return |true| for an equivalence whose each class has 2 elements. -@<|UNormalMoments::selectEquiv| code@>= -bool UNormalMoments::selectEquiv(const Equivalence& e) -{ - if (2*e.numClasses() != e.getN()) - return false; - for (Equivalence::const_seqit si = e.begin(); - si != e.end(); ++si) { - if ((*si).length() != 2) - return false; - } - return true; -} - -@ Here we go through all the unfolded container, fold each tensor and -insert it. -@<|FNormalMoments| constructor code@>= -FNormalMoments::FNormalMoments(const UNormalMoments& moms) - : TensorContainer(1) -{ - for (UNormalMoments::const_iterator it = moms.begin(); - it != moms.end(); ++it) { - FRSingleTensor* fm = new FRSingleTensor(*((*it).second)); - insert(fm); - } -} - - -@ End of {\tt normal\_moments.cpp} file. \ No newline at end of file diff --git a/dynare++/tl/cc/normal_moments.hh b/dynare++/tl/cc/normal_moments.hh new file mode 100644 index 000000000..a2685e4e2 --- /dev/null +++ b/dynare++/tl/cc/normal_moments.hh @@ -0,0 +1,129 @@ +// Copyright 2004, Ondra Kamenik + +// Moments of normal distribution. + +/* Here we calculate the higher order moments of normally distributed + random vector $u$ with means equal to zero and given + variance--covariance matrix $V$, this is $u\sim N(0,V)$. The moment + generating function for such distribution is $f(t)=e^{{1\over 2}t^TVt}$. If + we derivate it wrt $t$ and unfold the higher dimensional tensors + row-wise, we obtain terms like + $$\eqalign{ + {\partial\over\partial t}f(t)=&f(t)\cdot Vt\cr + {\partial^2\over\partial t^2}f(t)=&f(t)\cdot(Vt\otimes Vt+v)\cr + {\partial^3\over\partial t^3}f(t)=&f(t)\cdot + (Vt\otimes Vt\otimes Vt+P_?(v\otimes Vt)+P_?(Vt\otimes v)+v\otimes Vt)\cr + {\partial^4\over\partial t^4}f(t)=&f(t)\cdot + (Vt\otimes Vt\otimes Vt\otimes Vt+S_?(v\otimes Vt\otimes Vt)+ + S_?(Vt\otimes v\otimes Vt)+S_?(Vt\otimes Vt\otimes v)+S_?(v\otimes v))} + $$ + where $v$ is vectorized $V$ ($v=\hbox{vec}(V)$), and $P_?$ is a + suitable row permutation (corresponds to permutation of + multidimensional indices) which permutes the tensor data, so that the + index of a variable being derived would be the last. This ensures that + all (permuted) tensors can be summed yielding a tensor whose indices + have some order (in here we chose the order that more recent + derivating variables are to the right). Finally, $S_?$ is a suitable + sum of various $P_?$. + + We are interested in $S_?$ multiplying the Kronecker powers + $\otimes^nv$. The $S_?$ is a (possibly) multi-set of permutations of + even order. Note that we know a number of permutations in $S_?$. The + above formulas for $F(t)$ derivatives are valid also for monomial + $u$, and from literature we know that $2n$-th moment is ${(2n!)\over + n!2^n}\sigma^2$. So there are ${(2n!)\over n!2^n}$ permutations in + $S_?$. + + In order to find the $S_?$ we need to define a couple of + things. First we define a sort of equivalence between the permutations + applicable to even number of indices. We write $P_1\equiv P_2$ + whenever $P_1^{-1}\circ P_2$ permutes only whole pairs, or items + within pairs, but not indices across the pairs. For instance the + permutations $(0,1,2,3)$ and $(3,2,0,1)$ are equivalent, but + $(0,2,1,3)$ is not equivalent with the two. Clearly, the $\equiv$ is + an equivalence. + + This allows to define a relation $\sqsubseteq$ between the permutation + multi-sets $S$, which is basically the subset relation $\subseteq$ but + with respect to the equivalence $\equiv$, more formally: + $$S_1\sqsubseteq S_2\quad\hbox{iff}\quad P\in S_1 + \Rightarrow\exists Q\in S_2:P\equiv Q$$ + This induces an equivalence $S_1\equiv S_2$. + + Now let $F_n$ denote a set of permutations on $2n$ indices which is + maximal with respect to $\sqsubseteq$, and minimal with respect to + $\equiv$. (In other words, it contains everything up to the + equivalence $\equiv$.) It is straightforward to calculate a number of + permutations in $F_n$. This is a total number of all permutations of + $2n$ divided by permutations of pairs divided by permutations within + the pairs. This is ${(2n!)\over n!2^n}$. + + We prove that $S_?\equiv F_n$. Clearly $S_?\sqsubseteq F_n$, since + $F_n$ is maximal. In order to prove that $F_n\sqsubseteq S_?$, let us + assert that for any permutation $P$ and for any (semi)positive + definite matrix $V$ we have $PS_?\otimes^nv=S_?\otimes^nv$. Below we + show that there is a positive definite matrix $V$ of some dimension + that for any two permutation multi-sets $S_1$, $S_2$, we have + $$S_1\not\equiv S_2\Rightarrow S_1(\otimes^nv)\neq S_2(\otimes^nv)$$ + So it follows that for any permutation $P$, we have $PS_?\equiv + S_?$. For a purpose of contradiction let $P\in F_n$ be a permutation + which is not equivalent to any permutation from $S_?$. Since $S_?$ is + non-empty, let us pick $P_0\in S_?$. Now assert that + $P_0^{-1}S_?\not\equiv P^{-1}S_?$ since the first contains an identity + and the second does not contain a permutation equivalent to + identity. Thus we have $(P\circ P_0^{-1})S_?\not\equiv S_?$ which + gives the contradiction and we have proved that $F_n\sqsubseteq + S_?$. Thus $F_n\equiv S_?$. Moreover, we know that $S_?$ and $F_n$ + have the same number of permutations, hence the minimality of $S_?$ + with respect to $\equiv$. + + Now it suffices to prove that there exists a positive definite $V$ + such that for any two permutation multi-sets $S_1$, and $S_2$ holds + $S_1\not\equiv S_2\Rightarrow S_1(\otimes^nv)\neq S_2(\otimes^nv)$. If + $V$ is $n\times n$ matrix, then $S_1\not\equiv S_2$ implies that there + is identically nonzero polynomial of elements from $V$ of order $n$ + over integers. If $V=A^TA$ then there is identically non-zero + polynomial of elements from $A$ of order $2n$. This means, that we + have to find $n(n+1)/2$ tuple $x$ of real numbers such that all + identically non-zero polynomials $p$ of order $2n$ over integers yield + $p(x)\neq 0$. + + The $x$ is constructed as follows: $x_i = \pi^{\log{r_i}}$, where $r_i$ + is $i$-th prime. Let us consider monom $x_1^{j_1}\cdot\ldots\cdot + x_k^{j_k}$. When the monom is evaluated, we get + $$\pi^{\log{r_1^{j_1}}+\ldots+\log{r_k^{j_k}}}= + \pi^{\log{\left(r_1^{j_1}\cdot\ldots\cdot r_k^{j_k}\right)}}$$ + Now it is easy to see that if an integer combination of such terms is + zero, then the combination must be either trivial or sum to $0$ and + all monoms must be equal. Both cases imply a polynomial identically + equal to zero. So, any non-trivial integer polynomial evaluated at $x$ + must be non-zero. + + So, having this result in hand, now it is straightforward to calculate + higher moments of normal distribution. Here we define a container, + which does the job. In its constructor, we simply calculate Kronecker + powers of $v$ and apply $F_n$ to $\otimes^nv$. $F_n$ is, in fact, a + set of all equivalences in sense of class |Equivalence| over $2n$ + elements, having $n$ classes each of them having exactly 2 elements. */ + +#ifndef NORMAL_MOMENTS_H +#define NORMAL_MOMENTS_H + +#include "t_container.hh" + +class UNormalMoments : public TensorContainer +{ +public: + UNormalMoments(int maxdim, const TwoDMatrix &v); +private: + void generateMoments(int maxdim, const TwoDMatrix &v); + static bool selectEquiv(const Equivalence &e); +}; + +class FNormalMoments : public TensorContainer +{ +public: + FNormalMoments(const UNormalMoments &moms); +}; + +#endif diff --git a/dynare++/tl/cc/normal_moments.hweb b/dynare++/tl/cc/normal_moments.hweb deleted file mode 100644 index 8f23a6bf7..000000000 --- a/dynare++/tl/cc/normal_moments.hweb +++ /dev/null @@ -1,139 +0,0 @@ -@q $Id: normal_moments.hweb 148 2005-04-19 15:12:26Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@*2 Moments of normal distribution. Start of {\tt normal\_moments.h} file. - -Here we calculate the higher order moments of normally distributed -random vector $u$ with means equal to zero and given -variance--covariance matrix $V$, this is $u\sim N(0,V)$. The moment -generating function for such distribution is $f(t)=e^{{1\over 2}t^TVt}$. If -we derivate it wrt $t$ and unfold the higher dimensional tensors -row-wise, we obtain terms like -$$\eqalign{ -{\partial\over\partial t}f(t)=&f(t)\cdot Vt\cr -{\partial^2\over\partial t^2}f(t)=&f(t)\cdot(Vt\otimes Vt+v)\cr -{\partial^3\over\partial t^3}f(t)=&f(t)\cdot - (Vt\otimes Vt\otimes Vt+P_?(v\otimes Vt)+P_?(Vt\otimes v)+v\otimes Vt)\cr -{\partial^4\over\partial t^4}f(t)=&f(t)\cdot - (Vt\otimes Vt\otimes Vt\otimes Vt+S_?(v\otimes Vt\otimes Vt)+ - S_?(Vt\otimes v\otimes Vt)+S_?(Vt\otimes Vt\otimes v)+S_?(v\otimes v))} -$$ -where $v$ is vectorized $V$ ($v=\hbox{vec}(V)$), and $P_?$ is a -suitable row permutation (corresponds to permutation of -multidimensional indices) which permutes the tensor data, so that the -index of a variable being derived would be the last. This ensures that -all (permuted) tensors can be summed yielding a tensor whose indices -have some order (in here we chose the order that more recent -derivating variables are to the right). Finally, $S_?$ is a suitable -sum of various $P_?$. - -We are interested in $S_?$ multiplying the Kronecker powers -$\otimes^nv$. The $S_?$ is a (possibly) multi-set of permutations of -even order. Note that we know a number of permutations in $S_?$. The -above formulas for $F(t)$ derivatives are valid also for monomial -$u$, and from literature we know that $2n$-th moment is ${(2n!)\over -n!2^n}\sigma^2$. So there are ${(2n!)\over n!2^n}$ permutations in -$S_?$. - -In order to find the $S_?$ we need to define a couple of -things. First we define a sort of equivalence between the permutations -applicable to even number of indices. We write $P_1\equiv P_2$ -whenever $P_1^{-1}\circ P_2$ permutes only whole pairs, or items -within pairs, but not indices across the pairs. For instance the -permutations $(0,1,2,3)$ and $(3,2,0,1)$ are equivalent, but -$(0,2,1,3)$ is not equivalent with the two. Clearly, the $\equiv$ is -an equivalence. - -This allows to define a relation $\sqsubseteq$ between the permutation -multi-sets $S$, which is basically the subset relation $\subseteq$ but -with respect to the equivalence $\equiv$, more formally: -$$S_1\sqsubseteq S_2\quad\hbox{iff}\quad P\in S_1 -\Rightarrow\exists Q\in S_2:P\equiv Q$$ -This induces an equivalence $S_1\equiv S_2$. - -Now let $F_n$ denote a set of permutations on $2n$ indices which is -maximal with respect to $\sqsubseteq$, and minimal with respect to -$\equiv$. (In other words, it contains everything up to the -equivalence $\equiv$.) It is straightforward to calculate a number of -permutations in $F_n$. This is a total number of all permutations of -$2n$ divided by permutations of pairs divided by permutations within -the pairs. This is ${(2n!)\over n!2^n}$. - -We prove that $S_?\equiv F_n$. Clearly $S_?\sqsubseteq F_n$, since -$F_n$ is maximal. In order to prove that $F_n\sqsubseteq S_?$, let us -assert that for any permutation $P$ and for any (semi)positive -definite matrix $V$ we have $PS_?\otimes^nv=S_?\otimes^nv$. Below we -show that there is a positive definite matrix $V$ of some dimension -that for any two permutation multi-sets $S_1$, $S_2$, we have -$$S_1\not\equiv S_2\Rightarrow S_1(\otimes^nv)\neq S_2(\otimes^nv)$$ -So it follows that for any permutation $P$, we have $PS_?\equiv -S_?$. For a purpose of contradiction let $P\in F_n$ be a permutation -which is not equivalent to any permutation from $S_?$. Since $S_?$ is -non-empty, let us pick $P_0\in S_?$. Now assert that -$P_0^{-1}S_?\not\equiv P^{-1}S_?$ since the first contains an identity -and the second does not contain a permutation equivalent to -identity. Thus we have $(P\circ P_0^{-1})S_?\not\equiv S_?$ which -gives the contradiction and we have proved that $F_n\sqsubseteq -S_?$. Thus $F_n\equiv S_?$. Moreover, we know that $S_?$ and $F_n$ -have the same number of permutations, hence the minimality of $S_?$ -with respect to $\equiv$. - -Now it suffices to prove that there exists a positive definite $V$ -such that for any two permutation multi-sets $S_1$, and $S_2$ holds -$S_1\not\equiv S_2\Rightarrow S_1(\otimes^nv)\neq S_2(\otimes^nv)$. If -$V$ is $n\times n$ matrix, then $S_1\not\equiv S_2$ implies that there -is identically nonzero polynomial of elements from $V$ of order $n$ -over integers. If $V=A^TA$ then there is identically non-zero -polynomial of elements from $A$ of order $2n$. This means, that we -have to find $n(n+1)/2$ tuple $x$ of real numbers such that all -identically non-zero polynomials $p$ of order $2n$ over integers yield -$p(x)\neq 0$. - -The $x$ is constructed as follows: $x_i = \pi^{\log{r_i}}$, where $r_i$ -is $i$-th prime. Let us consider monom $x_1^{j_1}\cdot\ldots\cdot -x_k^{j_k}$. When the monom is evaluated, we get -$$\pi^{\log{r_1^{j_1}}+\ldots+\log{r_k^{j_k}}}= -\pi^{\log{\left(r_1^{j_1}\cdot\ldots\cdot r_k^{j_k}\right)}}$$ -Now it is easy to see that if an integer combination of such terms is -zero, then the combination must be either trivial or sum to $0$ and -all monoms must be equal. Both cases imply a polynomial identically -equal to zero. So, any non-trivial integer polynomial evaluated at $x$ -must be non-zero. - -So, having this result in hand, now it is straightforward to calculate -higher moments of normal distribution. Here we define a container, -which does the job. In its constructor, we simply calculate Kronecker -powers of $v$ and apply $F_n$ to $\otimes^nv$. $F_n$ is, in fact, a -set of all equivalences in sense of class |Equivalence| over $2n$ -elements, having $n$ classes each of them having exactly 2 elements. - -@c -#ifndef NORMAL_MOMENTS_H -#define NORMAL_MOMENTS_H - -#include "t_container.h" - -@<|UNormalMoments| class declaration@>; -@<|FNormalMoments| class declaration@>; - -#endif - -@ -@<|UNormalMoments| class declaration@>= -class UNormalMoments : public TensorContainer { -public:@; - UNormalMoments(int maxdim, const TwoDMatrix& v); -private:@; - void generateMoments(int maxdim, const TwoDMatrix& v); - static bool selectEquiv( const Equivalence& e); -}; - -@ -@<|FNormalMoments| class declaration@>= -class FNormalMoments : public TensorContainer { -public:@; - FNormalMoments(const UNormalMoments& moms); -}; - - -@ End of {\tt normal\_moments.h} file. \ No newline at end of file diff --git a/dynare++/tl/cc/permutation.cc b/dynare++/tl/cc/permutation.cc new file mode 100644 index 000000000..43d72372c --- /dev/null +++ b/dynare++/tl/cc/permutation.cc @@ -0,0 +1,163 @@ +// Copyright 2004, Ondra Kamenik + +#include "permutation.hh" +#include "tl_exception.hh" + +/* This is easy, we simply apply the map in the fashion $s\circ m$.. */ + +void +Permutation::apply(const IntSequence &src, IntSequence &tar) const +{ + TL_RAISE_IF(src.size() != permap.size() || tar.size() != permap.size(), + "Wrong sizes of input or output in Permutation::apply"); + for (int i = 0; i < permap.size(); i++) + tar[i] = src[permap[i]]; +} + +void +Permutation::apply(IntSequence &tar) const +{ + IntSequence tmp(tar); + apply(tmp, tar); +} + +void +Permutation::inverse() +{ + IntSequence former(permap); + for (int i = 0; i < size(); i++) + permap[former[i]] = i; +} + +/* Here we find a number of trailing indices which are identical with + the permutation. */ + +int +Permutation::tailIdentity() const +{ + int i = permap.size(); + while (i > 0 && permap[i-1] == i-1) + i--; + return permap.size() - i; +} + +/* This calculates a map which corresponds to sorting in the following + sense: $(\hbox{sorted }s)\circ m = s$, where $s$ is a given sequence. + + We go through |s| and find an the same item in sorted |s|. We + construct the |permap| from the found pair of indices. We have to be + careful, to not assign to two positions in |s| the same position in + sorted |s|, so we maintain a bitmap |flag|, in which we remember + indices from the sorted |s| already assigned. */ + +void +Permutation::computeSortingMap(const IntSequence &s) +{ + IntSequence srt(s); + srt.sort(); + IntSequence flags(s.size(), 0); + + for (int i = 0; i < s.size(); i++) + { + int j = 0; + while (j < s.size() && (flags[j] || srt[j] != s[i])) + j++; + TL_RAISE_IF(j == s.size(), + "Internal algorithm error in Permutation::computeSortingMap"); + flags[j] = 1; + permap[i] = j; + } +} + +PermutationSet::PermutationSet() + : order(1), size(1), pers(new const Permutation *[size]) +{ + pers[0] = new Permutation(1); +} + +PermutationSet::PermutationSet(const PermutationSet &sp, int n) + : order(n), size(n*sp.size), + pers(new const Permutation *[size]) +{ + for (int i = 0; i < size; i++) + pers[i] = NULL; + + TL_RAISE_IF(n != sp.order+1, + "Wrong new order in PermutationSet constructor"); + + int k = 0; + for (int i = 0; i < sp.size; i++) + { + for (int j = 0; j < order; j++, k++) + { + pers[k] = new Permutation(*(sp.pers[i]), j); + } + } +} + +PermutationSet::~PermutationSet() +{ + for (int i = 0; i < size; i++) + if (pers[i]) + delete pers[i]; + delete [] pers; +} + +vector +PermutationSet::getPreserving(const IntSequence &s) const +{ + TL_RAISE_IF(s.size() != order, + "Wrong sequence length in PermutationSet::getPreserving"); + + vector res; + IntSequence tmp(s.size()); + for (int i = 0; i < size; i++) + { + pers[i]->apply(s, tmp); + if (s == tmp) + { + res.push_back(pers[i]); + } + } + + return res; +} + +PermutationBundle::PermutationBundle(int nmax) +{ + nmax = max(nmax, 1); + generateUpTo(nmax); +} + +PermutationBundle::~PermutationBundle() +{ + for (unsigned int i = 0; i < bundle.size(); i++) + delete bundle[i]; +} + +const PermutationSet & +PermutationBundle::get(int n) const +{ + if (n > (int) (bundle.size()) || n < 1) + { + TL_RAISE("Permutation set not found in PermutationSet::get"); + return *(bundle[0]); + } + else + { + return *(bundle[n-1]); + } +} + +void +PermutationBundle::generateUpTo(int nmax) +{ + if (bundle.size() == 0) + bundle.push_back(new PermutationSet()); + + int curmax = bundle.size(); + for (int n = curmax+1; n <= nmax; n++) + { + bundle.push_back(new PermutationSet(*(bundle.back()), n)); + } +} diff --git a/dynare++/tl/cc/permutation.cweb b/dynare++/tl/cc/permutation.cweb deleted file mode 100644 index 011c3a543..000000000 --- a/dynare++/tl/cc/permutation.cweb +++ /dev/null @@ -1,188 +0,0 @@ -@q $Id: permutation.cweb 332 2005-07-15 13:41:48Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@ Start of {\tt permutation.cweb} file. -@c - -#include "permutation.h" -#include "tl_exception.h" - -@<|Permutation::apply| code@>; -@<|Permutation::inverse| code@>; -@<|Permutation::tailIdentity| code@>; -@<|Permutation::computeSortingMap| code@>; -@<|PermutationSet| constructor code 1@>; -@<|PermutationSet| constructor code 2@>; -@<|PermutationSet| destructor code@>; -@<|PermutationSet::getPreserving| code@>; -@<|PermutationBundle| constructor code@>; -@<|PermutationBundle| destructor code@>; -@<|PermutationBundle::get| code@>; -@<|PermutationBundle::generateUpTo| code@>; - - -@ This is easy, we simply apply the map in the fashion $s\circ m$.. -@<|Permutation::apply| code@>= -void Permutation::apply(const IntSequence& src, IntSequence& tar) const -{ - TL_RAISE_IF(src.size() != permap.size() || tar.size() != permap.size(), - "Wrong sizes of input or output in Permutation::apply"); - for (int i = 0; i < permap.size(); i++) - tar[i] = src[permap[i]]; -} - - -void Permutation::apply(IntSequence& tar) const -{ - IntSequence tmp(tar); - apply(tmp, tar); -} - -@ -@<|Permutation::inverse| code@>= -void Permutation::inverse() -{ - IntSequence former(permap); - for (int i = 0; i < size(); i++) - permap[former[i]] = i; -} - - -@ Here we find a number of trailing indices which are identical with -the permutation. - -@<|Permutation::tailIdentity| code@>= -int Permutation::tailIdentity() const -{ - int i = permap.size(); - while (i > 0 && permap[i-1] == i-1) - i--; - return permap.size() - i; -} - -@ This calculates a map which corresponds to sorting in the following -sense: $(\hbox{sorted }s)\circ m = s$, where $s$ is a given sequence. - -We go through |s| and find an the same item in sorted |s|. We -construct the |permap| from the found pair of indices. We have to be -careful, to not assign to two positions in |s| the same position in -sorted |s|, so we maintain a bitmap |flag|, in which we remember -indices from the sorted |s| already assigned. - -@<|Permutation::computeSortingMap| code@>= -void Permutation::computeSortingMap(const IntSequence& s) -{ - IntSequence srt(s); - srt.sort(); - IntSequence flags(s.size(),0); - - for (int i = 0; i < s.size(); i++) { - int j = 0; - while (j < s.size() && (flags[j] || srt[j] != s[i])) - j++; - TL_RAISE_IF(j == s.size(), - "Internal algorithm error in Permutation::computeSortingMap"); - flags[j] = 1; - permap[i] = j; - } -} - -@ -@<|PermutationSet| constructor code 1@>= -PermutationSet::PermutationSet() - : order(1), size(1), pers(new const Permutation*[size]) -{ - pers[0] = new Permutation(1); -} - -@ -@<|PermutationSet| constructor code 2@>= -PermutationSet::PermutationSet(const PermutationSet& sp, int n) - : order(n), size(n*sp.size), - pers(new const Permutation*[size]) -{ - for (int i = 0; i < size; i++) - pers[i] = NULL; - - TL_RAISE_IF(n != sp.order+1, - "Wrong new order in PermutationSet constructor"); - - int k = 0; - for (int i = 0; i < sp.size; i++) { - for (int j = 0; j < order; j++,k++) { - pers[k] = new Permutation(*(sp.pers[i]), j); - } - } -} - -@ -@<|PermutationSet| destructor code@>= -PermutationSet::~PermutationSet() -{ - for (int i = 0; i < size; i++) - if (pers[i]) - delete pers[i]; - delete [] pers; -} - -@ -@<|PermutationSet::getPreserving| code@>= -vector PermutationSet::getPreserving(const IntSequence& s) const -{ - TL_RAISE_IF(s.size() != order, - "Wrong sequence length in PermutationSet::getPreserving"); - - vector res; - IntSequence tmp(s.size()); - for (int i = 0; i < size; i++) { - pers[i]->apply(s, tmp); - if (s == tmp) { - res.push_back(pers[i]); - } - } - - return res; -} - -@ -@<|PermutationBundle| constructor code@>= -PermutationBundle::PermutationBundle(int nmax) -{ - nmax = max(nmax, 1); - generateUpTo(nmax); -} - -@ -@<|PermutationBundle| destructor code@>= -PermutationBundle::~PermutationBundle() -{ - for (unsigned int i = 0; i < bundle.size(); i++) - delete bundle[i]; -} - -@ -@<|PermutationBundle::get| code@>= -const PermutationSet& PermutationBundle::get(int n) const -{ - if (n > (int)(bundle.size()) || n < 1) { - TL_RAISE("Permutation set not found in PermutationSet::get"); - return *(bundle[0]); - } else { - return *(bundle[n-1]); - } -} - -@ -@<|PermutationBundle::generateUpTo| code@>= -void PermutationBundle::generateUpTo(int nmax) -{ - if (bundle.size() == 0) - bundle.push_back(new PermutationSet()); - - int curmax = bundle.size(); - for (int n = curmax+1; n <= nmax; n++) { - bundle.push_back(new PermutationSet(*(bundle.back()), n)); - } -} - -@ End of {\tt permutation.cweb} file. diff --git a/dynare++/tl/cc/permutation.hh b/dynare++/tl/cc/permutation.hh new file mode 100644 index 000000000..8a684ffb0 --- /dev/null +++ b/dynare++/tl/cc/permutation.hh @@ -0,0 +1,177 @@ +// Copyright 2004, Ondra Kamenik + +// Permutations. + +/* The permutation class is useful when describing a permutation of + indices in permuted symmetry tensor. This tensor comes to existence, + for instance, as a result of the following tensor multiplication: + $$\left[g_{y^3}\right]_{\gamma_1\gamma_2\gamma_3} + \left[g_{yu}\right]^{\gamma_1}_{\alpha_1\beta_3} + \left[g_{yu}\right]^{\gamma_2}_{\alpha_2\beta_1} + \left[g_u\right]^{\gamma_3}_{\beta_2} + $$ + If this operation is done by a Kronecker product of unfolded tensors, + the resulting tensor has permuted indices. So, in this case the + permutation is implied by the equivalence: + $\{\{0,4\},\{1,3\},\{2\}\}$. This results in a permutation which maps + indices $(0,1,2,3,4)\mapsto(0,2,4,3,1)$. + + The other application of |Permutation| class is to permute indices + with the same permutation as done during sorting. + + Here we only define an abstraction for the permutation defined by an + equivalence. Its basic operation is to apply the permutation to the + integer sequence. The application is right (or inner), in sense that + it works on indices of the sequence not items of the sequence. More + formally $s\circ m \not=m\circ s$. In here, the application of the + permutation defined by map $m$ is $s\circ m$. + + Also, we need |PermutationSet| class which contains all permutations + of $n$ element set, and a bundle of permutations |PermutationBundle| + which contains all permutation sets up to a given number. */ + +#ifndef PERMUTATION_H +#define PERMUTATION_H + +#include "int_sequence.hh" +#include "equivalence.hh" + +#include + +/* The permutation object will have a map, which defines mapping of + indices $(0,1,\ldots,n-1)\mapsto(m_0,m_1,\ldots, m_{n-1})$. The map is + the sequence $(m_0,m_1,\ldots, m_{n-1}$. When the permutation with the + map $m$ is applied on sequence $s$, it permutes its indices: + $s\circ\hbox{id}\mapsto s\circ m$. + + So we have one constructor from equivalence, then a method |apply|, + and finally a method |tailIdentity| which returns a number of trailing + indices which yield identity. Also we have a constructor calculating + map, which corresponds to permutation in sort. This is, we want + $(\hbox{sorted }s)\circ m = s$. */ + +class Permutation +{ +protected: + IntSequence permap; +public: + Permutation(int len) + : permap(len) + { + for (int i = 0; i < len; i++) + permap[i] = i; + } + Permutation(const Equivalence &e) + : permap(e.getN()) + { + e.trace(permap); + } + Permutation(const Equivalence &e, const Permutation &per) + : permap(e.getN()) + { + e.trace(permap, per); + } + Permutation(const IntSequence &s) + : permap(s.size()) + { + computeSortingMap(s); + }; + Permutation(const Permutation &p) + : permap(p.permap) + { + } + Permutation(const Permutation &p1, const Permutation &p2) + : permap(p2.permap) + { + p1.apply(permap); + } + Permutation(const Permutation &p, int i) + : permap(p.size(), p.permap, i) + { + } + const Permutation & + operator=(const Permutation &p) + { + permap = p.permap; return *this; + } + bool + operator==(const Permutation &p) + { + return permap == p.permap; + } + int + size() const + { + return permap.size(); + } + void + print() const + { + permap.print(); + } + void apply(const IntSequence &src, IntSequence &tar) const; + void apply(IntSequence &tar) const; + void inverse(); + int tailIdentity() const; + const IntSequence & + getMap() const + { + return permap; + } + IntSequence & + getMap() + { + return permap; + } +protected: + void computeSortingMap(const IntSequence &s); +}; + +/* The |PermutationSet| maintains an array of of all permutations. The + default constructor constructs one element permutation set of one + element sets. The second constructor constructs a new permutation set + over $n$ from all permutations over $n-1$. The parameter $n$ need not + to be provided, but it serves to distinguish the constructor from copy + constructor, which is not provided. + + The method |getPreserving| returns a factor subgroup of permutations, + which are invariants with respect to the given sequence. This are all + permutations $p$ yielding $p\circ s = s$, where $s$ is the given + sequence. */ + +class PermutationSet +{ + int order; + int size; + const Permutation **const pers; +public: + PermutationSet(); + PermutationSet(const PermutationSet &ps, int n); + ~PermutationSet(); + int + getNum() const + { + return size; + } + const Permutation & + get(int i) const + { + return *(pers[i]); + } + vector getPreserving(const IntSequence &s) const; +}; + +/* The permutation bundle encapsulates all permutations sets up to some + given dimension. */ + +class PermutationBundle +{ + vector bundle; +public: + PermutationBundle(int nmax); + ~PermutationBundle(); + const PermutationSet&get(int n) const; + void generateUpTo(int nmax); +}; + +#endif diff --git a/dynare++/tl/cc/permutation.hweb b/dynare++/tl/cc/permutation.hweb deleted file mode 100644 index b084ffec2..000000000 --- a/dynare++/tl/cc/permutation.hweb +++ /dev/null @@ -1,147 +0,0 @@ -@q $Id: permutation.hweb 148 2005-04-19 15:12:26Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@*2 Permutations. Start of {\tt permutation.h} file. - -The permutation class is useful when describing a permutation of -indices in permuted symmetry tensor. This tensor comes to existence, -for instance, as a result of the following tensor multiplication: -$$\left[g_{y^3}\right]_{\gamma_1\gamma_2\gamma_3} -\left[g_{yu}\right]^{\gamma_1}_{\alpha_1\beta_3} -\left[g_{yu}\right]^{\gamma_2}_{\alpha_2\beta_1} -\left[g_u\right]^{\gamma_3}_{\beta_2} -$$ -If this operation is done by a Kronecker product of unfolded tensors, -the resulting tensor has permuted indices. So, in this case the -permutation is implied by the equivalence: -$\{\{0,4\},\{1,3\},\{2\}\}$. This results in a permutation which maps -indices $(0,1,2,3,4)\mapsto(0,2,4,3,1)$. - -The other application of |Permutation| class is to permute indices -with the same permutation as done during sorting. - -Here we only define an abstraction for the permutation defined by an -equivalence. Its basic operation is to apply the permutation to the -integer sequence. The application is right (or inner), in sense that -it works on indices of the sequence not items of the sequence. More -formally $s\circ m \not=m\circ s$. In here, the application of the -permutation defined by map $m$ is $s\circ m$. - -Also, we need |PermutationSet| class which contains all permutations -of $n$ element set, and a bundle of permutations |PermutationBundle| -which contains all permutation sets up to a given number. - -@s Permutation int -@s PermutationSet int -@s PermutationBundle int - -@c -#ifndef PERMUTATION_H -#define PERMUTATION_H - -#include "int_sequence.h" -#include "equivalence.h" - -#include - -@<|Permutation| class declaration@>; -@<|PermutationSet| class declaration@>; -@<|PermutationBundle| class declaration@>; - -#endif - -@ The permutation object will have a map, which defines mapping of -indices $(0,1,\ldots,n-1)\mapsto(m_0,m_1,\ldots, m_{n-1})$. The map is -the sequence $(m_0,m_1,\ldots, m_{n-1}$. When the permutation with the -map $m$ is applied on sequence $s$, it permutes its indices: -$s\circ\hbox{id}\mapsto s\circ m$. - -So we have one constructor from equivalence, then a method |apply|, -and finally a method |tailIdentity| which returns a number of trailing -indices which yield identity. Also we have a constructor calculating -map, which corresponds to permutation in sort. This is, we want -$(\hbox{sorted }s)\circ m = s$. - -@<|Permutation| class declaration@>= -class Permutation { -protected:@; - IntSequence permap; -public:@; - Permutation(int len) - : permap(len) {@+ for (int i = 0; i < len; i++) permap[i] = i;@+} - Permutation(const Equivalence& e) - : permap(e.getN()) {@+ e.trace(permap);@+} - Permutation(const Equivalence& e, const Permutation& per) - : permap(e.getN()) {@+ e.trace(permap, per);@+} - Permutation(const IntSequence& s) - : permap(s.size()) {@+ computeSortingMap(s);@+}; - Permutation(const Permutation& p) - : permap(p.permap)@+ {} - Permutation(const Permutation& p1, const Permutation& p2) - : permap(p2.permap) {@+ p1.apply(permap);@+} - Permutation(const Permutation& p, int i) - : permap(p.size(), p.permap, i)@+ {} - const Permutation& operator=(const Permutation& p) - {@+ permap = p.permap;@+ return *this;@+} - bool operator==(const Permutation& p) - {@+ return permap == p.permap;@+} - int size() const - {@+ return permap.size();@+} - void print() const - {@+ permap.print();@+} - void apply(const IntSequence& src, IntSequence& tar) const; - void apply(IntSequence& tar) const; - void inverse(); - int tailIdentity() const; - const IntSequence& getMap() const - {@+ return permap;@+} - IntSequence& getMap() - {@+ return permap;@+} -protected:@; - void computeSortingMap(const IntSequence& s); -}; - - -@ The |PermutationSet| maintains an array of of all permutations. The -default constructor constructs one element permutation set of one -element sets. The second constructor constructs a new permutation set -over $n$ from all permutations over $n-1$. The parameter $n$ need not -to be provided, but it serves to distinguish the constructor from copy -constructor, which is not provided. - -The method |getPreserving| returns a factor subgroup of permutations, -which are invariants with respect to the given sequence. This are all -permutations $p$ yielding $p\circ s = s$, where $s$ is the given -sequence. - -@<|PermutationSet| class declaration@>= -class PermutationSet { - int order; - int size; - const Permutation** const pers; -public:@; - PermutationSet(); - PermutationSet(const PermutationSet& ps, int n); - ~PermutationSet(); - int getNum() const - {@+ return size;@+} - const Permutation& get(int i) const - {@+ return *(pers[i]);@+} - vector getPreserving(const IntSequence& s) const; -}; - - -@ The permutation bundle encapsulates all permutations sets up to some -given dimension. - -@<|PermutationBundle| class declaration@>= -class PermutationBundle { - vector bundle; -public:@; - PermutationBundle(int nmax); - ~PermutationBundle(); - const PermutationSet& get(int n) const; - void generateUpTo(int nmax); -}; - -@ End of {\tt permutation.h} file. \ No newline at end of file diff --git a/dynare++/tl/cc/ps_tensor.cc b/dynare++/tl/cc/ps_tensor.cc new file mode 100644 index 000000000..3d191d0b3 --- /dev/null +++ b/dynare++/tl/cc/ps_tensor.cc @@ -0,0 +1,399 @@ +// Copyright 2004, Ondra Kamenik + +#include "ps_tensor.hh" +#include "fs_tensor.hh" +#include "tl_exception.hh" +#include "tl_static.hh" +#include "stack_container.hh" + +/* Here we decide, what method for filling a slice in slicing + constructor to use. A few experiments suggest, that if the tensor is + more than 8\% filled, the first method (|fillFromSparseOne|) is + better. For fill factors less than 1\%, the second can be 3 times + quicker. */ + +UPSTensor::fill_method +UPSTensor::decideFillMethod(const FSSparseTensor &t) +{ + if (t.getFillFactor() > 0.08) + return first; + else + return second; +} + +/* Here we make a slice. We decide what fill method to use and set it. */ + +UPSTensor::UPSTensor(const FSSparseTensor &t, const IntSequence &ss, + const IntSequence &coor, const PerTensorDimens &ptd) + : UTensor(along_col, ptd.getNVX(), + t.nrows(), ptd.calcUnfoldMaxOffset(), ptd.dimen()), + tdims(ptd) +{ + TL_RAISE_IF(coor.size() != t.dimen(), + "Wrong coordinates length of stacks for UPSTensor slicing constructor"); + TL_RAISE_IF(ss.sum() != t.nvar(), + "Wrong length of stacks for UPSTensor slicing constructor"); + + if (first == decideFillMethod(t)) + fillFromSparseOne(t, ss, coor); + else + fillFromSparseTwo(t, ss, coor); +} + +void +UPSTensor::increment(IntSequence &v) const +{ + TL_RAISE_IF(v.size() != dimen(), + "Wrong input/output vector size in UPSTensor::increment"); + + UTensor::increment(v, tdims.getNVX()); +} + +void +UPSTensor::decrement(IntSequence &v) const +{ + TL_RAISE_IF(v.size() != dimen(), + "Wrong input/output vector size in UPSTensor::decrement"); + + UTensor::decrement(v, tdims.getNVX()); +} + +FTensor & +UPSTensor::fold() const +{ + TL_RAISE("Never should come to this place in UPSTensor::fold"); + FFSTensor *nothing = new FFSTensor(0, 0, 0); + return *nothing; +} + +int +UPSTensor::getOffset(const IntSequence &v) const +{ + TL_RAISE_IF(v.size() != dimen(), + "Wrong input vector size in UPSTensor::getOffset"); + + return UTensor::getOffset(v, tdims.getNVX()); +} + +void +UPSTensor::addTo(FGSTensor &out) const +{ + TL_RAISE_IF(out.getDims() != tdims, + "Tensors have incompatible dimens in UPSTensor::addTo"); + for (index in = out.begin(); in != out.end(); ++in) + { + IntSequence vtmp(dimen()); + tdims.getPer().apply(in.getCoor(), vtmp); + index tin(this, vtmp); + out.addColumn(*this, *tin, *in); + } +} + +/* In here, we have to add this permuted symmetry unfolded tensor to an + unfolded not permuted tensor. One easy way would be to go through the + target tensor, permute each index, and add the column. + + However, it may happen, that the permutation has some non-empty + identity tail. In this case, we can add not only individual columns, + but much bigger data chunks, which is usually more + efficient. Therefore, the code is quite dirty, because we have not an + iterator, which iterates over tensor at some higher levels. So we + simulate it by the following code. + + First we set |cols| to the length of the data chunk and |off| to its + dimension. Then we need a front part of |nvmax| of |out|, which is + |nvmax_part|. Our iterator here is an integer sequence |outrun| with + full length, and |outrun_part| its front part. The |outrun| is + initialized to zeros. In each step we need to increment |outrun| + |cols|-times, this is done by incrementing its prefix |outrun_part|. + + So we loop over all |cols|wide partitions of |out|, permute |outrun| + to obtain |perrun| to obtain column of this matrix. (note that the + trailing part of |perrun| is the same as of |outrun|. Then we + construct submatrices, add them, and increment |outrun|. */ + +void +UPSTensor::addTo(UGSTensor &out) const +{ + TL_RAISE_IF(out.getDims() != tdims, + "Tensors have incompatible dimens in UPSTensor::addTo"); + int cols = tailIdentitySize(); + int off = tdims.tailIdentity(); + IntSequence outrun(out.dimen(), 0); + IntSequence outrun_part(outrun, 0, out.dimen()-off); + IntSequence nvmax_part(out.getDims().getNVX(), 0, out.dimen()-off); + for (int out_col = 0; out_col < out.ncols(); out_col += cols) + { + // permute |outrun| + IntSequence perrun(out.dimen()); + tdims.getPer().apply(outrun, perrun); + index from(this, perrun); + // construct submatrices + ConstTwoDMatrix subfrom(*this, *from, cols); + TwoDMatrix subout(out, out_col, cols); + // add + subout.add(1, subfrom); + // increment |outrun| by cols + UTensor::increment(outrun_part, nvmax_part); + } +} + +/* This returns a product of all items in |nvmax| which make up the + trailing identity part. */ + +int +UPSTensor::tailIdentitySize() const +{ + return tdims.getNVX().mult(dimen()-tdims.tailIdentity(), dimen()); +} + +/* This fill method is pretty dumb. We go through all columns in |this| + tensor, translate coordinates to sparse tensor, sort them and find an + item in the sparse tensor. There are many not successful lookups for + really sparse tensor, that is why the second method works better for + really sparse tensors. */ + +void +UPSTensor::fillFromSparseOne(const FSSparseTensor &t, const IntSequence &ss, + const IntSequence &coor) +{ + IntSequence cumtmp(ss.size()); + cumtmp[0] = 0; + for (int i = 1; i < ss.size(); i++) + cumtmp[i] = cumtmp[i-1] + ss[i-1]; + IntSequence cum(coor.size()); + for (int i = 0; i < coor.size(); i++) + cum[i] = cumtmp[coor[i]]; + + zeros(); + for (Tensor::index run = begin(); run != end(); ++run) + { + IntSequence c(run.getCoor()); + c.add(1, cum); + c.sort(); + FSSparseTensor::const_iterator sl = t.getMap().lower_bound(c); + if (sl != t.getMap().end()) + { + FSSparseTensor::const_iterator su = t.getMap().upper_bound(c); + for (FSSparseTensor::const_iterator srun = sl; srun != su; ++srun) + get((*srun).second.first, *run) = (*srun).second.second; + } + } +} + +/* This is the second way of filling the slice. For instance, let the + slice correspond to partitions $abac$. In here we first calculate + lower and upper bounds for index of the sparse tensor for the + slice. These are |lb_srt| and |ub_srt| respectively. They corresponds + to ordering $aabc$. Then we go through that interval, and select items + which are really between the bounds. Then we take the index, subtract + the lower bound to get it to coordinates of the slice. We get + something like $(i_a,j_a,k_b,l_c)$. Then we apply the inverse + permutation as of the sorting form $abac\mapsto aabc$ to get index + $(i_a,k_b,j_a,l_c)$. Recall that the slice is unfolded, so we have to + apply all permutations preserving the stack coordinates $abac$. In our + case we get list of indices $(i_a,k_b,j_a,l_c)$ and + $(j_a,k_b,i_a,l_c)$. For all we copy the item of the sparse tensor to + the appropriate column. */ + +void +UPSTensor::fillFromSparseTwo(const FSSparseTensor &t, const IntSequence &ss, + const IntSequence &coor) +{ + IntSequence coor_srt(coor); + coor_srt.sort(); + IntSequence cum(ss.size()); + cum[0] = 0; + for (int i = 1; i < ss.size(); i++) + cum[i] = cum[i-1] + ss[i-1]; + IntSequence lb_srt(coor.size()); + IntSequence ub_srt(coor.size()); + for (int i = 0; i < coor.size(); i++) + { + lb_srt[i] = cum[coor_srt[i]]; + ub_srt[i] = cum[coor_srt[i]] + ss[coor_srt[i]] - 1; + } + + const PermutationSet &pset = tls.pbundle->get(coor.size()); + vector pp = pset.getPreserving(coor); + + Permutation unsort(coor); + zeros(); + FSSparseTensor::const_iterator lbi = t.getMap().lower_bound(lb_srt); + FSSparseTensor::const_iterator ubi = t.getMap().upper_bound(ub_srt); + for (FSSparseTensor::const_iterator run = lbi; run != ubi; ++run) + { + if (lb_srt.lessEq((*run).first) && (*run).first.lessEq(ub_srt)) + { + IntSequence c((*run).first); + c.add(-1, lb_srt); + unsort.apply(c); + for (unsigned int i = 0; i < pp.size(); i++) + { + IntSequence cp(coor.size()); + pp[i]->apply(c, cp); + Tensor::index ind(this, cp); + TL_RAISE_IF(*ind < 0 || *ind >= ncols(), + "Internal error in slicing constructor of UPSTensor"); + get((*run).second.first, *ind) = (*run).second.second; + } + } + } +} + +/* Here we calculate the maximum offsets in each folded dimension + (dimension sizes, hence |ds|). */ + +void +PerTensorDimens2::setDimensionSizes() +{ + const IntSequence &nvs = getNVS(); + for (int i = 0; i < numSyms(); i++) + { + TensorDimens td(syms[i], nvs); + ds[i] = td.calcFoldMaxOffset(); + } +} + +/* If there are two folded dimensions, the offset in such a dimension + is offset of the second plus offset of the first times the maximum + offset of the second. If there are $n+1$ dimensions, the offset is a + sum of offsets of the last dimension plus the offset in the first $n$ + dimensions multiplied by the maximum offset of the last + dimension. This is exactly what the following code does. */ + +int +PerTensorDimens2::calcOffset(const IntSequence &coor) const +{ + TL_RAISE_IF(coor.size() != dimen(), + "Wrong length of coordinates in PerTensorDimens2::calcOffset"); + IntSequence cc(coor); + int ret = 0; + int off = 0; + for (int i = 0; i < numSyms(); i++) + { + TensorDimens td(syms[i], getNVS()); + IntSequence c(cc, off, off+syms[i].dimen()); + int a = td.calcFoldOffset(c); + ret = ret*ds[i] + a; + off += syms[i].dimen(); + } + return ret; +} + +void +PerTensorDimens2::print() const +{ + printf("nvmax: "); nvmax.print(); + printf("per: "); per.print(); + printf("syms: "); syms.print(); + printf("dims: "); ds.print(); +} + +/* Here we increment the given integer sequence. It corresponds to + |UTensor::increment| of the whole sequence, and then partial + monotonizing of the subsequences with respect to the + symmetries of each dimension. */ + +void +FPSTensor::increment(IntSequence &v) const +{ + TL_RAISE_IF(v.size() != dimen(), + "Wrong length of coordinates in FPSTensor::increment"); + UTensor::increment(v, tdims.getNVX()); + int off = 0; + for (int i = 0; i < tdims.numSyms(); i++) + { + IntSequence c(v, off, off+tdims.getSym(i).dimen()); + c.pmonotone(tdims.getSym(i)); + off += tdims.getSym(i).dimen(); + } +} + +void +FPSTensor::decrement(IntSequence &v) const +{ + TL_RAISE("FPSTensor::decrement not implemented"); +} + +UTensor & +FPSTensor::unfold() const +{ + TL_RAISE("Unfolding of FPSTensor not implemented"); + UFSTensor *nothing = new UFSTensor(0, 0, 0); + return *nothing; +} + +/* We only call |calcOffset| of the |PerTensorDimens2|. */ + +int +FPSTensor::getOffset(const IntSequence &v) const +{ + return tdims.calcOffset(v); +} + +/* Here we add the tensor to |out|. We go through all columns of the + |out|, apply the permutation to get index in the tensor, and add the + column. Note that if the permutation is identity, then the dimensions + of the tensors might not be the same (since this tensor is partially + folded). */ + +void +FPSTensor::addTo(FGSTensor &out) const +{ + for (index tar = out.begin(); tar != out.end(); ++tar) + { + IntSequence coor(dimen()); + tdims.getPer().apply(tar.getCoor(), coor); + index src(this, coor); + out.addColumn(*this, *src, *tar); + } +} + +/* Here is the constructor which multiplies the Kronecker product with + the general symmetry sparse tensor |GSSparseTensor|. The main idea is + to go through items in the sparse tensor (each item selects rows in + the matrices form the Kornecker product), then to Kronecker multiply + the rows and multiply with the item, and to add the resulting row to + the appropriate row of the resulting |FPSTensor|. + + The realization of this idea is a bit more complicated since we have + to go through all items, and each item must be added as many times as + it has its symmetric elements. Moreover, the permutations shuffle + order of rows in their Kronecker product. + + So, we through all unfolded indices in a tensor with the same + dimensions as the |GSSparseTensor| (sparse slice). For each such index + we calculate its folded version (corresponds to ordering of + subsequences within symmetries), we test if there is an item in the + sparse slice with such coordinates, and if there is, we construct the + Kronecker product of the rows, and go through all of items with the + coordinates, and add to appropriate rows of |this| tensor. */ + +FPSTensor::FPSTensor(const TensorDimens &td, const Equivalence &e, const Permutation &p, + const GSSparseTensor &a, const KronProdAll &kp) + : FTensor(along_col, PerTensorDimens(td, Permutation(e, p)).getNVX(), + a.nrows(), kp.ncols(), td.dimen()), + tdims(td, e, p) +{ + zeros(); + + UGSTensor dummy(0, a.getDims()); + for (Tensor::index run = dummy.begin(); run != dummy.end(); ++run) + { + Tensor::index fold_ind = dummy.getFirstIndexOf(run); + const IntSequence &c = fold_ind.getCoor(); + GSSparseTensor::const_iterator sl = a.getMap().lower_bound(c); + if (sl != a.getMap().end()) + { + Vector *row_prod = kp.multRows(run.getCoor()); + GSSparseTensor::const_iterator su = a.getMap().upper_bound(c); + for (GSSparseTensor::const_iterator srun = sl; srun != su; ++srun) + { + Vector out_row((*srun).second.first, *this); + out_row.add((*srun).second.second, *row_prod); + } + delete row_prod; + } + } +} diff --git a/dynare++/tl/cc/ps_tensor.cweb b/dynare++/tl/cc/ps_tensor.cweb deleted file mode 100644 index 3857adf81..000000000 --- a/dynare++/tl/cc/ps_tensor.cweb +++ /dev/null @@ -1,422 +0,0 @@ -@q $Id: ps_tensor.cweb 148 2005-04-19 15:12:26Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@ Start of {\tt ps\_tensor.cpp} file. -@c -#include "ps_tensor.h" -#include "fs_tensor.h" -#include "tl_exception.h" -#include "tl_static.h" -#include "stack_container.h" - -@<|UPSTensor::decideFillMethod| code@>; -@<|UPSTensor| slicing constructor code@>; -@<|UPSTensor| increment and decrement@>; -@<|UPSTensor::fold| code@>; -@<|UPSTensor::getOffset| code@>; -@<|UPSTensor::addTo| folded code@>; -@<|UPSTensor::addTo| unfolded code@>; -@<|UPSTensor::tailIdentitySize| code@>; -@<|UPSTensor::fillFromSparseOne| code@>; -@<|UPSTensor::fillFromSparseTwo| code@>; -@<|PerTensorDimens2::setDimensionSizes| code@>; -@<|PerTensorDimens2::calcOffset| code@>; -@<|PerTensorDimens2::print| code@>; -@<|FPSTensor::increment| code@>; -@<|FPSTensor::decrement| code@>; -@<|FPSTensor::unfold| code@>; -@<|FPSTensor::getOffset| code@>; -@<|FPSTensor::addTo| code@>; -@<|FPSTensor| sparse constructor@>; - -@ Here we decide, what method for filling a slice in slicing -constructor to use. A few experiments suggest, that if the tensor is -more than 8\% filled, the first method (|fillFromSparseOne|) is -better. For fill factors less than 1\%, the second can be 3 times -quicker. - -@<|UPSTensor::decideFillMethod| code@>= -UPSTensor::fill_method UPSTensor::decideFillMethod(const FSSparseTensor& t) -{ - if (t.getFillFactor() > 0.08) - return first; - else - return second; -} - -@ Here we make a slice. We decide what fill method to use and set it. - -@<|UPSTensor| slicing constructor code@>= -UPSTensor::UPSTensor(const FSSparseTensor& t, const IntSequence& ss, - const IntSequence& coor, const PerTensorDimens& ptd) - : UTensor(along_col, ptd.getNVX(), - t.nrows(), ptd.calcUnfoldMaxOffset(), ptd.dimen()), - tdims(ptd) -{ - TL_RAISE_IF(coor.size() != t.dimen(), - "Wrong coordinates length of stacks for UPSTensor slicing constructor"); - TL_RAISE_IF(ss.sum() != t.nvar(), - "Wrong length of stacks for UPSTensor slicing constructor"); - - if (first == decideFillMethod(t)) - fillFromSparseOne(t, ss, coor); - else - fillFromSparseTwo(t, ss, coor); -} - - -@ -@<|UPSTensor| increment and decrement@>= -void UPSTensor::increment(IntSequence& v) const -{ - TL_RAISE_IF(v.size() != dimen(), - "Wrong input/output vector size in UPSTensor::increment"); - - UTensor::increment(v, tdims.getNVX()); -} - -void UPSTensor::decrement(IntSequence& v) const -{ - TL_RAISE_IF(v.size() != dimen(), - "Wrong input/output vector size in UPSTensor::decrement"); - - UTensor::decrement(v, tdims.getNVX()); -} - -@ -@<|UPSTensor::fold| code@>= -FTensor& UPSTensor::fold() const -{ - TL_RAISE("Never should come to this place in UPSTensor::fold"); - FFSTensor* nothing = new FFSTensor(0,0,0); - return *nothing; -} - - -@ -@<|UPSTensor::getOffset| code@>= -int UPSTensor::getOffset(const IntSequence& v) const -{ - TL_RAISE_IF(v.size() != dimen(), - "Wrong input vector size in UPSTensor::getOffset"); - - return UTensor::getOffset(v, tdims.getNVX()); -} - -@ -@<|UPSTensor::addTo| folded code@>= -void UPSTensor::addTo(FGSTensor& out) const -{ - TL_RAISE_IF(out.getDims() != tdims, - "Tensors have incompatible dimens in UPSTensor::addTo"); - for (index in = out.begin(); in != out.end(); ++in) { - IntSequence vtmp(dimen()); - tdims.getPer().apply(in.getCoor(), vtmp); - index tin(this, vtmp); - out.addColumn(*this, *tin, *in); - } -} - -@ In here, we have to add this permuted symmetry unfolded tensor to an -unfolded not permuted tensor. One easy way would be to go through the -target tensor, permute each index, and add the column. - -However, it may happen, that the permutation has some non-empty -identity tail. In this case, we can add not only individual columns, -but much bigger data chunks, which is usually more -efficient. Therefore, the code is quite dirty, because we have not an -iterator, which iterates over tensor at some higher levels. So we -simulate it by the following code. - -First we set |cols| to the length of the data chunk and |off| to its -dimension. Then we need a front part of |nvmax| of |out|, which is -|nvmax_part|. Our iterator here is an integer sequence |outrun| with -full length, and |outrun_part| its front part. The |outrun| is -initialized to zeros. In each step we need to increment |outrun| -|cols|-times, this is done by incrementing its prefix |outrun_part|. - -So we loop over all |cols|wide partitions of |out|, permute |outrun| -to obtain |perrun| to obtain column of this matrix. (note that the -trailing part of |perrun| is the same as of |outrun|. Then we -construct submatrices, add them, and increment |outrun|. - -@<|UPSTensor::addTo| unfolded code@>= -void UPSTensor::addTo(UGSTensor& out) const -{ - TL_RAISE_IF(out.getDims() != tdims, - "Tensors have incompatible dimens in UPSTensor::addTo"); - int cols = tailIdentitySize(); - int off = tdims.tailIdentity(); - IntSequence outrun(out.dimen(), 0); - IntSequence outrun_part(outrun, 0, out.dimen()-off); - IntSequence nvmax_part(out.getDims().getNVX(), 0, out.dimen()-off); - for (int out_col = 0; out_col < out.ncols(); out_col+=cols) { - // permute |outrun| - IntSequence perrun(out.dimen()); - tdims.getPer().apply(outrun, perrun); - index from(this, perrun); - // construct submatrices - ConstTwoDMatrix subfrom(*this, *from, cols); - TwoDMatrix subout(out, out_col, cols); - // add - subout.add(1, subfrom); - // increment |outrun| by cols - UTensor::increment(outrun_part, nvmax_part); - } -} - - -@ This returns a product of all items in |nvmax| which make up the -trailing identity part. - -@<|UPSTensor::tailIdentitySize| code@>= -int UPSTensor::tailIdentitySize() const -{ - return tdims.getNVX().mult(dimen()-tdims.tailIdentity(), dimen()); -} - -@ This fill method is pretty dumb. We go through all columns in |this| -tensor, translate coordinates to sparse tensor, sort them and find an -item in the sparse tensor. There are many not successful lookups for -really sparse tensor, that is why the second method works better for -really sparse tensors. - -@<|UPSTensor::fillFromSparseOne| code@>= -void UPSTensor::fillFromSparseOne(const FSSparseTensor& t, const IntSequence& ss, - const IntSequence& coor) -{ - IntSequence cumtmp(ss.size()); - cumtmp[0] = 0; - for (int i = 1; i < ss.size(); i++) - cumtmp[i] = cumtmp[i-1] + ss[i-1]; - IntSequence cum(coor.size()); - for (int i = 0; i < coor.size(); i++) - cum[i] = cumtmp[coor[i]]; - - zeros(); - for (Tensor::index run = begin(); run != end(); ++run) { - IntSequence c(run.getCoor()); - c.add(1, cum); - c.sort(); - FSSparseTensor::const_iterator sl = t.getMap().lower_bound(c); - if (sl != t.getMap().end()) { - FSSparseTensor::const_iterator su = t.getMap().upper_bound(c); - for (FSSparseTensor::const_iterator srun = sl; srun != su; ++srun) - get((*srun).second.first, *run) = (*srun).second.second; - } - } -} - -@ This is the second way of filling the slice. For instance, let the -slice correspond to partitions $abac$. In here we first calculate -lower and upper bounds for index of the sparse tensor for the -slice. These are |lb_srt| and |ub_srt| respectively. They corresponds -to ordering $aabc$. Then we go through that interval, and select items -which are really between the bounds. Then we take the index, subtract -the lower bound to get it to coordinates of the slice. We get -something like $(i_a,j_a,k_b,l_c)$. Then we apply the inverse -permutation as of the sorting form $abac\mapsto aabc$ to get index -$(i_a,k_b,j_a,l_c)$. Recall that the slice is unfolded, so we have to -apply all permutations preserving the stack coordinates $abac$. In our -case we get list of indices $(i_a,k_b,j_a,l_c)$ and -$(j_a,k_b,i_a,l_c)$. For all we copy the item of the sparse tensor to -the appropriate column. - -@<|UPSTensor::fillFromSparseTwo| code@>= -void UPSTensor::fillFromSparseTwo(const FSSparseTensor& t, const IntSequence& ss, - const IntSequence& coor) -{ - IntSequence coor_srt(coor); - coor_srt.sort(); - IntSequence cum(ss.size()); - cum[0] = 0; - for (int i = 1; i < ss.size(); i++) - cum[i] = cum[i-1] + ss[i-1]; - IntSequence lb_srt(coor.size()); - IntSequence ub_srt(coor.size()); - for (int i = 0; i < coor.size(); i++) { - lb_srt[i] = cum[coor_srt[i]]; - ub_srt[i] = cum[coor_srt[i]] + ss[coor_srt[i]] - 1; - } - - const PermutationSet& pset = tls.pbundle->get(coor.size()); - vector pp = pset.getPreserving(coor); - - Permutation unsort(coor); - zeros(); - FSSparseTensor::const_iterator lbi = t.getMap().lower_bound(lb_srt); - FSSparseTensor::const_iterator ubi = t.getMap().upper_bound(ub_srt); - for (FSSparseTensor::const_iterator run = lbi; run != ubi; ++run) { - if (lb_srt.lessEq((*run).first) && (*run).first.lessEq(ub_srt)) { - IntSequence c((*run).first); - c.add(-1, lb_srt); - unsort.apply(c); - for (unsigned int i = 0; i < pp.size(); i++) { - IntSequence cp(coor.size()); - pp[i]->apply(c, cp); - Tensor::index ind(this, cp); - TL_RAISE_IF(*ind < 0 || *ind >= ncols(), - "Internal error in slicing constructor of UPSTensor"); - get((*run).second.first, *ind) = (*run).second.second; - } - } - } -} - - -@ Here we calculate the maximum offsets in each folded dimension -(dimension sizes, hence |ds|). - -@<|PerTensorDimens2::setDimensionSizes| code@>= -void PerTensorDimens2::setDimensionSizes() -{ - const IntSequence& nvs = getNVS(); - for (int i = 0; i < numSyms(); i++) { - TensorDimens td(syms[i], nvs); - ds[i] = td.calcFoldMaxOffset(); - } -} - -@ If there are two folded dimensions, the offset in such a dimension -is offset of the second plus offset of the first times the maximum -offset of the second. If there are $n+1$ dimensions, the offset is a -sum of offsets of the last dimension plus the offset in the first $n$ -dimensions multiplied by the maximum offset of the last -dimension. This is exactly what the following code does. - -@<|PerTensorDimens2::calcOffset| code@>= -int PerTensorDimens2::calcOffset(const IntSequence& coor) const -{ - TL_RAISE_IF(coor.size() != dimen(), - "Wrong length of coordinates in PerTensorDimens2::calcOffset"); - IntSequence cc(coor); - int ret = 0; - int off = 0; - for (int i = 0; i < numSyms(); i++) { - TensorDimens td(syms[i], getNVS()); - IntSequence c(cc, off, off+syms[i].dimen()); - int a = td.calcFoldOffset(c); - ret = ret*ds[i] + a; - off += syms[i].dimen(); - } - return ret; -} - -@ -@<|PerTensorDimens2::print| code@>= -void PerTensorDimens2::print() const -{ - printf("nvmax: "); nvmax.print(); - printf("per: "); per.print(); - printf("syms: "); syms.print(); - printf("dims: "); ds.print(); -} - -@ Here we increment the given integer sequence. It corresponds to -|UTensor::increment| of the whole sequence, and then partial -monotonizing of the subsequences with respect to the -symmetries of each dimension. - -@<|FPSTensor::increment| code@>= -void FPSTensor::increment(IntSequence& v) const -{ - TL_RAISE_IF(v.size() != dimen(), - "Wrong length of coordinates in FPSTensor::increment"); - UTensor::increment(v, tdims.getNVX()); - int off = 0; - for (int i = 0; i < tdims.numSyms(); i++) { - IntSequence c(v, off, off+tdims.getSym(i).dimen()); - c.pmonotone(tdims.getSym(i)); - off += tdims.getSym(i).dimen(); - } -} - - -@ -@<|FPSTensor::decrement| code@>= -void FPSTensor::decrement(IntSequence& v) const -{ - TL_RAISE("FPSTensor::decrement not implemented"); -} - -@ -@<|FPSTensor::unfold| code@>= -UTensor& FPSTensor::unfold() const -{ - TL_RAISE("Unfolding of FPSTensor not implemented"); - UFSTensor* nothing = new UFSTensor(0,0,0); - return *nothing; -} - -@ We only call |calcOffset| of the |PerTensorDimens2|. -@<|FPSTensor::getOffset| code@>= -int FPSTensor::getOffset(const IntSequence& v) const -{ - return tdims.calcOffset(v); -} - -@ Here we add the tensor to |out|. We go through all columns of the -|out|, apply the permutation to get index in the tensor, and add the -column. Note that if the permutation is identity, then the dimensions -of the tensors might not be the same (since this tensor is partially -folded). - -@<|FPSTensor::addTo| code@>= -void FPSTensor::addTo(FGSTensor& out) const -{ - for (index tar = out.begin(); tar != out.end(); ++tar) { - IntSequence coor(dimen()); - tdims.getPer().apply(tar.getCoor(), coor); - index src(this, coor); - out.addColumn(*this, *src, *tar); - } -} - -@ Here is the constructor which multiplies the Kronecker product with -the general symmetry sparse tensor |GSSparseTensor|. The main idea is -to go through items in the sparse tensor (each item selects rows in -the matrices form the Kornecker product), then to Kronecker multiply -the rows and multiply with the item, and to add the resulting row to -the appropriate row of the resulting |FPSTensor|. - -The realization of this idea is a bit more complicated since we have -to go through all items, and each item must be added as many times as -it has its symmetric elements. Moreover, the permutations shuffle -order of rows in their Kronecker product. - -So, we through all unfolded indices in a tensor with the same -dimensions as the |GSSparseTensor| (sparse slice). For each such index -we calculate its folded version (corresponds to ordering of -subsequences within symmetries), we test if there is an item in the -sparse slice with such coordinates, and if there is, we construct the -Kronecker product of the rows, and go through all of items with the -coordinates, and add to appropriate rows of |this| tensor. - -@<|FPSTensor| sparse constructor@>= -FPSTensor::FPSTensor(const TensorDimens& td, const Equivalence& e, const Permutation& p, - const GSSparseTensor& a, const KronProdAll& kp) - : FTensor(along_col, PerTensorDimens(td, Permutation(e, p)).getNVX(), - a.nrows(), kp.ncols(), td.dimen()), - tdims(td, e, p) -{ - zeros(); - - UGSTensor dummy(0, a.getDims()); - for (Tensor::index run = dummy.begin(); run != dummy.end(); ++run) { - Tensor::index fold_ind = dummy.getFirstIndexOf(run); - const IntSequence& c = fold_ind.getCoor(); - GSSparseTensor::const_iterator sl = a.getMap().lower_bound(c); - if (sl != a.getMap().end()) { - Vector* row_prod = kp.multRows(run.getCoor()); - GSSparseTensor::const_iterator su = a.getMap().upper_bound(c); - for (GSSparseTensor::const_iterator srun = sl; srun != su; ++srun) { - Vector out_row((*srun).second.first, *this); - out_row.add((*srun).second.second, *row_prod); - } - delete row_prod; - } - } -} - - -@ End of {\tt ps\_tensor.cpp} file. diff --git a/dynare++/tl/cc/ps_tensor.hh b/dynare++/tl/cc/ps_tensor.hh new file mode 100644 index 000000000..52ce91b56 --- /dev/null +++ b/dynare++/tl/cc/ps_tensor.hh @@ -0,0 +1,384 @@ +// Copyright 2004, Ondra Kamenik + +// Even more general symmetry tensor. + +/* Here we define an abstraction for a tensor, which has a general + symmetry, but the symmetry is not of what is modelled by + |Symmetry|. This kind of tensor comes to existence when we evaluate + something like: + $$\left[B_{y^2u^3}\right]_{\alpha_1\alpha_2\beta_1\beta_2\beta_3}= + \cdots+\left[g_{y^3}\right]_{\gamma_1\gamma_2\gamma_3} + \left[g_{yu}\right]^{\gamma_1}_{\alpha_1\beta_3} + \left[g_{yu}\right]^{\gamma_2}_{\alpha_2\beta_1} + \left[g_u\right]^{\gamma_3}_{\beta_2}+\cdots + $$ + If the tensors are unfolded, we obtain a tensor + $$g_{y^3}\cdot\left(g_{yu}\otimes g_{yu}\otimes g_{u}\right)$$ + + Obviously, this tensor can have a symmetry not compatible with + ordering $\alpha_1\alpha_2\beta_1\beta_2\beta_3$, (in other words, not + compatible with symmetry $y^2u^3$). In fact, the indices are permuted. + + This kind of tensor must be added to $\left[B_{y^2u^3}\right]$. Its + dimensions are the same as of $\left[B_{y^2u^3}\right]$, but some + coordinates are permuted. The addition is the only action we need to + do with the tensor. + + Another application where this permuted symmetry tensor appears is a + slice of a fully symmetric tensor. If the symmetric dimension of the + tensor is partitioned to continuous parts, and we are interested only + in data with a given symmetry (permuted) of the partitions, then we + have the permuted symmetry tensor. For instance, if $x$ is partitioned + $x=[a,b,c,d]$, and having tensor $\left[f_{x^3}\right]$, one can d a + slice (subtensor) $\left[f_{aca}\right]$. The data of this tensor are + permuted of $\left[f_{a^c}\right]$. + + Here we also define the folded version of permuted symmetry tensor. It + has permuted symmetry and is partially folded. One can imagine it as a + product of a few dimensions, each of them is folded and having a few + variables. The underlying variables are permuted. The product of such + dimensions is described by |PerTensorDimens2|. The tensor holding the + underlying data is |FPSTensor|. */ + +#ifndef PS_TENSOR_H +#define PS_TENSOR_H + +#include "tensor.hh" +#include "gs_tensor.hh" +#include "equivalence.hh" +#include "permutation.hh" +#include "kron_prod.hh" +#include "sparse_tensor.hh" + +/* This is just a helper class for ordering a sequence on call stack. */ + +class SortIntSequence : public IntSequence +{ +public: + SortIntSequence(const IntSequence &s) + : IntSequence(s) + { + sort(); + } +}; + +/* Here we declare a class describing dimensions of permuted symmetry + tensor. It inherits from |TensorDimens| and adds a permutation which + permutes |nvmax|. It has two constructors, each corresponds to a + context where the tensor appears. + + The first constructor calculates the permutation from a given equivalence. + + The second constructor corresponds to dimensions of a slice. Let us + take $\left[f_{aca}\right]$ as an example. First it calculates + |TensorDimens| of $\left[f_{a^c}\right]$, then it calculates a + permutation corresponding to ordering of $aca$ to $a^2c$, and applies + this permutation on the dimensions as the first constructor. The + constructor takes only stack sizes (lengths of $a$, $b$, $c$, and + $d$), and coordinates of picked partitions. + + Note that inherited methods |calcUnfoldColumns| and |calcFoldColumns| + work, since number of columns is independent on the permutation, and + |calcFoldColumns| does not use changed |nvmax|, it uses |nvs|, so it + is OK. */ + +class PerTensorDimens : public TensorDimens +{ +protected: + Permutation per; +public: + PerTensorDimens(const Symmetry &s, const IntSequence &nvars, + const Equivalence &e) + : TensorDimens(s, nvars), per(e) + { + per.apply(nvmax); + } + PerTensorDimens(const TensorDimens &td, const Equivalence &e) + : TensorDimens(td), per(e) + { + per.apply(nvmax); + } + PerTensorDimens(const TensorDimens &td, const Permutation &p) + : TensorDimens(td), per(p) + { + per.apply(nvmax); + } + PerTensorDimens(const IntSequence &ss, const IntSequence &coor) + : TensorDimens(ss, SortIntSequence(coor)), per(coor) + { + per.apply(nvmax); + } + PerTensorDimens(const PerTensorDimens &td) + : TensorDimens(td), per(td.per) + { + } + const PerTensorDimens & + operator=(const PerTensorDimens &td) + { + TensorDimens::operator=(td); per = td.per; return *this; + } + bool + operator==(const PerTensorDimens &td) + { + return TensorDimens::operator==(td) && per == td.per; + } + int + tailIdentity() const + { + return per.tailIdentity(); + } + const Permutation & + getPer() const + { + return per; + } +}; + +/* Here we declare the permuted symmetry unfolded tensor. It has + |PerTensorDimens| as a member. It inherits from |UTensor| which + requires to implement |fold| method. There is no folded counterpart, + so in our implementation we raise unconditional exception, and return + some dummy object (just to make it compilable without warnings). + + The class has two sorts of constructors corresponding to a context where it + appears. The first constructs object from a given matrix, and + Kronecker product. Within the constructor, all the calculations are + performed. Also we need to define dimensions, these are the same of + the resulting matrix (in our example $\left[B_{y^2u^3}\right]$) but + permuted. The permutation is done in |PerTensorDimens| constructor. + + The second type of constructor is slicing. It makes a slice from + |FSSparseTensor|. The slice is given by stack sizes, and coordinates of + picked stacks. + + There are two algorithms for filling a slice of a sparse tensor. The + first |fillFromSparseOne| works well for more dense tensors, the + second |fillFromSparseTwo| is better for very sparse tensors. We + provide a static method, which decides what of the two algorithms is + better. */ + +class UPSTensor : public UTensor +{ + const PerTensorDimens tdims; +public: + // |UPSTensor| constructors from Kronecker product + /* Here we have four constructors making an |UPSTensor| from a product + of matrix and Kronecker product. The first constructs the tensor from + equivalence classes of the given equivalence in an order given by the + equivalence. The second does the same but with optimized + |KronProdAllOptim|, which has a different order of matrices than given + by the classes in the equivalence. This permutation is projected to + the permutation of the |UPSTensor|. The third, is the same as the + first, but the classes of the equivalence are permuted by the given + permutation. Finally, the fourth is the most general combination. It + allows for a permutation of equivalence classes, and for optimized + |KronProdAllOptim|, which permutes the permuted equivalence classes. */ + UPSTensor(const TensorDimens &td, const Equivalence &e, + const ConstTwoDMatrix &a, const KronProdAll &kp) + : UTensor(along_col, PerTensorDimens(td, e).getNVX(), + a.nrows(), kp.ncols(), td.dimen()), tdims(td, e) + { + kp.mult(a, *this); + } + UPSTensor(const TensorDimens &td, const Equivalence &e, + const ConstTwoDMatrix &a, const KronProdAllOptim &kp) + : UTensor(along_col, PerTensorDimens(td, Permutation(e, kp.getPer())).getNVX(), + a.nrows(), kp.ncols(), td.dimen()), tdims(td, Permutation(e, kp.getPer())) + { + kp.mult(a, *this); + } + UPSTensor(const TensorDimens &td, const Equivalence &e, const Permutation &p, + const ConstTwoDMatrix &a, const KronProdAll &kp) + : UTensor(along_col, PerTensorDimens(td, Permutation(e, p)).getNVX(), + a.nrows(), kp.ncols(), td.dimen()), tdims(td, Permutation(e, p)) + { + kp.mult(a, *this); + } + UPSTensor(const TensorDimens &td, const Equivalence &e, const Permutation &p, + const ConstTwoDMatrix &a, const KronProdAllOptim &kp) + : UTensor(along_col, PerTensorDimens(td, Permutation(e, Permutation(p, kp.getPer()))).getNVX(), + a.nrows(), kp.ncols(), td.dimen()), tdims(td, Permutation(e, Permutation(p, kp.getPer()))) + { + kp.mult(a, *this); + } + UPSTensor(const FSSparseTensor &t, const IntSequence &ss, + const IntSequence &coor, const PerTensorDimens &ptd); + UPSTensor(const UPSTensor &ut) + : UTensor(ut), tdims(ut.tdims) + { + } + + void increment(IntSequence &v) const; + void decrement(IntSequence &v) const; + FTensor&fold() const; + + int getOffset(const IntSequence &v) const; + void addTo(FGSTensor &out) const; + void addTo(UGSTensor &out) const; + + enum fill_method {first, second}; + static fill_method decideFillMethod(const FSSparseTensor &t); +private: + int tailIdentitySize() const; + void fillFromSparseOne(const FSSparseTensor &t, const IntSequence &ss, + const IntSequence &coor); + void fillFromSparseTwo(const FSSparseTensor &t, const IntSequence &ss, + const IntSequence &coor); +}; + +/* Here we define an abstraction for the tensor dimension with the + symmetry like $xuv\vert uv\vert xu\vert y\vert y\vert x\vert x\vert + y$. These symmetries come as induces symmetries of equivalence and + some outer symmetry. Thus the underlying variables are permuted. One + can imagine the dimensions as an unfolded product of dimensions which + consist of folded products of variables. + + We inherit from |PerTensorDimens| since we need the permutation + implied by the equivalence. The new member are the induced symmetries + (symmetries of each folded dimensions) and |ds| which are sizes of the + dimensions. The number of folded dimensions is return by |numSyms|. + + The object is constructed from outer tensor dimensions and from + equivalence with optionally permuted classes. */ + +class PerTensorDimens2 : public PerTensorDimens +{ + InducedSymmetries syms; + IntSequence ds; +public: + PerTensorDimens2(const TensorDimens &td, const Equivalence &e, + const Permutation &p) + : PerTensorDimens(td, Permutation(e, p)), + syms(e, p, td.getSym()), + ds(syms.size()) + { + setDimensionSizes(); + } + PerTensorDimens2(const TensorDimens &td, const Equivalence &e) + : PerTensorDimens(td, e), + syms(e, td.getSym()), + ds(syms.size()) + { + setDimensionSizes(); + } + int + numSyms() const + { + return (int) syms.size(); + } + const Symmetry & + getSym(int i) const + { + return syms[i]; + } + int + calcMaxOffset() const + { + return ds.mult(); + } + int calcOffset(const IntSequence &coor) const; + void print() const; +protected: + void setDimensionSizes(); +}; + +/* Here we define an abstraction of the permuted symmetry folded + tensor. It is needed in context of the Faa Di Bruno formula for folded + stack container multiplied with container of dense folded tensors, or + multiplied by one full symmetry sparse tensor. + + For example, if we perform the Faa Di Bruno for $F=f(z)$, where + $z=[g(x,y,u,v), h(x,y,u), x, y]^T$, we get for one concrete + equivalence: + $$ + \left[F_{x^4y^3u^3v^2}\right]=\ldots+ + \left[f_{g^2h^2x^2y}\right]\left( + [g]_{xv}\otimes[g]_{u^2v}\otimes + [h]_{xu}\otimes[h]_{y^2}\otimes + \left[\vphantom{\sum}[I]_x\otimes[I]_x\right]\otimes + \left[\vphantom{\sum}[I]_y\right] + \right) + +\ldots + $$ + + The class |FPSTensor| represents the tensor at the right. Its + dimension corresponds to a product of 7 dimensions with the following + symmetries: $xv\vert u^v\vert xu\vert y^2\vert x\vert x\vert y$. Such + the dimension is described by |PerTensorDimens2|. + + The tensor is constructed in a context of stack container + multiplication, so, it is constructed from dimensions |td| (dimensions + of the output tensor), stack product |sp| (implied symmetries picking + tensors from a stack container, here it is $z$), then a sorted integer + sequence of the picked stacks of the stack product (it is always + sorted, here it is $(0,0,1,1,2,2,3)$), then the tensor + $\left[f_{g^2h^2x^2y}\right]$ (its symmetry must be the same as + symmetry given by the |istacks|), and finally from the equivalence + with permuted classes. + + We implement |increment| and |getOffset| methods, |decrement| and + |unfold| raise an exception. Also, we implement |addTo| method, which + adds the tensor data (partially unfolded) to folded general symmetry + tensor. */ + +template +class StackProduct; + +class FPSTensor : public FTensor +{ + const PerTensorDimens2 tdims; +public: + /* As for |UPSTensor|, we provide four constructors allowing for + combinations of permuting equivalence classes, and optimization of + |KronProdAllOptim|. These constructors multiply with dense general + symmetry tensor (coming from the dense container, or as a dense slice + of the full symmetry sparse tensor). In addition to these 4 + constructors, we have one constructor multiplying with general + symmetry sparse tensor (coming as a sparse slice of the full symmetry + sparse tensor). */ + FPSTensor(const TensorDimens &td, const Equivalence &e, + const ConstTwoDMatrix &a, const KronProdAll &kp) + : FTensor(along_col, PerTensorDimens(td, e).getNVX(), + a.nrows(), kp.ncols(), td.dimen()), tdims(td, e) + { + kp.mult(a, *this); + } + FPSTensor(const TensorDimens &td, const Equivalence &e, + const ConstTwoDMatrix &a, const KronProdAllOptim &kp) + : FTensor(along_col, PerTensorDimens(td, Permutation(e, kp.getPer())).getNVX(), + a.nrows(), kp.ncols(), td.dimen()), tdims(td, e, kp.getPer()) + { + kp.mult(a, *this); + } + FPSTensor(const TensorDimens &td, const Equivalence &e, const Permutation &p, + const ConstTwoDMatrix &a, const KronProdAll &kp) + : FTensor(along_col, PerTensorDimens(td, Permutation(e, p)).getNVX(), + a.nrows(), kp.ncols(), td.dimen()), tdims(td, e, p) + { + kp.mult(a, *this); + } + FPSTensor(const TensorDimens &td, const Equivalence &e, const Permutation &p, + const ConstTwoDMatrix &a, const KronProdAllOptim &kp) + : FTensor(along_col, PerTensorDimens(td, Permutation(e, Permutation(p, kp.getPer()))).getNVX(), + a.nrows(), kp.ncols(), td.dimen()), tdims(td, e, Permutation(p, kp.getPer())) + { + kp.mult(a, *this); + } + + FPSTensor(const TensorDimens &td, const Equivalence &e, const Permutation &p, + const GSSparseTensor &t, const KronProdAll &kp); + + FPSTensor(const FPSTensor &ft) + : FTensor(ft), tdims(ft.tdims) + { + } + + void increment(IntSequence &v) const; + void decrement(IntSequence &v) const; + UTensor&unfold() const; + + int getOffset(const IntSequence &v) const; + void addTo(FGSTensor &out) const; +}; + +#endif diff --git a/dynare++/tl/cc/ps_tensor.hweb b/dynare++/tl/cc/ps_tensor.hweb deleted file mode 100644 index 28b550f1c..000000000 --- a/dynare++/tl/cc/ps_tensor.hweb +++ /dev/null @@ -1,351 +0,0 @@ -@q $Id: ps_tensor.hweb 741 2006-05-09 11:12:46Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@*2 Even more general symmetry tensor. Start of {\tt ps\_tensor.h} file. - -Here we define an abstraction for a tensor, which has a general -symmetry, but the symmetry is not of what is modelled by -|Symmetry|. This kind of tensor comes to existence when we evaluate -something like: -$$\left[B_{y^2u^3}\right]_{\alpha_1\alpha_2\beta_1\beta_2\beta_3}= -\cdots+\left[g_{y^3}\right]_{\gamma_1\gamma_2\gamma_3} -\left[g_{yu}\right]^{\gamma_1}_{\alpha_1\beta_3} -\left[g_{yu}\right]^{\gamma_2}_{\alpha_2\beta_1} -\left[g_u\right]^{\gamma_3}_{\beta_2}+\cdots -$$ -If the tensors are unfolded, we obtain a tensor -$$g_{y^3}\cdot\left(g_{yu}\otimes g_{yu}\otimes g_{u}\right)$$ - -Obviously, this tensor can have a symmetry not compatible with -ordering $\alpha_1\alpha_2\beta_1\beta_2\beta_3$, (in other words, not -compatible with symmetry $y^2u^3$). In fact, the indices are permuted. - -This kind of tensor must be added to $\left[B_{y^2u^3}\right]$. Its -dimensions are the same as of $\left[B_{y^2u^3}\right]$, but some -coordinates are permuted. The addition is the only action we need to -do with the tensor. - -Another application where this permuted symmetry tensor appears is a -slice of a fully symmetric tensor. If the symmetric dimension of the -tensor is partitioned to continuous parts, and we are interested only -in data with a given symmetry (permuted) of the partitions, then we -have the permuted symmetry tensor. For instance, if $x$ is partitioned -$x=[a,b,c,d]$, and having tensor $\left[f_{x^3}\right]$, one can d a -slice (subtensor) $\left[f_{aca}\right]$. The data of this tensor are -permuted of $\left[f_{a^c}\right]$. - -Here we also define the folded version of permuted symmetry tensor. It -has permuted symmetry and is partially folded. One can imagine it as a -product of a few dimensions, each of them is folded and having a few -variables. The underlying variables are permuted. The product of such -dimensions is described by |PerTensorDimens2|. The tensor holding the -underlying data is |FPSTensor|. - -@s SortIntSequence int -@s PerTensorDimens int -@s UPSTensor int -@s PerTensorDimens2 int -@s FPSTensor int -@s KronProdFoldStacks int - -@c - -#ifndef PS_TENSOR_H -#define PS_TENSOR_H - -#include "tensor.h" -#include "gs_tensor.h" -#include "equivalence.h" -#include "permutation.h" -#include "kron_prod.h" -#include "sparse_tensor.h" - -@<|SortIntSequence| class declaration@>; -@<|PerTensorDimens| class declaration@>; -@<|UPSTensor| class declaration@>; -@<|PerTensorDimens2| class declaration@>; -@<|FPSTensor| class declaration@>; - -#endif - -@ This is just a helper class for ordering a sequence on call stack. - -@<|SortIntSequence| class declaration@>= -class SortIntSequence : public IntSequence { -public:@; - SortIntSequence(const IntSequence& s) - : IntSequence(s) {@+ sort();@+} -}; - - -@ Here we declare a class describing dimensions of permuted symmetry -tensor. It inherits from |TensorDimens| and adds a permutation which -permutes |nvmax|. It has two constructors, each corresponds to a -context where the tensor appears. - -The first constructor calculates the permutation from a given equivalence. - -The second constructor corresponds to dimensions of a slice. Let us -take $\left[f_{aca}\right]$ as an example. First it calculates -|TensorDimens| of $\left[f_{a^c}\right]$, then it calculates a -permutation corresponding to ordering of $aca$ to $a^2c$, and applies -this permutation on the dimensions as the first constructor. The -constructor takes only stack sizes (lengths of $a$, $b$, $c$, and -$d$), and coordinates of picked partitions. - -Note that inherited methods |calcUnfoldColumns| and |calcFoldColumns| -work, since number of columns is independent on the permutation, and -|calcFoldColumns| does not use changed |nvmax|, it uses |nvs|, so it -is OK. - -@<|PerTensorDimens| class declaration@>= -class PerTensorDimens : public TensorDimens { -protected:@; - Permutation per; -public:@; - PerTensorDimens(const Symmetry& s, const IntSequence& nvars, - const Equivalence& e) - : TensorDimens(s, nvars), per(e) - {@+ per.apply(nvmax);@+} - PerTensorDimens(const TensorDimens& td, const Equivalence& e) - : TensorDimens(td), per(e) - {@+ per.apply(nvmax);@+} - PerTensorDimens(const TensorDimens& td, const Permutation& p) - : TensorDimens(td), per(p) - {@+ per.apply(nvmax);@+} - PerTensorDimens(const IntSequence& ss, const IntSequence& coor) - : TensorDimens(ss, SortIntSequence(coor)), per(coor) - {@+ per.apply(nvmax);@+} - PerTensorDimens(const PerTensorDimens& td) - : TensorDimens(td), per(td.per)@+ {} - const PerTensorDimens& operator=(const PerTensorDimens& td) - {@+ TensorDimens::operator=(td);@+ per = td.per;@+ return *this;@+} - bool operator==(const PerTensorDimens& td) - {@+ return TensorDimens::operator==(td) && per == td.per;@+} - int tailIdentity() const - {@+ return per.tailIdentity();@+} - const Permutation& getPer() const - {@+ return per;@+} -}; - -@ Here we declare the permuted symmetry unfolded tensor. It has -|PerTensorDimens| as a member. It inherits from |UTensor| which -requires to implement |fold| method. There is no folded counterpart, -so in our implementation we raise unconditional exception, and return -some dummy object (just to make it compilable without warnings). - -The class has two sorts of constructors corresponding to a context where it -appears. The first constructs object from a given matrix, and -Kronecker product. Within the constructor, all the calculations are -performed. Also we need to define dimensions, these are the same of -the resulting matrix (in our example $\left[B_{y^2u^3}\right]$) but -permuted. The permutation is done in |PerTensorDimens| constructor. - -The second type of constructor is slicing. It makes a slice from -|FSSparseTensor|. The slice is given by stack sizes, and coordinates of -picked stacks. - -There are two algorithms for filling a slice of a sparse tensor. The -first |fillFromSparseOne| works well for more dense tensors, the -second |fillFromSparseTwo| is better for very sparse tensors. We -provide a static method, which decides what of the two algorithms is -better. - -@<|UPSTensor| class declaration@>= -class UPSTensor : public UTensor { - const PerTensorDimens tdims; -public:@; - @<|UPSTensor| constructors from Kronecker product@>; - UPSTensor(const FSSparseTensor& t, const IntSequence& ss, - const IntSequence& coor, const PerTensorDimens& ptd); - UPSTensor(const UPSTensor& ut) - : UTensor(ut), tdims(ut.tdims)@+ {} - - void increment(IntSequence& v) const; - void decrement(IntSequence& v) const; - FTensor& fold() const; - - int getOffset(const IntSequence& v) const; - void addTo(FGSTensor& out) const; - void addTo(UGSTensor& out) const; - - enum fill_method {first, second}; - static fill_method decideFillMethod(const FSSparseTensor& t); -private:@; - int tailIdentitySize() const; - void fillFromSparseOne(const FSSparseTensor& t, const IntSequence& ss, - const IntSequence& coor); - void fillFromSparseTwo(const FSSparseTensor& t, const IntSequence& ss, - const IntSequence& coor); -}; - -@ Here we have four constructors making an |UPSTensor| from a product -of matrix and Kronecker product. The first constructs the tensor from -equivalence classes of the given equivalence in an order given by the -equivalence. The second does the same but with optimized -|KronProdAllOptim|, which has a different order of matrices than given -by the classes in the equivalence. This permutation is projected to -the permutation of the |UPSTensor|. The third, is the same as the -first, but the classes of the equivalence are permuted by the given -permutation. Finally, the fourth is the most general combination. It -allows for a permutation of equivalence classes, and for optimized -|KronProdAllOptim|, which permutes the permuted equivalence classes. - -@<|UPSTensor| constructors from Kronecker product@>= - UPSTensor(const TensorDimens& td, const Equivalence& e, - const ConstTwoDMatrix& a, const KronProdAll& kp) - : UTensor(along_col, PerTensorDimens(td, e).getNVX(), - a.nrows(), kp.ncols(), td.dimen()), tdims(td, e) - {@+ kp.mult(a, *this);@+} - UPSTensor(const TensorDimens& td, const Equivalence& e, - const ConstTwoDMatrix& a, const KronProdAllOptim& kp) - : UTensor(along_col, PerTensorDimens(td, Permutation(e, kp.getPer())).getNVX(), - a.nrows(), kp.ncols(), td.dimen()), tdims(td, Permutation(e, kp.getPer())) - {@+ kp.mult(a, *this);@+} - UPSTensor(const TensorDimens& td, const Equivalence& e, const Permutation& p, - const ConstTwoDMatrix& a, const KronProdAll& kp) - : UTensor(along_col, PerTensorDimens(td, Permutation(e, p)).getNVX(), - a.nrows(), kp.ncols(), td.dimen()), tdims(td, Permutation(e, p)) - {@+ kp.mult(a, *this);@+} - UPSTensor(const TensorDimens& td, const Equivalence& e, const Permutation& p, - const ConstTwoDMatrix& a, const KronProdAllOptim& kp) - : UTensor(along_col, PerTensorDimens(td, Permutation(e, Permutation(p, kp.getPer()))).getNVX(), - a.nrows(), kp.ncols(), td.dimen()), tdims(td, Permutation(e, Permutation(p, kp.getPer()))) - {@+ kp.mult(a, *this);@+} - -@ Here we define an abstraction for the tensor dimension with the -symmetry like $xuv\vert uv\vert xu\vert y\vert y\vert x\vert x\vert -y$. These symmetries come as induces symmetries of equivalence and -some outer symmetry. Thus the underlying variables are permuted. One -can imagine the dimensions as an unfolded product of dimensions which -consist of folded products of variables. - -We inherit from |PerTensorDimens| since we need the permutation -implied by the equivalence. The new member are the induced symmetries -(symmetries of each folded dimensions) and |ds| which are sizes of the -dimensions. The number of folded dimensions is return by |numSyms|. - -The object is constructed from outer tensor dimensions and from -equivalence with optionally permuted classes. - -@<|PerTensorDimens2| class declaration@>= -class PerTensorDimens2 : public PerTensorDimens { - InducedSymmetries syms; - IntSequence ds; -public:@; - PerTensorDimens2(const TensorDimens& td, const Equivalence& e, - const Permutation& p) - : PerTensorDimens(td, Permutation(e, p)), - syms(e, p, td.getSym()), - ds(syms.size()) - {@+ setDimensionSizes();@+} - PerTensorDimens2(const TensorDimens& td, const Equivalence& e) - : PerTensorDimens(td, e), - syms(e, td.getSym()), - ds(syms.size()) - {@+ setDimensionSizes();@+} - int numSyms() const - {@+ return (int)syms.size();@+} - const Symmetry& getSym(int i) const - {@+ return syms[i];@+} - int calcMaxOffset() const - {@+ return ds.mult(); @+} - int calcOffset(const IntSequence& coor) const; - void print() const; -protected:@; - void setDimensionSizes(); -}; - -@ Here we define an abstraction of the permuted symmetry folded -tensor. It is needed in context of the Faa Di Bruno formula for folded -stack container multiplied with container of dense folded tensors, or -multiplied by one full symmetry sparse tensor. - -For example, if we perform the Faa Di Bruno for $F=f(z)$, where -$z=[g(x,y,u,v), h(x,y,u), x, y]^T$, we get for one concrete -equivalence: -$$ -\left[F_{x^4y^3u^3v^2}\right]=\ldots+ -\left[f_{g^2h^2x^2y}\right]\left( -[g]_{xv}\otimes[g]_{u^2v}\otimes -[h]_{xu}\otimes[h]_{y^2}\otimes -\left[\vphantom{\sum}[I]_x\otimes[I]_x\right]\otimes -\left[\vphantom{\sum}[I]_y\right] -\right) -+\ldots -$$ - -The class |FPSTensor| represents the tensor at the right. Its -dimension corresponds to a product of 7 dimensions with the following -symmetries: $xv\vert u^v\vert xu\vert y^2\vert x\vert x\vert y$. Such -the dimension is described by |PerTensorDimens2|. - -The tensor is constructed in a context of stack container -multiplication, so, it is constructed from dimensions |td| (dimensions -of the output tensor), stack product |sp| (implied symmetries picking -tensors from a stack container, here it is $z$), then a sorted integer -sequence of the picked stacks of the stack product (it is always -sorted, here it is $(0,0,1,1,2,2,3)$), then the tensor -$\left[f_{g^2h^2x^2y}\right]$ (its symmetry must be the same as -symmetry given by the |istacks|), and finally from the equivalence -with permuted classes. - -We implement |increment| and |getOffset| methods, |decrement| and -|unfold| raise an exception. Also, we implement |addTo| method, which -adds the tensor data (partially unfolded) to folded general symmetry -tensor. - -@<|FPSTensor| class declaration@>= -template class StackProduct; - -class FPSTensor : public FTensor { - const PerTensorDimens2 tdims; -public:@; - @<|FPSTensor| constructors@>; - - void increment(IntSequence& v) const; - void decrement(IntSequence& v) const; - UTensor& unfold() const; - - int getOffset(const IntSequence& v) const; - void addTo(FGSTensor& out) const; -}; - -@ As for |UPSTensor|, we provide four constructors allowing for -combinations of permuting equivalence classes, and optimization of -|KronProdAllOptim|. These constructors multiply with dense general -symmetry tensor (coming from the dense container, or as a dense slice -of the full symmetry sparse tensor). In addition to these 4 -constructors, we have one constructor multiplying with general -symmetry sparse tensor (coming as a sparse slice of the full symmetry -sparse tensor). - -@<|FPSTensor| constructors@>= - FPSTensor(const TensorDimens& td, const Equivalence& e, - const ConstTwoDMatrix& a, const KronProdAll& kp) - : FTensor(along_col, PerTensorDimens(td, e).getNVX(), - a.nrows(), kp.ncols(), td.dimen()), tdims(td, e) - {@+ kp.mult(a, *this);@+} - FPSTensor(const TensorDimens& td, const Equivalence& e, - const ConstTwoDMatrix& a, const KronProdAllOptim& kp) - : FTensor(along_col, PerTensorDimens(td, Permutation(e, kp.getPer())).getNVX(), - a.nrows(), kp.ncols(), td.dimen()), tdims(td, e, kp.getPer()) - {@+ kp.mult(a, *this);@+} - FPSTensor(const TensorDimens& td, const Equivalence& e, const Permutation& p, - const ConstTwoDMatrix& a, const KronProdAll& kp) - : FTensor(along_col, PerTensorDimens(td, Permutation(e, p)).getNVX(), - a.nrows(), kp.ncols(), td.dimen()), tdims(td, e, p) - {@+ kp.mult(a, *this);@+} - FPSTensor(const TensorDimens& td, const Equivalence& e, const Permutation& p, - const ConstTwoDMatrix& a, const KronProdAllOptim& kp) - : FTensor(along_col, PerTensorDimens(td, Permutation(e, Permutation(p, kp.getPer()))).getNVX(), - a.nrows(), kp.ncols(), td.dimen()), tdims(td, e, Permutation(p, kp.getPer())) - {@+ kp.mult(a, *this);@+} - - FPSTensor(const TensorDimens& td, const Equivalence& e, const Permutation& p, - const GSSparseTensor& t, const KronProdAll& kp); - - FPSTensor(const FPSTensor& ft) - : FTensor(ft), tdims(ft.tdims)@+ {} - -@ End of {\tt ps\_tensor.h} file. diff --git a/dynare++/tl/cc/pyramid_prod.cc b/dynare++/tl/cc/pyramid_prod.cc new file mode 100644 index 000000000..651349378 --- /dev/null +++ b/dynare++/tl/cc/pyramid_prod.cc @@ -0,0 +1,78 @@ +// Copyright 2004, Ondra Kamenik + +#include "pyramid_prod.hh" +#include "permutation.hh" +#include "tl_exception.hh" + +/* Here we construct the |USubTensor| object. We allocate space via the + parent |URTensor|. Number of columns is a length of the list of + indices |lst|, number of variables and dimensions are of the tensor + $h$, this is given by |hdims|. + + We go through all equivalences with number of classes equal to + dimension of $B$. For each equivalence we make a permutation + |per|. Then we fetch all the necessary tensors $g$ with symmetries + implied by symmetry of $B$ and the equivalence. Then we go through the + list of indices, permute them by the permutation and add the Kronecker + product of the selected columns. This is done by |addKronColumn|. */ + +USubTensor::USubTensor(const TensorDimens &bdims, + const TensorDimens &hdims, + const FGSContainer &cont, + const vector &lst) + : URTensor(lst.size(), hdims.getNVX()[0], hdims.dimen()) +{ + TL_RAISE_IF(!hdims.getNVX().isConstant(), + "Tensor has not full symmetry in USubTensor()"); + const EquivalenceSet &eset = cont.getEqBundle().get(bdims.dimen()); + zeros(); + for (EquivalenceSet::const_iterator it = eset.begin(); + it != eset.end(); ++it) + { + if ((*it).numClasses() == hdims.dimen()) + { + Permutation per(*it); + vector ts + = cont.fetchTensors(bdims.getSym(), *it); + for (int i = 0; i < (int) lst.size(); i++) + { + IntSequence perindex(lst[i].size()); + per.apply(lst[i], perindex); + addKronColumn(i, ts, perindex); + } + } + } +} + +/* This makes a Kronecker product of appropriate columns from tensors + in |fs| and adds such data to |i|-th column of this matrix. The + appropriate columns are defined by |pindex| sequence. A column of a + tensor has index created from a corresponding part of |pindex|. The + sizes of these parts are given by dimensions of the tensors in |ts|. + + Here we break the given index |pindex| according to the dimensions of + the tensors in |ts|, and for each subsequence of the |pindex| we find + an index of the folded tensor, which involves calling |getOffset| for + folded tensor, which might be costly. We gather all columns to a + vector |tmpcols| which are Kronecker multiplied in constructor of + |URSingleTensor|. Finally we add data of |URSingleTensor| to the + |i|-th column. */ + +void +USubTensor::addKronColumn(int i, const vector &ts, + const IntSequence &pindex) +{ + vector tmpcols; + int lastdim = 0; + for (unsigned int j = 0; j < ts.size(); j++) + { + IntSequence ind(pindex, lastdim, lastdim+ts[j]->dimen()); + lastdim += ts[j]->dimen(); + index in(ts[j], ind); + tmpcols.push_back(ConstVector(*(ts[j]), *in)); + } + + URSingleTensor kronmult(tmpcols); + Vector coli(*this, i); + coli.add(1.0, kronmult.getData()); +} diff --git a/dynare++/tl/cc/pyramid_prod.cweb b/dynare++/tl/cc/pyramid_prod.cweb deleted file mode 100644 index 2b4693cd3..000000000 --- a/dynare++/tl/cc/pyramid_prod.cweb +++ /dev/null @@ -1,86 +0,0 @@ -@q $Id: pyramid_prod.cweb 148 2005-04-19 15:12:26Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@ Start of {\tt pyramid\_prod.cpp} file. -@c - -#include "pyramid_prod.h" -#include "permutation.h" -#include "tl_exception.h" - -@<|USubTensor| constructor code@>; -@<|USubTensor::addKronColumn| code@>; - - -@ Here we construct the |USubTensor| object. We allocate space via the -parent |URTensor|. Number of columns is a length of the list of -indices |lst|, number of variables and dimensions are of the tensor -$h$, this is given by |hdims|. - -We go through all equivalences with number of classes equal to -dimension of $B$. For each equivalence we make a permutation -|per|. Then we fetch all the necessary tensors $g$ with symmetries -implied by symmetry of $B$ and the equivalence. Then we go through the -list of indices, permute them by the permutation and add the Kronecker -product of the selected columns. This is done by |addKronColumn|. - -@<|USubTensor| constructor code@>= -USubTensor::USubTensor(const TensorDimens& bdims, - const TensorDimens& hdims, - const FGSContainer& cont, - const vector& lst) - : URTensor(lst.size(), hdims.getNVX()[0], hdims.dimen()) -{ - TL_RAISE_IF(! hdims.getNVX().isConstant(), - "Tensor has not full symmetry in USubTensor()"); - const EquivalenceSet& eset = cont.getEqBundle().get(bdims.dimen()); - zeros(); - for (EquivalenceSet::const_iterator it = eset.begin(); - it != eset.end(); ++it) { - if ((*it).numClasses() == hdims.dimen()) { - Permutation per(*it); - vector ts = - cont.fetchTensors(bdims.getSym(), *it); - for (int i = 0; i < (int)lst.size(); i++) { - IntSequence perindex(lst[i].size()); - per.apply(lst[i], perindex); - addKronColumn(i, ts, perindex); - } - } - } -} - -@ This makes a Kronecker product of appropriate columns from tensors -in |fs| and adds such data to |i|-th column of this matrix. The -appropriate columns are defined by |pindex| sequence. A column of a -tensor has index created from a corresponding part of |pindex|. The -sizes of these parts are given by dimensions of the tensors in |ts|. - -Here we break the given index |pindex| according to the dimensions of -the tensors in |ts|, and for each subsequence of the |pindex| we find -an index of the folded tensor, which involves calling |getOffset| for -folded tensor, which might be costly. We gather all columns to a -vector |tmpcols| which are Kronecker multiplied in constructor of -|URSingleTensor|. Finally we add data of |URSingleTensor| to the -|i|-th column. - -@<|USubTensor::addKronColumn| code@>= -void USubTensor::addKronColumn(int i, const vector& ts, - const IntSequence& pindex) -{ - vector tmpcols; - int lastdim = 0; - for (unsigned int j = 0; j < ts.size(); j++) { - IntSequence ind(pindex, lastdim, lastdim+ts[j]->dimen()); - lastdim += ts[j]->dimen(); - index in(ts[j], ind); - tmpcols.push_back(ConstVector(*(ts[j]), *in)); - } - - URSingleTensor kronmult(tmpcols); - Vector coli(*this, i); - coli.add(1.0, kronmult.getData()); -} - - -@ End of {\tt pyramid\_prod.cpp} file. diff --git a/dynare++/tl/cc/pyramid_prod.hh b/dynare++/tl/cc/pyramid_prod.hh new file mode 100644 index 000000000..11ba39612 --- /dev/null +++ b/dynare++/tl/cc/pyramid_prod.hh @@ -0,0 +1,74 @@ +// Copyright 2004, Ondra Kamenik + +// Multiplying tensor columns. + +/* In here, we implement the Faa Di Bruno for folded + tensors. Recall, that one step of the Faa Di Bruno is a formula: + $$\left[B_{s^k}\right]_{\alpha_1\ldots\alpha_k}= + [h_{y^l}]_{\gamma_1\ldots\gamma_l} + \prod_{m=1}^l\left[g_{s^{\vert c_m\vert}}\right]^{\gamma_m}_{c_m(\alpha)} + $$ + + In contrast to unfolded implementation of |UGSContainer::multAndAdd| + with help of |KronProdAll| and |UPSTensor|, we take a completely + different strategy. We cannot afford full instantiation of + $$\sum_{c\in M_{l,k}} + \prod_{m=1}^l\left[g_{s^{\vert c_m\vert}}\right]^{\gamma_m}_{c_m(\alpha)}$$ + and therefore we do it per partes. We select some number of columns, + for instance 10, calculate 10 continuous iterators of tensor $B$. Then we + form unfolded tensor + $$[G]_S^{\gamma_1\ldots\gamma_l}=\left[\sum_{c\in M_{l,k}} + \prod_{m=1}^l\left[g_{s^{\vert c_m\vert}}\right]^{\gamma_m}_{c_m(\alpha)} + \right]_S$$ + where $S$ is the selected set of 10 indices. This is done as Kronecker + product of vectors corresponding to selected columns. Note that, in + general, there is no symmetry in $G$, its type is special class for + this purpose. + + If $g$ is folded, then we have to form folded version of $G$. There is + no symmetry in $G$ data, so we sum all unfolded indices corresponding + to folded index together. This is perfectly OK, since we multiply + these groups of (equivalent) items with the same number in fully + symmetric $g$. + + After this, we perform ordinary matrix multiplication to obtain a + selected set of columns of $B$. + + In here, we define a class for forming and representing + $[G]_S^{\gamma_1\ldots\gamma_l}$. Basically, this tensor is + row-oriented (multidimensional index is along rows), and it is fully + symmetric. So we inherit from |URTensor|. If we need its folded + version, we simply use a suitable conversion. The new abstraction will + have only a new constructor allowing a construction from the given set + of indices $S$, and given set of tensors $g$. The rest of the process + is implemented in |@<|FGSContainer::multAndAdd| unfolded code@>| or + |@<|FGSContainer::multAndAdd| folded code@>|. */ + +#ifndef PYRAMID_PROD_H +#define PYRAMID_PROD_H + +#include "int_sequence.hh" +#include "rfs_tensor.hh" +#include "gs_tensor.hh" +#include "t_container.hh" + +#include + +using namespace std; + +/* Here we define the new tensor for representing + $[G]_S^{\gamma_1\ldots\gamma_l}$. It allows a construction from + container of folded general symmetry tensors |cont|, and set of + indices |ts|. Also we have to supply dimensions of resulting tensor + $B$, and dimensions of tensor $h$. */ + +class USubTensor : public URTensor +{ +public: + USubTensor(const TensorDimens &bdims, const TensorDimens &hdims, + const FGSContainer &cont, const vector &lst); + void addKronColumn(int i, const vector &ts, + const IntSequence &pindex); +}; + +#endif diff --git a/dynare++/tl/cc/pyramid_prod.hweb b/dynare++/tl/cc/pyramid_prod.hweb deleted file mode 100644 index 3c6c9f8ab..000000000 --- a/dynare++/tl/cc/pyramid_prod.hweb +++ /dev/null @@ -1,80 +0,0 @@ -@q $Id: pyramid_prod.hweb 148 2005-04-19 15:12:26Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@*2 Multiplying tensor columns. Start of {\tt pyramid\_prod.h} file. - -In here, we implement the Faa Di Bruno for folded -tensors. Recall, that one step of the Faa Di Bruno is a formula: -$$\left[B_{s^k}\right]_{\alpha_1\ldots\alpha_k}= -[h_{y^l}]_{\gamma_1\ldots\gamma_l} -\prod_{m=1}^l\left[g_{s^{\vert c_m\vert}}\right]^{\gamma_m}_{c_m(\alpha)} -$$ - -In contrast to unfolded implementation of |UGSContainer::multAndAdd| -with help of |KronProdAll| and |UPSTensor|, we take a completely -different strategy. We cannot afford full instantiation of -$$\sum_{c\in M_{l,k}} -\prod_{m=1}^l\left[g_{s^{\vert c_m\vert}}\right]^{\gamma_m}_{c_m(\alpha)}$$ -and therefore we do it per partes. We select some number of columns, -for instance 10, calculate 10 continuous iterators of tensor $B$. Then we -form unfolded tensor -$$[G]_S^{\gamma_1\ldots\gamma_l}=\left[\sum_{c\in M_{l,k}} -\prod_{m=1}^l\left[g_{s^{\vert c_m\vert}}\right]^{\gamma_m}_{c_m(\alpha)} -\right]_S$$ -where $S$ is the selected set of 10 indices. This is done as Kronecker -product of vectors corresponding to selected columns. Note that, in -general, there is no symmetry in $G$, its type is special class for -this purpose. - -If $g$ is folded, then we have to form folded version of $G$. There is -no symmetry in $G$ data, so we sum all unfolded indices corresponding -to folded index together. This is perfectly OK, since we multiply -these groups of (equivalent) items with the same number in fully -symmetric $g$. - -After this, we perform ordinary matrix multiplication to obtain a -selected set of columns of $B$. - -In here, we define a class for forming and representing -$[G]_S^{\gamma_1\ldots\gamma_l}$. Basically, this tensor is -row-oriented (multidimensional index is along rows), and it is fully -symmetric. So we inherit from |URTensor|. If we need its folded -version, we simply use a suitable conversion. The new abstraction will -have only a new constructor allowing a construction from the given set -of indices $S$, and given set of tensors $g$. The rest of the process -is implemented in |@<|FGSContainer::multAndAdd| unfolded code@>| or -|@<|FGSContainer::multAndAdd| folded code@>|. - -@c -#ifndef PYRAMID_PROD_H -#define PYRAMID_PROD_H - -#include "int_sequence.h" -#include "rfs_tensor.h" -#include "gs_tensor.h" -#include "t_container.h" - -#include - -using namespace std; - -@<|USubTensor| class declaration@>; - -#endif - -@ Here we define the new tensor for representing -$[G]_S^{\gamma_1\ldots\gamma_l}$. It allows a construction from -container of folded general symmetry tensors |cont|, and set of -indices |ts|. Also we have to supply dimensions of resulting tensor -$B$, and dimensions of tensor $h$. - -@<|USubTensor| class declaration@>= -class USubTensor : public URTensor { -public:@; - USubTensor(const TensorDimens& bdims, const TensorDimens& hdims, - const FGSContainer& cont, const vector& lst); - void addKronColumn(int i, const vector& ts, - const IntSequence& pindex); -}; - -@ End of {\tt pyramid\_prod.h} file. diff --git a/dynare++/tl/cc/pyramid_prod2.cc b/dynare++/tl/cc/pyramid_prod2.cc new file mode 100644 index 000000000..d40cf27ea --- /dev/null +++ b/dynare++/tl/cc/pyramid_prod2.cc @@ -0,0 +1,116 @@ +// Copyright 2004, Ondra Kamenik + +#include "pyramid_prod2.hh" +#include "rfs_tensor.hh" + +/* Here we only call |sp.createPackedColumns(c, cols, unit_flag)| which + fills |cols| and |unit_flag| for the given column |c|. Then we set + |end_seq| according to |unit_flag| and columns lengths. */ + +IrregTensorHeader::IrregTensorHeader(const StackProduct &sp, + const IntSequence &c) + : nv(sp.getAllSize()), + unit_flag(sp.dimen()), + cols(new Vector *[sp.dimen()]), + end_seq(sp.dimen()) +{ + sp.createPackedColumns(c, cols, unit_flag); + for (int i = 0; i < sp.dimen(); i++) + { + end_seq[i] = cols[i]->length(); + if (unit_flag[i] != -1) + end_seq[i] = unit_flag[i]+1; + } +} + +/* Here we have to increment the given integer sequence. We do it by + the following code, whose pattern is valid for all tensor. The only + difference is how we increment item of coordinates. */ + +void +IrregTensorHeader::increment(IntSequence &v) const +{ + TL_RAISE_IF(v.size() != dimen(), + "Wrong size of coordinates in IrregTensorHeader::increment"); + + if (v.size() == 0) + return; + int i = v.size()-1; + + // increment |i|-th item in coordinate |v| + /* Here we increment item of coordinates. Whenever we reached end of + column coming from matrices, and |unit_flag| is not $-1$, we have to + jump to that |unit_flag|. */ + v[i]++; + if (unit_flag[i] != -1 && v[i] == cols[i]->length()-1) + v[i] = unit_flag[i]; + + while (i > 0 && v[i] == end_seq[i]) + { + v[i] = 0; + i--; + // increment |i|-th item in coordinate |v| + /* Same code as above */ + v[i]++; + if (unit_flag[i] != -1 && v[i] == cols[i]->length()-1) + v[i] = unit_flag[i]; + } +} + +IrregTensorHeader::~IrregTensorHeader() +{ + for (int i = 0; i < dimen(); i++) + delete cols[i]; + delete [] cols; +} + +/* It is a product of all column lengths. */ + +int +IrregTensorHeader::calcMaxOffset() const +{ + int res = 1; + for (int i = 0; i < dimen(); i++) + res *= cols[i]->length(); + return res; +} + +/* Everything is done in |IrregTensorHeader|, only we have to Kronecker + multiply all columns of the header. */ + +IrregTensor::IrregTensor(const IrregTensorHeader &h) + : Tensor(along_row, IntSequence(h.dimen(), 0), h.end_seq, + h.calcMaxOffset(), 1, h.dimen()), + header(h) +{ + if (header.dimen() == 1) + { + getData() = *(header.cols[0]); + return; + } + + Vector *last = new Vector(*(header.cols[header.dimen()-1])); + for (int i = header.dimen()-2; i > 0; i--) + { + Vector *newlast = new Vector(last->length()*header.cols[i]->length()); + KronProd::kronMult(ConstVector(*(header.cols[i])), + ConstVector(*last), *newlast); + delete last; + last = newlast; + } + KronProd::kronMult(ConstVector(*(header.cols[0])), + ConstVector(*last), getData()); + delete last; +} + +void +IrregTensor::addTo(FRSingleTensor &out) const +{ + for (index it = begin(); it != end(); ++it) + { + IntSequence tmp(it.getCoor()); + tmp.sort(); + Tensor::index ind(&out, tmp); + out.get(*ind, 0) += get(*it, 0); + } +} diff --git a/dynare++/tl/cc/pyramid_prod2.cweb b/dynare++/tl/cc/pyramid_prod2.cweb deleted file mode 100644 index 1f894d01a..000000000 --- a/dynare++/tl/cc/pyramid_prod2.cweb +++ /dev/null @@ -1,129 +0,0 @@ -@q $Id: pyramid_prod2.cweb 332 2005-07-15 13:41:48Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@ Start of {\tt pyramid\_prod2.cpp} file. - -@c -#include "pyramid_prod2.h" -#include "rfs_tensor.h" - -@<|IrregTensorHeader| constructor code@>; -@<|IrregTensorHeader::increment| code@>; -@<|IrregTensorHeader| destructor code@>; -@<|IrregTensorHeader::calcMaxOffset| code@>; -@<|IrregTensor| constructor code@>; -@<|IrregTensor::addTo| code@>; - -@ Here we only call |sp.createPackedColumns(c, cols, unit_flag)| which -fills |cols| and |unit_flag| for the given column |c|. Then we set -|end_seq| according to |unit_flag| and columns lengths. - -@<|IrregTensorHeader| constructor code@>= -IrregTensorHeader::IrregTensorHeader(const StackProduct& sp, - const IntSequence& c) - : nv(sp.getAllSize()), - unit_flag(sp.dimen()), - cols(new Vector*[sp.dimen()]), - end_seq(sp.dimen()) -{ - sp.createPackedColumns(c, cols, unit_flag); - for (int i = 0; i < sp.dimen(); i++) { - end_seq[i] = cols[i]->length(); - if (unit_flag[i] != -1) - end_seq[i] = unit_flag[i]+1; - } -} - - -@ Here we have to increment the given integer sequence. We do it by -the following code, whose pattern is valid for all tensor. The only -difference is how we increment item of coordinates. - -@<|IrregTensorHeader::increment| code@>= -void IrregTensorHeader::increment(IntSequence& v) const -{ - TL_RAISE_IF(v.size() != dimen(), - "Wrong size of coordinates in IrregTensorHeader::increment"); - - if (v.size() == 0) - return; - int i = v.size()-1; - @; - while (i > 0 && v[i] == end_seq[i]) { - v[i] = 0; - i--; - @; - } -} - -@ Here we increment item of coordinates. Whenever we reached end of -column coming from matrices, and |unit_flag| is not $-1$, we have to -jump to that |unit_flag|. - -@= - v[i]++; - if (unit_flag[i] != -1 && v[i] == cols[i]->length()-1) - v[i] = unit_flag[i]; - - -@ -@<|IrregTensorHeader| destructor code@>= -IrregTensorHeader::~IrregTensorHeader() -{ - for (int i = 0; i < dimen(); i++) - delete cols[i]; - delete [] cols; -} - -@ It is a product of all column lengths. -@<|IrregTensorHeader::calcMaxOffset| code@>= -int IrregTensorHeader::calcMaxOffset() const -{ - int res = 1; - for (int i = 0; i < dimen(); i++) - res *= cols[i]->length(); - return res; -} - - -@ Everything is done in |IrregTensorHeader|, only we have to Kronecker -multiply all columns of the header. - -@<|IrregTensor| constructor code@>= -IrregTensor::IrregTensor(const IrregTensorHeader& h) - : Tensor(along_row, IntSequence(h.dimen(), 0), h.end_seq, - h.calcMaxOffset(), 1, h.dimen()), - header(h) -{ - if (header.dimen() == 1) { - getData() = *(header.cols[0]); - return; - } - - Vector* last = new Vector(*(header.cols[header.dimen()-1])); - for (int i = header.dimen()-2; i > 0; i--) { - Vector* newlast = new Vector(last->length()*header.cols[i]->length()); - KronProd::kronMult(ConstVector(*(header.cols[i])), - ConstVector(*last), *newlast); - delete last; - last = newlast; - } - KronProd::kronMult(ConstVector(*(header.cols[0])), - ConstVector(*last), getData()); - delete last; -} - -@ Clear. -@<|IrregTensor::addTo| code@>= -void IrregTensor::addTo(FRSingleTensor& out) const -{ - for (index it = begin(); it != end(); ++it) { - IntSequence tmp(it.getCoor()); - tmp.sort(); - Tensor::index ind(&out, tmp); - out.get(*ind, 0) += get(*it, 0); - } -} - - -@ End of {\tt pyramid\_prod2.cpp} file. diff --git a/dynare++/tl/cc/pyramid_prod2.hh b/dynare++/tl/cc/pyramid_prod2.hh new file mode 100644 index 000000000..5594cf063 --- /dev/null +++ b/dynare++/tl/cc/pyramid_prod2.hh @@ -0,0 +1,155 @@ +// Copyright 2004, Ondra Kamenik + +// Multiplying stacked tensor columns. + +/* We need to calculate the following tensor product: + $$\left[f_{s^j}\right]_{\alpha_1\ldots\alpha_j}= + \sum_{l=1}^j\left[f_{z^l}\right]_{\beta_1\ldots\beta_l} + \sum_{c\in M_{l,j}}\prod_{m=1}^l\left[z_{c_m}\right]^{\beta_m}_{c_m(\alpha)} + $$ + where $s=[y,u,u',\sigma]$, and $z$ is a composition of four variables, + say $[v,w,y,u]$. Note that $z$ ends with $y$ and $u$, and the only + non-zero derivative of the trailing part of $z$ involving $y$ or $u$ + is the first derivative and is the unit matrix $y_y=[1]$ or + $u_u=[1]$. Also, we suppose that the dependence of $v$, and $w$ on $s$ + is such that whenever derivative of $w$ is nonzero, then also of + $v$. This means that there for any derivative and any index there is a + continuous part of derivatives of $v$ and optionally of $w$ followed by + column of zeros containing at most one $1$. + + This structure can be modelled and exploited with some costs at + programming. For example, let us consider the following product: + $$\left[B_{y^2u^3}\right]_{\alpha_1\alpha_2\beta_1\beta_2\beta_3}= + \ldots + \left[f_{z^3}\right]_{\gamma_1\gamma_2\gamma_3} + \left[z_{yu}\right]^{\gamma_1}_{\alpha_1\beta_1} + \left[z_{y}\right]^{\gamma_2}_{\alpha_2} + \left[z_{uu}\right]^{\gamma_3}_{\beta_2\beta_3} + \ldots$$ + The term corresponds to equivalence $\{\{0,2\},\{1\},\{3,4\}\}$. For + the fixed index $\alpha_1\alpha_2\beta_1\beta_2\beta_3$ we have to + make a Kronecker product of the columns + $$ + \left[z_{yu}\right]_{\alpha_1\beta_1}\otimes + \left[z_{y}\right]_{\alpha_2}\otimes + \left[z_{uu}\right]_{\beta_2\beta_3} + $$ + which can be written as + $$ + \left[\matrix{\left[v_{yu}\right]_{\alpha_1\beta_1}\cr + \left[w_{yu}\right]_{\alpha_1\beta_1}\cr 0\cr 0}\right]\otimes + \left[\matrix{\left[v_y\right]_{\alpha_2\vphantom{(}}\cr + \left[w_y\right]_{\alpha_2}\cr 1_{\alpha_2}\cr 0}\right]\otimes + \left[\matrix{\left[v_{uu}\right]_{\beta_2\beta_3\vphantom{(}}\cr + \left[w_{uu}\right]_{\beta_2\beta_3}\cr 0\cr 0}\right] + $$ + where $1_{\alpha_2}$ is a column of zeros having the only $1$ at + $\alpha_2$ index. + + This file develops the abstraction for this Kronecker product column + without multiplication of the zeros at the top. Basically, it will be + a column which is a Kronecker product of the columns without the + zeros: + $$ + \left[\matrix{\left[v_{yu}\right]_{\alpha_1\beta_1}\cr + \left[w_{yu}\right]_{\alpha_1\beta_1}}\right]\otimes + \left[\matrix{\left[v_y\right]_{\alpha_2}\cr + \left[w_y\right]_{\alpha_2}\cr 1}\right]\otimes + \left[\matrix{\left[v_{uu}\right]_{\beta_2\beta_3}\cr + \left[w_{uu}\right]_{\beta_2\beta_3}}\right] + $$ + The class will have a tensor infrastructure introducing |index| which + iterates over all items in the column with $\gamma_1\gamma_2\gamma_3$ + as coordinates in $\left[f_{z^3}\right]$. The data of such a tensor is + not suitable for any matrix operation and will have to be accessed + only through the |index|. Note that this does not matter, since + $\left[f_{z^l}\right]$ are sparse. */ + +#ifndef PYRAMID_PROD2_H +#define PYRAMID_PROD2_H + +#include "permutation.hh" +#include "tensor.hh" +#include "tl_exception.hh" +#include "rfs_tensor.hh" +#include "stack_container.hh" + +#include "Vector.h" + +/* First we declare a helper class for the tensor. Its purpose is to + gather the columns which are going to be Kronecker multiplied. The + input of this helper class is |StackProduct| and coordinate + |c| of the column. + + It maintains |unit_flag| array which says for what columns we must + stack 1 below $v$ and $w$. In this case, the value of |unit_flag| is + an index of the $1$, otherwise the value of |unit_flag| is -1. + + Also we have storage for the stacked columns |cols|. The object is + responsible for memory management associated to this storage. That is + why we do not allow any copy constructor, since we need to be sure + that no accidental copies take place. We declare the copy constructor + as private and not implement it. */ + +class IrregTensor; +class IrregTensorHeader +{ + friend class IrregTensor; + int nv; + IntSequence unit_flag; + Vector **const cols; + IntSequence end_seq; +public: + IrregTensorHeader(const StackProduct &sp, const IntSequence &c); + ~IrregTensorHeader(); + int + dimen() const + { + return unit_flag.size(); + } + void increment(IntSequence &v) const; + int calcMaxOffset() const; +private: + IrregTensorHeader(const IrregTensorHeader &); +}; + +/* Here we declare the irregular tensor. There is no special logic + here. We inherit from |Tensor| and we must implement three methods, + |increment|, |decrement| and |getOffset|. The last two are not + implemented now, since they are not needed, and they raise an + exception. The first just calls |increment| of the header. Also we + declare a method |addTo| which adds this unfolded irregular single + column tensor to folded (regular) single column tensor. + + The header |IrregTensorHeader| lives with an object by a + reference. This is dangerous. However, we will use this class only in + a simple loop and both |IrregTensor| and |IrregTensorHeader| will be + destructed at the end of a block. Since the super class |Tensor| must + be initialized before any member, we could do either a save copy of + |IrregTensorHeader|, or relatively dangerous the reference member. For + the reason above we chose the latter. */ + +class IrregTensor : public Tensor +{ + const IrregTensorHeader &header; +public: + IrregTensor(const IrregTensorHeader &h); + void addTo(FRSingleTensor &out) const; + void + increment(IntSequence &v) const + { + header.increment(v); + } + void + decrement(IntSequence &v) const + { + TL_RAISE("Not implemented error in IrregTensor::decrement"); + } + int + getOffset(const IntSequence &v) const + { + TL_RAISE("Not implemented error in IrregTensor::getOffset"); return 0; + } +}; + +#endif diff --git a/dynare++/tl/cc/pyramid_prod2.hweb b/dynare++/tl/cc/pyramid_prod2.hweb deleted file mode 100644 index 42cf370ed..000000000 --- a/dynare++/tl/cc/pyramid_prod2.hweb +++ /dev/null @@ -1,151 +0,0 @@ -@q $Id: pyramid_prod2.hweb 148 2005-04-19 15:12:26Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@*2 Multiplying stacked tensor columns. Start of {\tt pyramid\_prod2.h} file. - -We need to calculate the following tensor product: -$$\left[f_{s^j}\right]_{\alpha_1\ldots\alpha_j}= -\sum_{l=1}^j\left[f_{z^l}\right]_{\beta_1\ldots\beta_l} -\sum_{c\in M_{l,j}}\prod_{m=1}^l\left[z_{c_m}\right]^{\beta_m}_{c_m(\alpha)} -$$ -where $s=[y,u,u',\sigma]$, and $z$ is a composition of four variables, -say $[v,w,y,u]$. Note that $z$ ends with $y$ and $u$, and the only -non-zero derivative of the trailing part of $z$ involving $y$ or $u$ -is the first derivative and is the unit matrix $y_y=[1]$ or -$u_u=[1]$. Also, we suppose that the dependence of $v$, and $w$ on $s$ -is such that whenever derivative of $w$ is nonzero, then also of -$v$. This means that there for any derivative and any index there is a -continuous part of derivatives of $v$ and optionally of $w$ followed by -column of zeros containing at most one $1$. - -This structure can be modelled and exploited with some costs at -programming. For example, let us consider the following product: -$$\left[B_{y^2u^3}\right]_{\alpha_1\alpha_2\beta_1\beta_2\beta_3}= -\ldots -\left[f_{z^3}\right]_{\gamma_1\gamma_2\gamma_3} -\left[z_{yu}\right]^{\gamma_1}_{\alpha_1\beta_1} -\left[z_{y}\right]^{\gamma_2}_{\alpha_2} -\left[z_{uu}\right]^{\gamma_3}_{\beta_2\beta_3} -\ldots$$ -The term corresponds to equivalence $\{\{0,2\},\{1\},\{3,4\}\}$. For -the fixed index $\alpha_1\alpha_2\beta_1\beta_2\beta_3$ we have to -make a Kronecker product of the columns -$$ -\left[z_{yu}\right]_{\alpha_1\beta_1}\otimes -\left[z_{y}\right]_{\alpha_2}\otimes -\left[z_{uu}\right]_{\beta_2\beta_3} -$$ -which can be written as -$$ -\left[\matrix{\left[v_{yu}\right]_{\alpha_1\beta_1}\cr - \left[w_{yu}\right]_{\alpha_1\beta_1}\cr 0\cr 0}\right]\otimes -\left[\matrix{\left[v_y\right]_{\alpha_2\vphantom{(}}\cr - \left[w_y\right]_{\alpha_2}\cr 1_{\alpha_2}\cr 0}\right]\otimes -\left[\matrix{\left[v_{uu}\right]_{\beta_2\beta_3\vphantom{(}}\cr - \left[w_{uu}\right]_{\beta_2\beta_3}\cr 0\cr 0}\right] -$$ -where $1_{\alpha_2}$ is a column of zeros having the only $1$ at -$\alpha_2$ index. - -This file develops the abstraction for this Kronecker product column -without multiplication of the zeros at the top. Basically, it will be -a column which is a Kronecker product of the columns without the -zeros: -$$ -\left[\matrix{\left[v_{yu}\right]_{\alpha_1\beta_1}\cr - \left[w_{yu}\right]_{\alpha_1\beta_1}}\right]\otimes -\left[\matrix{\left[v_y\right]_{\alpha_2}\cr - \left[w_y\right]_{\alpha_2}\cr 1}\right]\otimes -\left[\matrix{\left[v_{uu}\right]_{\beta_2\beta_3}\cr - \left[w_{uu}\right]_{\beta_2\beta_3}}\right] -$$ -The class will have a tensor infrastructure introducing |index| which -iterates over all items in the column with $\gamma_1\gamma_2\gamma_3$ -as coordinates in $\left[f_{z^3}\right]$. The data of such a tensor is -not suitable for any matrix operation and will have to be accessed -only through the |index|. Note that this does not matter, since -$\left[f_{z^l}\right]$ are sparse. - -@c -#ifndef PYRAMID_PROD2_H -#define PYRAMID_PROD2_H - -#include "permutation.h" -#include "tensor.h" -#include "tl_exception.h" -#include "rfs_tensor.h" -#include "stack_container.h" - -#include "Vector.h" - -@<|IrregTensorHeader| class declaration@>; -@<|IrregTensor| class declaration@>; - -#endif - -@ First we declare a helper class for the tensor. Its purpose is to -gather the columns which are going to be Kronecker multiplied. The -input of this helper class is |StackProduct| and coordinate -|c| of the column. - -It maintains |unit_flag| array which says for what columns we must -stack 1 below $v$ and $w$. In this case, the value of |unit_flag| is -an index of the $1$, otherwise the value of |unit_flag| is -1. - -Also we have storage for the stacked columns |cols|. The object is -responsible for memory management associated to this storage. That is -why we do not allow any copy constructor, since we need to be sure -that no accidental copies take place. We declare the copy constructor -as private and not implement it. - -@<|IrregTensorHeader| class declaration@>= -class IrregTensor; -class IrregTensorHeader { - friend class IrregTensor; - int nv; - IntSequence unit_flag; - Vector** const cols; - IntSequence end_seq; -public:@; - IrregTensorHeader(const StackProduct& sp, const IntSequence& c); - ~IrregTensorHeader(); - int dimen() const - {@+ return unit_flag.size();@+} - void increment(IntSequence& v) const; - int calcMaxOffset() const; -private:@; - IrregTensorHeader(const IrregTensorHeader&); -}; - - -@ Here we declare the irregular tensor. There is no special logic -here. We inherit from |Tensor| and we must implement three methods, -|increment|, |decrement| and |getOffset|. The last two are not -implemented now, since they are not needed, and they raise an -exception. The first just calls |increment| of the header. Also we -declare a method |addTo| which adds this unfolded irregular single -column tensor to folded (regular) single column tensor. - -The header |IrregTensorHeader| lives with an object by a -reference. This is dangerous. However, we will use this class only in -a simple loop and both |IrregTensor| and |IrregTensorHeader| will be -destructed at the end of a block. Since the super class |Tensor| must -be initialized before any member, we could do either a save copy of -|IrregTensorHeader|, or relatively dangerous the reference member. For -the reason above we chose the latter. - -@<|IrregTensor| class declaration@>= -class IrregTensor : public Tensor { - const IrregTensorHeader& header; -public:@; - IrregTensor(const IrregTensorHeader& h); - void addTo(FRSingleTensor& out) const; - void increment(IntSequence& v) const - {@+ header.increment(v);@+} - void decrement(IntSequence& v) const - {@+ TL_RAISE("Not implemented error in IrregTensor::decrement");@+} - int getOffset(const IntSequence& v) const - {@+ TL_RAISE("Not implemented error in IrregTensor::getOffset");@+return 0;@+} -}; - -@ End of {\tt pyramid\_prod2.h} file. diff --git a/dynare++/tl/cc/rfs_tensor.cc b/dynare++/tl/cc/rfs_tensor.cc new file mode 100644 index 000000000..2e1fdb701 --- /dev/null +++ b/dynare++/tl/cc/rfs_tensor.cc @@ -0,0 +1,187 @@ +// Copyright 2004, Ondra Kamenik + +#include "rfs_tensor.hh" +#include "kron_prod.hh" +#include "tl_exception.hh" + +// |FRTensor| conversion from unfolded +/* The conversion from unfolded to folded sums up all data from + unfolded corresponding to one folded index. So we go through all the + rows in the unfolded tensor |ut|, make an index of the folded tensor + by sorting the coordinates, and add the row. */ +FRTensor::FRTensor(const URTensor &ut) + : FTensor(along_row, IntSequence(ut.dimen(), ut.nvar()), + FFSTensor::calcMaxOffset(ut.nvar(), ut.dimen()), ut.ncols(), + ut.dimen()), + nv(ut.nvar()) +{ + zeros(); + for (index in = ut.begin(); in != ut.end(); ++in) + { + IntSequence vtmp(in.getCoor()); + vtmp.sort(); + index tar(this, vtmp); + addRow(ut, *in, *tar); + } +} + +/* Here just make a new instance and return the reference. */ + +UTensor & +FRTensor::unfold() const +{ + return *(new URTensor(*this)); +} + +/* Incrementing is easy. The same as for |FFSTensor|. */ + +void +FRTensor::increment(IntSequence &v) const +{ + TL_RAISE_IF(v.size() != dimen(), + "Wrong input/output vector size in FRTensor::increment"); + + UTensor::increment(v, nv); + v.monotone(); +} + +/* Decrement calls static |FTensor::decrement|. */ + +void +FRTensor::decrement(IntSequence &v) const +{ + TL_RAISE_IF(v.size() != dimen(), + "Wrong input/output vector size in FRTensor::decrement"); + + FTensor::decrement(v, nv); +} + +// |URTensor| conversion from folded +/* Here we convert folded full symmetry tensor to unfolded. We copy all + columns of folded tensor to unfolded and leave other columns + (duplicates) zero. In this way, if the unfolded tensor is folded back, + we should get the same data. */ +URTensor::URTensor(const FRTensor &ft) + : UTensor(along_row, IntSequence(ft.dimen(), ft.nvar()), + UFSTensor::calcMaxOffset(ft.nvar(), ft.dimen()), ft.ncols(), + ft.dimen()), + nv(ft.nvar()) +{ + zeros(); + for (index src = ft.begin(); src != ft.end(); ++src) + { + index in(this, src.getCoor()); + copyRow(ft, *src, *in); + } +} + +/* Here we just return a reference to new instance of folded tensor. */ + +FTensor & +URTensor::fold() const +{ + return *(new FRTensor(*this)); +} + +/* Here we just call |UTensor| respective static methods. */ + +void +URTensor::increment(IntSequence &v) const +{ + TL_RAISE_IF(v.size() != dimen(), + "Wrong input/output vector size in URTensor::increment"); + + UTensor::increment(v, nv); +} + +void +URTensor::decrement(IntSequence &v) const +{ + TL_RAISE_IF(v.size() != dimen(), + "Wrong input/output vector size in URTensor::decrement"); + + UTensor::decrement(v, nv); +} + +int +URTensor::getOffset(const IntSequence &v) const +{ + TL_RAISE_IF(v.size() != dimen(), + "Wrong input vector size in URTensor::getOffset"); + + return UTensor::getOffset(v, nv); +} + +/* Here we construct $v_1\otimes v_2\otimes\ldots\otimes v_n$, where + $v_1,v_2,\ldots,v_n$ are stored in |vector|. */ + +URSingleTensor::URSingleTensor(const vector &cols) + : URTensor(1, cols[0].length(), cols.size()) +{ + if (dimen() == 1) + { + getData() = cols[0]; + return; + } + + Vector *last = new Vector(cols[cols.size()-1]); + for (int i = cols.size()-2; i > 0; i--) + { + Vector *newlast = new Vector(Tensor::power(nvar(), cols.size()-i)); + KronProd::kronMult(cols[i], ConstVector(*last), *newlast); + delete last; + last = newlast; + } + KronProd::kronMult(cols[0], ConstVector(*last), getData()); + delete last; +} + +/* Here we construct $v\otimes\ldots\otimes v$, where the number of $v$ + copies is |d|. */ + +URSingleTensor::URSingleTensor(const ConstVector &v, int d) + : URTensor(1, v.length(), d) +{ + if (d == 1) + { + getData() = v; + return; + } + + Vector *last = new Vector(v); + for (int i = d-2; i > 0; i--) + { + Vector *newlast = new Vector(last->length()*v.length()); + KronProd::kronMult(v, ConstVector(*last), *newlast); + delete last; + last = newlast; + } + KronProd::kronMult(v, ConstVector(*last), getData()); + delete last; +} + +/* Here we construct |FRSingleTensor| from |URSingleTensor| and return + its reference. */ + +FTensor & +URSingleTensor::fold() const +{ + return *(new FRSingleTensor(*this)); +} + +// |FRSingleTensor| conversion from unfolded +/* The conversion from unfolded |URSingleTensor| to folded + |FRSingleTensor| is completely the same as conversion from |URTensor| + to |FRTensor|, only we do not copy rows but elements. */ +FRSingleTensor::FRSingleTensor(const URSingleTensor &ut) + : FRTensor(1, ut.nvar(), ut.dimen()) +{ + zeros(); + for (index in = ut.begin(); in != ut.end(); ++in) + { + IntSequence vtmp(in.getCoor()); + vtmp.sort(); + index tar(this, vtmp); + get(*tar, 0) += ut.get(*in, 0); + } +} diff --git a/dynare++/tl/cc/rfs_tensor.cweb b/dynare++/tl/cc/rfs_tensor.cweb deleted file mode 100644 index 33c6c93bf..000000000 --- a/dynare++/tl/cc/rfs_tensor.cweb +++ /dev/null @@ -1,205 +0,0 @@ -@q $Id: rfs_tensor.cweb 148 2005-04-19 15:12:26Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@ Start of {\tt rfs\_tensor.cpp} file. - -@c -#include "rfs_tensor.h" -#include "kron_prod.h" -#include "tl_exception.h" - -@<|FRTensor| conversion from unfolded@>; -@<|FRTensor::unfold| code@>; -@<|FRTensor::increment| code@>; -@<|FRTensor::decrement| code@>; -@<|URTensor| conversion from folded@>; -@<|URTensor::fold| code@>; -@<|URTensor| increment and decrement@>; -@<|URTensor::getOffset| code@>; -@<|URSingleTensor| constructor 1 code@>; -@<|URSingleTensor| constructor 2 code@>; -@<|URSingleTensor::fold| code@>; -@<|FRSingleTensor| conversion from unfolded@>; - -@ The conversion from unfolded to folded sums up all data from -unfolded corresponding to one folded index. So we go through all the -rows in the unfolded tensor |ut|, make an index of the folded tensor -by sorting the coordinates, and add the row. - -@<|FRTensor| conversion from unfolded@>= -FRTensor::FRTensor(const URTensor& ut) - : FTensor(along_row, IntSequence(ut.dimen(), ut.nvar()), - FFSTensor::calcMaxOffset(ut.nvar(), ut.dimen()), ut.ncols(), - ut.dimen()), - nv(ut.nvar()) -{ - zeros(); - for (index in = ut.begin(); in != ut.end(); ++in) { - IntSequence vtmp(in.getCoor()); - vtmp.sort(); - index tar(this, vtmp); - addRow(ut, *in, *tar); - } -} - -@ Here just make a new instance and return the reference. -@<|FRTensor::unfold| code@>= -UTensor& FRTensor::unfold() const -{ - return *(new URTensor(*this)); -} - -@ Incrementing is easy. The same as for |FFSTensor|. - -@<|FRTensor::increment| code@>= -void FRTensor::increment(IntSequence& v) const -{ - TL_RAISE_IF(v.size() != dimen(), - "Wrong input/output vector size in FRTensor::increment"); - - UTensor::increment(v, nv); - v.monotone(); -} - -@ Decrement calls static |FTensor::decrement|. - -@<|FRTensor::decrement| code@>= -void FRTensor::decrement(IntSequence& v) const -{ - TL_RAISE_IF(v.size() != dimen(), - "Wrong input/output vector size in FRTensor::decrement"); - - FTensor::decrement(v, nv); -} - - -@ Here we convert folded full symmetry tensor to unfolded. We copy all -columns of folded tensor to unfolded and leave other columns -(duplicates) zero. In this way, if the unfolded tensor is folded back, -we should get the same data. - -@<|URTensor| conversion from folded@>= -URTensor::URTensor(const FRTensor& ft) - : UTensor(along_row, IntSequence(ft.dimen(), ft.nvar()), - UFSTensor::calcMaxOffset(ft.nvar(), ft.dimen()), ft.ncols(), - ft.dimen()), - nv(ft.nvar()) -{ - zeros(); - for (index src = ft.begin(); src != ft.end(); ++src) { - index in(this, src.getCoor()); - copyRow(ft, *src, *in); - } -} - -@ Here we just return a reference to new instance of folded tensor. -@<|URTensor::fold| code@>= -FTensor& URTensor::fold() const -{ - return *(new FRTensor(*this)); -} - -@ Here we just call |UTensor| respective static methods. -@<|URTensor| increment and decrement@>= -void URTensor::increment(IntSequence& v) const -{ - TL_RAISE_IF(v.size() != dimen(), - "Wrong input/output vector size in URTensor::increment"); - - UTensor::increment(v, nv); -} - -void URTensor::decrement(IntSequence& v) const -{ - TL_RAISE_IF(v.size() != dimen(), - "Wrong input/output vector size in URTensor::decrement"); - - UTensor::decrement(v, nv); -} - -@ -@<|URTensor::getOffset| code@>= -int URTensor::getOffset(const IntSequence& v) const -{ - TL_RAISE_IF(v.size() != dimen(), - "Wrong input vector size in URTensor::getOffset"); - - return UTensor::getOffset(v, nv); -} - -@ Here we construct $v_1\otimes v_2\otimes\ldots\otimes v_n$, where -$v_1,v_2,\ldots,v_n$ are stored in |vector|. - -@<|URSingleTensor| constructor 1 code@>= -URSingleTensor::URSingleTensor(const vector& cols) - : URTensor(1, cols[0].length(), cols.size()) -{ - if (dimen() == 1) { - getData() = cols[0]; - return; - } - - Vector* last = new Vector(cols[cols.size()-1]); - for (int i = cols.size()-2; i > 0; i--) { - Vector* newlast = new Vector(Tensor::power(nvar(), cols.size()-i)); - KronProd::kronMult(cols[i], ConstVector(*last), *newlast); - delete last; - last = newlast; - } - KronProd::kronMult(cols[0], ConstVector(*last), getData()); - delete last; -} - -@ Here we construct $v\otimes\ldots\otimes v$, where the number of $v$ -copies is |d|. - -@<|URSingleTensor| constructor 2 code@>= -URSingleTensor::URSingleTensor(const ConstVector& v, int d) - : URTensor(1, v.length(), d) -{ - if (d == 1) { - getData() = v; - return; - } - - Vector* last = new Vector(v); - for (int i = d-2; i > 0; i--) { - Vector* newlast = new Vector(last->length()*v.length()); - KronProd::kronMult(v, ConstVector(*last), *newlast); - delete last; - last = newlast; - } - KronProd::kronMult(v, ConstVector(*last), getData()); - delete last; -} - -@ Here we construct |FRSingleTensor| from |URSingleTensor| and return -its reference. - -@<|URSingleTensor::fold| code@>= -FTensor& URSingleTensor::fold() const -{ - return *(new FRSingleTensor(*this)); -} - - - -@ The conversion from unfolded |URSingleTensor| to folded -|FRSingleTensor| is completely the same as conversion from |URTensor| -to |FRTensor|, only we do not copy rows but elements. - -@<|FRSingleTensor| conversion from unfolded@>= -FRSingleTensor::FRSingleTensor(const URSingleTensor& ut) - : FRTensor(1, ut.nvar(), ut.dimen()) -{ - zeros(); - for (index in = ut.begin(); in != ut.end(); ++in) { - IntSequence vtmp(in.getCoor()); - vtmp.sort(); - index tar(this, vtmp); - get(*tar, 0) += ut.get(*in, 0); - } -} - - -@ End of {\tt rfs\_tensor.cpp} file. diff --git a/dynare++/tl/cc/rfs_tensor.hh b/dynare++/tl/cc/rfs_tensor.hh new file mode 100644 index 000000000..219398f69 --- /dev/null +++ b/dynare++/tl/cc/rfs_tensor.hh @@ -0,0 +1,173 @@ +// Copyright 2004, Ondra Kamenik + +// Row-wise full symmetry tensor. + +/* Here we define classes for full symmetry tensors with the + multidimensional index identified with rows. The primary usage is for + storage of data coming from (or from a sum of) + $$\prod_{m=1}^l\left[g_{s^{\vert c_m\vert}}\right]^{\gamma_m}_{c_m(\alpha)}$$ + where $\alpha$ coming from a multidimensional index go through some + set $S$ and $c$ is some equivalence. So we model a tensor of the form: + $$\left[\prod_{m=1}^l + \left[g_{s^{\vert c_m\vert}}\right]^{\gamma_m}_{c_m(\alpha)} + \right]_S^{\gamma_1\ldots\gamma_l}$$ + Since all $\gamma_1,\ldots,\gamma_l$ correspond to the same variable, + the tensor is fully symmetric. The set of indices $S$ cannot be very + large and sometimes it is only one element. This case is handled in a + special subclass. + + We provide both folded and unfolded versions. Their logic is perfectly + the same as in |UFSTensor| and |FFSTensor| with two exceptions. One + has been already mentioned, the multidimensional index is along the + rows. The second are conversions between the two types. Since this + kind of tensor is used to multiply (from the right) a tensor whose + multidimensional index is identified with columns, we will need a + different way of a conversion. If the multiplication of two folded + tensors is to be equivalent with multiplication of two unfolded, the + folding of the right tensor must sum all equivalent elements since + they are multiplied with the same number from the folded + tensor. (Equivalent here means all elements of unfolded tensor + corresponding to one element in folded tensor.) For this reason, it is + necessary to calculate a column number from the given sequence, so we + implement |getOffset|. Process of unfolding is not used, so we + implemented it so that unfolding and then folding a tensor would yield + the same data. */ + +#ifndef RFS_TENSOR_H +#define RFS_TENSOR_H + +#include "tensor.hh" +#include "fs_tensor.hh" +#include "symmetry.hh" + +/* This is straightforward and very similar to |UFSTensor|. */ + +class FRTensor; +class URTensor : public UTensor +{ + int nv; +public: + URTensor(int c, int nvar, int d) + : UTensor(along_row, IntSequence(d, nvar), + UFSTensor::calcMaxOffset(nvar, d), c, d), nv(nvar) + { + } + URTensor(const URTensor &ut) + : UTensor(ut), nv(ut.nv) + { + } + URTensor(const FRTensor &ft); + + virtual ~URTensor() + { + } + + void increment(IntSequence &v) const; + void decrement(IntSequence &v) const; + FTensor&fold() const; + + int getOffset(const IntSequence &v) const; + int + nvar() const + { + return nv; + } + Symmetry + getSym() const + { + return Symmetry(dimen()); + } +}; + +/* This is straightforward and very similar to |FFSTensor|. */ + +class FRTensor : public FTensor +{ + int nv; +public: + FRTensor(int c, int nvar, int d) + : FTensor(along_row, IntSequence(d, nvar), + FFSTensor::calcMaxOffset(nvar, d), c, d), nv(nvar) + { + } + FRTensor(const FRTensor &ft) + : FTensor(ft), nv(ft.nv) + { + } + FRTensor(const URTensor &ut); + + virtual ~FRTensor() + { + } + + void increment(IntSequence &v) const; + void decrement(IntSequence &v) const; + UTensor&unfold() const; + + int + nvar() const + { + return nv; + } + int + getOffset(const IntSequence &v) const + { + return FTensor::getOffset(v, nv); + } + Symmetry + getSym() const + { + return Symmetry(dimen()); + } +}; + +/* The following class represents specialization of |URTensor| coming + from Kronecker multiplication of a few vectors. So the resulting + row-oriented tensor has one column. We provide two constructors, + one constructs the tensor from a few vectors stored as + |vector|. The second makes the Kronecker power of one + given vector. */ + +class URSingleTensor : public URTensor +{ +public: + URSingleTensor(int nvar, int d) + : URTensor(1, nvar, d) + { + } + URSingleTensor(const vector &cols); + URSingleTensor(const ConstVector &v, int d); + URSingleTensor(const URSingleTensor &ut) + : URTensor(ut) + { + } + virtual ~URSingleTensor() + { + } + FTensor&fold() const; +}; + +/* This class represents one column row-oriented tensor. The only way + how to construct it is from the |URSingleTensor| or from the + scratch. The folding algorithm is the same as folding of general + |URTensor|. Only its implementation is different, since we do not copy + rows, but only elements. */ + +class FRSingleTensor : public FRTensor +{ +public: + FRSingleTensor(int nvar, int d) + : FRTensor(1, nvar, d) + { + } + FRSingleTensor(const URSingleTensor &ut); + FRSingleTensor(const FRSingleTensor &ft) + : FRTensor(ft) + { + } + virtual ~FRSingleTensor() + { + } +}; + +#endif diff --git a/dynare++/tl/cc/rfs_tensor.hweb b/dynare++/tl/cc/rfs_tensor.hweb deleted file mode 100644 index b1efe69ae..000000000 --- a/dynare++/tl/cc/rfs_tensor.hweb +++ /dev/null @@ -1,148 +0,0 @@ -@q $Id: rfs_tensor.hweb 741 2006-05-09 11:12:46Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@*2 Row-wise full symmetry tensor. Start of {\tt rfs\_tensor.h} file. - -Here we define classes for full symmetry tensors with the -multidimensional index identified with rows. The primary usage is for -storage of data coming from (or from a sum of) -$$\prod_{m=1}^l\left[g_{s^{\vert c_m\vert}}\right]^{\gamma_m}_{c_m(\alpha)}$$ -where $\alpha$ coming from a multidimensional index go through some -set $S$ and $c$ is some equivalence. So we model a tensor of the form: -$$\left[\prod_{m=1}^l -\left[g_{s^{\vert c_m\vert}}\right]^{\gamma_m}_{c_m(\alpha)} -\right]_S^{\gamma_1\ldots\gamma_l}$$ -Since all $\gamma_1,\ldots,\gamma_l$ correspond to the same variable, -the tensor is fully symmetric. The set of indices $S$ cannot be very -large and sometimes it is only one element. This case is handled in a -special subclass. - -We provide both folded and unfolded versions. Their logic is perfectly -the same as in |UFSTensor| and |FFSTensor| with two exceptions. One -has been already mentioned, the multidimensional index is along the -rows. The second are conversions between the two types. Since this -kind of tensor is used to multiply (from the right) a tensor whose -multidimensional index is identified with columns, we will need a -different way of a conversion. If the multiplication of two folded -tensors is to be equivalent with multiplication of two unfolded, the -folding of the right tensor must sum all equivalent elements since -they are multiplied with the same number from the folded -tensor. (Equivalent here means all elements of unfolded tensor -corresponding to one element in folded tensor.) For this reason, it is -necessary to calculate a column number from the given sequence, so we -implement |getOffset|. Process of unfolding is not used, so we -implemented it so that unfolding and then folding a tensor would yield -the same data. - -@c -#ifndef RFS_TENSOR_H -#define RFS_TENSOR_H - -#include "tensor.h" -#include "fs_tensor.h" -#include "symmetry.h" - -@<|URTensor| class declaration@>; -@<|FRTensor| class declaration@>; -@<|URSingleTensor| class declaration@>; -@<|FRSingleTensor| class declaration@>; - -#endif - -@ This is straightforward and very similar to |UFSTensor|. -@<|URTensor| class declaration@>= -class FRTensor; -class URTensor : public UTensor { - int nv; -public:@; - @<|URTensor| constructor declaration@>; - virtual ~URTensor()@+ {} - - void increment(IntSequence& v) const; - void decrement(IntSequence& v) const; - FTensor& fold() const; - - int getOffset(const IntSequence& v) const; - int nvar() const - {@+ return nv;@+} - Symmetry getSym() const - {@+ return Symmetry(dimen());@+} -}; - -@ -@<|URTensor| constructor declaration@>= - URTensor(int c, int nvar, int d) - : UTensor(along_row, IntSequence(d, nvar), - UFSTensor::calcMaxOffset(nvar, d), c, d), nv(nvar)@+ {} - URTensor(const URTensor& ut) - : UTensor(ut), nv(ut.nv)@+ {} - URTensor(const FRTensor& ft); - -@ This is straightforward and very similar to |FFSTensor|. -@<|FRTensor| class declaration@>= -class FRTensor : public FTensor { - int nv; -public:@; - @<|FRTensor| constructor declaration@>; - virtual ~FRTensor()@+ {} - - void increment(IntSequence& v) const; - void decrement(IntSequence& v) const; - UTensor& unfold() const; - - int nvar() const - {@+ return nv;@+} - int getOffset(const IntSequence& v) const - {@+ return FTensor::getOffset(v, nv);@+} - Symmetry getSym() const - {@+ return Symmetry(dimen());@+} -}; - -@ -@<|FRTensor| constructor declaration@>= - FRTensor(int c, int nvar, int d) - : FTensor(along_row, IntSequence(d, nvar), - FFSTensor::calcMaxOffset(nvar, d), c, d), nv(nvar)@+ {} - FRTensor(const FRTensor& ft) - : FTensor(ft), nv(ft.nv)@+ {} - FRTensor(const URTensor& ut); - -@ The following class represents specialization of |URTensor| coming -from Kronecker multiplication of a few vectors. So the resulting -row-oriented tensor has one column. We provide two constructors, -one constructs the tensor from a few vectors stored as -|vector|. The second makes the Kronecker power of one -given vector. - -@<|URSingleTensor| class declaration@>= -class URSingleTensor : public URTensor { -public:@; - URSingleTensor(int nvar, int d) - : URTensor(1, nvar, d)@+ {} - URSingleTensor(const vector& cols); - URSingleTensor(const ConstVector& v, int d); - URSingleTensor(const URSingleTensor& ut) - : URTensor(ut)@+ {} - virtual ~URSingleTensor()@+ {} - FTensor& fold() const; -}; - -@ This class represents one column row-oriented tensor. The only way -how to construct it is from the |URSingleTensor| or from the -scratch. The folding algorithm is the same as folding of general -|URTensor|. Only its implementation is different, since we do not copy -rows, but only elements. - -@<|FRSingleTensor| class declaration@>= -class FRSingleTensor : public FRTensor { -public:@; - FRSingleTensor(int nvar, int d) - : FRTensor(1, nvar, d)@+ {} - FRSingleTensor(const URSingleTensor& ut); - FRSingleTensor(const FRSingleTensor& ft) - : FRTensor(ft)@+ {} - virtual ~FRSingleTensor()@+ {} -}; - - -@ End of {\tt rfs\_tensor.h} file. diff --git a/dynare++/tl/cc/sparse_tensor.cc b/dynare++/tl/cc/sparse_tensor.cc new file mode 100644 index 000000000..bd3ed02d0 --- /dev/null +++ b/dynare++/tl/cc/sparse_tensor.cc @@ -0,0 +1,248 @@ +// Copyright 2004, Ondra Kamenik + +#include "sparse_tensor.hh" +#include "fs_tensor.hh" +#include "tl_exception.hh" + +#include + +/* This is straightforward. Before we insert anything, we do a few + checks. Then we reset |first_nz_row| and |last_nz_row| if necessary. */ + +void +SparseTensor::insert(const IntSequence &key, int r, double c) +{ + TL_RAISE_IF(r < 0 || r >= nr, + "Row number out of dimension of tensor in SparseTensor::insert"); + TL_RAISE_IF(key.size() != dimen(), + "Wrong length of key in SparseTensor::insert"); + TL_RAISE_IF(!std::isfinite(c), + "Insertion of non-finite value in SparseTensor::insert"); + + iterator first_pos = m.lower_bound(key); + + // check that pair |key| and |r| is unique + iterator last_pos = m.upper_bound(key); + for (iterator it = first_pos; it != last_pos; ++it) + if ((*it).second.first == r) + { + TL_RAISE("Duplicate insertion in SparseTensor::insert"); + return; + } + + m.insert(first_pos, Map::value_type(key, Item(r, c))); + if (first_nz_row > r) + first_nz_row = r; + if (last_nz_row < r) + last_nz_row = r; +} + +/* This returns true if all items are finite (not Nan nor Inf). */ + +bool +SparseTensor::isFinite() const +{ + bool res = true; + const_iterator run = m.begin(); + while (res && run != m.end()) + { + if (!std::isfinite((*run).second.second)) + res = false; + ++run; + } + return res; +} + +/* This returns a ratio of a number of non-zero columns in folded + tensor to the total number of columns. */ + +double +SparseTensor::getFoldIndexFillFactor() const +{ + int cnt = 0; + const_iterator start_col = m.begin(); + while (start_col != m.end()) + { + cnt++; + const IntSequence &key = (*start_col).first; + start_col = m.upper_bound(key); + } + + return ((double) cnt)/ncols(); +} + +/* This returns a ratio of a number of non-zero columns in unfolded + tensor to the total number of columns. */ + +double +SparseTensor::getUnfoldIndexFillFactor() const +{ + int cnt = 0; + const_iterator start_col = m.begin(); + while (start_col != m.end()) + { + const IntSequence &key = (*start_col).first; + Symmetry s(key); + cnt += Tensor::noverseq(s); + start_col = m.upper_bound(key); + } + + return ((double) cnt)/ncols(); +} + +/* This prints the fill factor and all items. */ + +void +SparseTensor::print() const +{ + printf("Fill: %3.2f %%\n", 100*getFillFactor()); + const_iterator start_col = m.begin(); + while (start_col != m.end()) + { + const IntSequence &key = (*start_col).first; + printf("Column: "); key.print(); + const_iterator end_col = m.upper_bound(key); + int cnt = 1; + for (const_iterator run = start_col; run != end_col; ++run, cnt++) + { + if ((cnt/7)*7 == cnt) + printf("\n"); + printf("%d(%6.2g) ", (*run).second.first, (*run).second.second); + } + printf("\n"); + start_col = end_col; + } +} + +FSSparseTensor::FSSparseTensor(int d, int nvar, int r) + : SparseTensor(d, r, FFSTensor::calcMaxOffset(nvar, d)), + nv(nvar), sym(d) +{ +} + +FSSparseTensor::FSSparseTensor(const FSSparseTensor &t) + : SparseTensor(t), + nv(t.nvar()), sym(t.sym) +{ +} + +void +FSSparseTensor::insert(const IntSequence &key, int r, double c) +{ + TL_RAISE_IF(!key.isSorted(), + "Key is not sorted in FSSparseTensor::insert"); + TL_RAISE_IF(key[key.size()-1] >= nv || key[0] < 0, + "Wrong value of the key in FSSparseTensor::insert"); + SparseTensor::insert(key, r, c); +} + +/* We go through the tensor |t| which is supposed to have single + column. If the item of |t| is nonzero, we make a key by sorting the + index, and then we go through all items having the same key (it is its + column), obtain the row number and the element, and do the + multiplication. + + The test for non-zero is |a != 0.0|, since there will be items which + are exact zeros. + + I have also tried to make the loop through the sparse tensor outer, and + find index of tensor |t| within the loop. Surprisingly, it is little + slower (for monomial tests with probability of zeros equal 0.3). But + everything depends how filled is the sparse tensor. */ + +void +FSSparseTensor::multColumnAndAdd(const Tensor &t, Vector &v) const +{ + // check compatibility of input parameters + TL_RAISE_IF(v.length() != nrows(), + "Wrong size of output vector in FSSparseTensor::multColumnAndAdd"); + TL_RAISE_IF(t.dimen() != dimen(), + "Wrong dimension of tensor in FSSparseTensor::multColumnAndAdd"); + TL_RAISE_IF(t.ncols() != 1, + "The input tensor is not single-column in FSSparseTensor::multColumnAndAdd"); + + for (Tensor::index it = t.begin(); it != t.end(); ++it) + { + int ind = *it; + double a = t.get(ind, 0); + if (a != 0.0) + { + IntSequence key(it.getCoor()); + key.sort(); + + // check that |key| is within the range + TL_RAISE_IF(key[0] < 0 || key[key.size()-1] >= nv, + "Wrong coordinates of index in FSSparseTensor::multColumnAndAdd"); + + const_iterator first_pos = m.lower_bound(key); + const_iterator last_pos = m.upper_bound(key); + for (const_iterator cit = first_pos; cit != last_pos; ++cit) + { + int r = (*cit).second.first; + double c = (*cit).second.second; + v[r] += c * a; + } + } + } +} + +void +FSSparseTensor::print() const +{ + printf("FS Sparse tensor: dim=%d, nv=%d, (%dx%d)\n", dim, nv, nr, nc); + SparseTensor::print(); +} + +// |GSSparseTensor| slicing constructor +/* This is the same as |@<|FGSTensor| slicing from |FSSparseTensor|@>|. */ +GSSparseTensor::GSSparseTensor(const FSSparseTensor &t, const IntSequence &ss, + const IntSequence &coor, const TensorDimens &td) + : SparseTensor(td.dimen(), t.nrows(), td.calcFoldMaxOffset()), + tdims(td) +{ + // set |lb| and |ub| to lower and upper bounds of slice indices + /* This is the same as |@| in {\tt gs\_tensor.cpp}, see that file for details. */ + IntSequence s_offsets(ss.size(), 0); + for (int i = 1; i < ss.size(); i++) + s_offsets[i] = s_offsets[i-1] + ss[i-1]; + + IntSequence lb(coor.size()); + IntSequence ub(coor.size()); + for (int i = 0; i < coor.size(); i++) + { + lb[i] = s_offsets[coor[i]]; + ub[i] = s_offsets[coor[i]] + ss[coor[i]] - 1; + } + + FSSparseTensor::const_iterator lbi = t.getMap().lower_bound(lb); + FSSparseTensor::const_iterator ubi = t.getMap().upper_bound(ub); + for (FSSparseTensor::const_iterator run = lbi; run != ubi; ++run) + { + if (lb.lessEq((*run).first) && (*run).first.lessEq(ub)) + { + IntSequence c((*run).first); + c.add(-1, lb); + insert(c, (*run).second.first, (*run).second.second); + } + } + +} + +void +GSSparseTensor::insert(const IntSequence &s, int r, double c) +{ + TL_RAISE_IF(!s.less(tdims.getNVX()), + "Wrong coordinates of index in GSSparseTensor::insert"); + SparseTensor::insert(s, r, c); +} + +void +GSSparseTensor::print() const +{ + printf("GS Sparse tensor: (%dx%d)\nSymmetry: ", nr, nc); + tdims.getSym().print(); + printf("NVS: "); + tdims.getNVS().print(); + SparseTensor::print(); +} diff --git a/dynare++/tl/cc/sparse_tensor.cweb b/dynare++/tl/cc/sparse_tensor.cweb deleted file mode 100644 index 214e3495d..000000000 --- a/dynare++/tl/cc/sparse_tensor.cweb +++ /dev/null @@ -1,274 +0,0 @@ -@q $Id: sparse_tensor.cweb 1258 2007-05-11 13:59:10Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@ Start of {\tt sparse\_tensor.cpp} file. - -@c -#include "sparse_tensor.h" -#include "fs_tensor.h" -#include "tl_exception.h" - -#include - -@<|SparseTensor::insert| code@>; -@<|SparseTensor::isFinite| code@>; -@<|SparseTensor::getFoldIndexFillFactor| code@>; -@<|SparseTensor::getUnfoldIndexFillFactor| code@>; -@<|SparseTensor::print| code@>; -@<|FSSparseTensor| constructor code@>; -@<|FSSparseTensor| copy constructor code@>; -@<|FSSparseTensor::insert| code@>; -@<|FSSparseTensor::multColumnAndAdd| code@>; -@<|FSSparseTensor::print| code@>; -@<|GSSparseTensor| slicing constructor@>; -@<|GSSparseTensor::insert| code@>; -@<|GSSparseTensor::print| code@>; - -@ This is straightforward. Before we insert anything, we do a few -checks. Then we reset |first_nz_row| and |last_nz_row| if necessary. - -@<|SparseTensor::insert| code@>= -void SparseTensor::insert(const IntSequence& key, int r, double c) -{ - TL_RAISE_IF(r < 0 || r >= nr, - "Row number out of dimension of tensor in SparseTensor::insert"); - TL_RAISE_IF(key.size() != dimen(), - "Wrong length of key in SparseTensor::insert"); - TL_RAISE_IF(! std::isfinite(c), - "Insertion of non-finite value in SparseTensor::insert"); - - iterator first_pos = m.lower_bound(key); - @; - m.insert(first_pos, Map::value_type(key, Item(r,c))); - if (first_nz_row > r) - first_nz_row = r; - if (last_nz_row < r) - last_nz_row = r; -} - -@ -@= - iterator last_pos = m.upper_bound(key); - for (iterator it = first_pos; it != last_pos; ++it) - if ((*it).second.first == r) { - TL_RAISE("Duplicate insertion in SparseTensor::insert"); - return; - } - -@ This returns true if all items are finite (not Nan nor Inf). -@<|SparseTensor::isFinite| code@>= -bool SparseTensor::isFinite() const -{ - bool res = true; - const_iterator run = m.begin(); - while (res && run != m.end()) { - if (! std::isfinite((*run).second.second)) - res = false; - ++run; - } - return res; -} - -@ This returns a ratio of a number of non-zero columns in folded -tensor to the total number of columns. - -@<|SparseTensor::getFoldIndexFillFactor| code@>= -double SparseTensor::getFoldIndexFillFactor() const -{ - int cnt = 0; - const_iterator start_col = m.begin(); - while (start_col != m.end()) { - cnt++; - const IntSequence& key = (*start_col).first; - start_col = m.upper_bound(key); - } - - return ((double)cnt)/ncols(); -} - -@ This returns a ratio of a number of non-zero columns in unfolded -tensor to the total number of columns. - -@<|SparseTensor::getUnfoldIndexFillFactor| code@>= -double SparseTensor::getUnfoldIndexFillFactor() const -{ - int cnt = 0; - const_iterator start_col = m.begin(); - while (start_col != m.end()) { - const IntSequence& key = (*start_col).first; - Symmetry s(key); - cnt += Tensor::noverseq(s); - start_col = m.upper_bound(key); - } - - return ((double)cnt)/ncols(); -} - - - -@ This prints the fill factor and all items. -@<|SparseTensor::print| code@>= -void SparseTensor::print() const -{ - printf("Fill: %3.2f %%\n", 100*getFillFactor()); - const_iterator start_col = m.begin(); - while (start_col != m.end()) { - const IntSequence& key = (*start_col).first; - printf("Column: ");key.print(); - const_iterator end_col = m.upper_bound(key); - int cnt = 1; - for (const_iterator run = start_col; run != end_col; ++run, cnt++) { - if ((cnt/7)*7 == cnt) - printf("\n"); - printf("%d(%6.2g) ", (*run).second.first, (*run).second.second); - } - printf("\n"); - start_col = end_col; - } -} - - - -@ -@<|FSSparseTensor| constructor code@>= -FSSparseTensor::FSSparseTensor(int d, int nvar, int r) - : SparseTensor(d, r, FFSTensor::calcMaxOffset(nvar, d)), - nv(nvar), sym(d) -{} - -@ -@<|FSSparseTensor| copy constructor code@>= -FSSparseTensor::FSSparseTensor(const FSSparseTensor& t) - : SparseTensor(t), - nv(t.nvar()), sym(t.sym) -{} - -@ -@<|FSSparseTensor::insert| code@>= -void FSSparseTensor::insert(const IntSequence& key, int r, double c) -{ - TL_RAISE_IF(!key.isSorted(), - "Key is not sorted in FSSparseTensor::insert"); - TL_RAISE_IF(key[key.size()-1] >= nv || key[0] < 0, - "Wrong value of the key in FSSparseTensor::insert"); - SparseTensor::insert(key, r, c); -} - -@ We go through the tensor |t| which is supposed to have single -column. If the item of |t| is nonzero, we make a key by sorting the -index, and then we go through all items having the same key (it is its -column), obtain the row number and the element, and do the -multiplication. - -The test for non-zero is |a != 0.0|, since there will be items which -are exact zeros. - -I have also tried to make the loop through the sparse tensor outer, and -find index of tensor |t| within the loop. Surprisingly, it is little -slower (for monomial tests with probability of zeros equal 0.3). But -everything depends how filled is the sparse tensor. - -@<|FSSparseTensor::multColumnAndAdd| code@>= -void FSSparseTensor::multColumnAndAdd(const Tensor& t, Vector& v) const -{ - @; - for (Tensor::index it = t.begin(); it != t.end(); ++it) { - int ind = *it; - double a = t.get(ind, 0); - if (a != 0.0) { - IntSequence key(it.getCoor()); - key.sort(); - @; - const_iterator first_pos = m.lower_bound(key); - const_iterator last_pos = m.upper_bound(key); - for (const_iterator cit = first_pos; cit != last_pos; ++cit) { - int r = (*cit).second.first; - double c = (*cit).second.second; - v[r] += c * a; - } - } - } -} - - -@ -@= - TL_RAISE_IF(v.length() != nrows(), - "Wrong size of output vector in FSSparseTensor::multColumnAndAdd"); - TL_RAISE_IF(t.dimen() != dimen(), - "Wrong dimension of tensor in FSSparseTensor::multColumnAndAdd"); - TL_RAISE_IF(t.ncols() != 1, - "The input tensor is not single-column in FSSparseTensor::multColumnAndAdd"); - - -@ -@= - TL_RAISE_IF(key[0] < 0 || key[key.size()-1] >= nv, - "Wrong coordinates of index in FSSparseTensor::multColumnAndAdd"); - -@ -@<|FSSparseTensor::print| code@>= -void FSSparseTensor::print() const -{ - printf("FS Sparse tensor: dim=%d, nv=%d, (%dx%d)\n", dim, nv, nr, nc); - SparseTensor::print(); -} - -@ This is the same as |@<|FGSTensor| slicing from |FSSparseTensor|@>|. -@<|GSSparseTensor| slicing constructor@>= -GSSparseTensor::GSSparseTensor(const FSSparseTensor& t, const IntSequence& ss, - const IntSequence& coor, const TensorDimens& td) - : SparseTensor(td.dimen(), t.nrows(), td.calcFoldMaxOffset()), - tdims(td) -{ - @; - - FSSparseTensor::const_iterator lbi = t.getMap().lower_bound(lb); - FSSparseTensor::const_iterator ubi = t.getMap().upper_bound(ub); - for (FSSparseTensor::const_iterator run = lbi; run != ubi; ++run) { - if (lb.lessEq((*run).first) && (*run).first.lessEq(ub)) { - IntSequence c((*run).first); - c.add(-1, lb); - insert(c, (*run).second.first, (*run).second.second); - } - } - -} - -@ This is the same as |@| in {\tt gs\_tensor.cpp}, see that file for details. - -@= - IntSequence s_offsets(ss.size(), 0); - for (int i = 1; i < ss.size(); i++) - s_offsets[i] = s_offsets[i-1] + ss[i-1]; - - IntSequence lb(coor.size()); - IntSequence ub(coor.size()); - for (int i = 0; i < coor.size(); i++) { - lb[i] = s_offsets[coor[i]]; - ub[i] = s_offsets[coor[i]] + ss[coor[i]] - 1; - } - - -@ -@<|GSSparseTensor::insert| code@>= -void GSSparseTensor::insert(const IntSequence& s, int r, double c) -{ - TL_RAISE_IF(! s.less(tdims.getNVX()), - "Wrong coordinates of index in GSSparseTensor::insert"); - SparseTensor::insert(s, r, c); -} - -@ -@<|GSSparseTensor::print| code@>= -void GSSparseTensor::print() const -{ - printf("GS Sparse tensor: (%dx%d)\nSymmetry: ", nr, nc); - tdims.getSym().print(); - printf("NVS: "); - tdims.getNVS().print(); - SparseTensor::print(); -} - -@ End of {\tt sparse\_tensor.cpp} file. diff --git a/dynare++/tl/cc/sparse_tensor.hh b/dynare++/tl/cc/sparse_tensor.hh new file mode 100644 index 000000000..1b61d7ef8 --- /dev/null +++ b/dynare++/tl/cc/sparse_tensor.hh @@ -0,0 +1,187 @@ +// Copyright 2004, Ondra Kamenik + +// Sparse tensor. + +/* Here we declare a sparse full and general symmetry tensors with the + multidimensional index along columns. We implement them as a |multimap| + associating to each sequence of coordinates |IntSequence| a set of + pairs (row, number). This is very convenient but not optimal in terms + of memory consumption. So the implementation can be changed. + + The current |multimap| implementation allows insertions. Another + advantage of this approach is that we do not need to calculate column + numbers from the |IntSequence|, since the column is accessed directly + via the key which is |IntSequence|. + + The only operation we need to do with the full symmetry sparse tensor + is a left multiplication of a row oriented single column tensor. The + result of such operation is a column of the same size as the sparse + tensor. Other important operations are slicing operations. We need to + do sparse and dense slices of full symmetry sparse tensors. In fact, + the only constructor of general symmetry sparse tensor is slicing from + the full symmetry sparse. */ + +#ifndef SPARSE_TENSOR_H +#define SPARSE_TENSOR_H + +#include "symmetry.hh" +#include "tensor.hh" +#include "gs_tensor.hh" +#include "Vector.h" + +#include + +using namespace std; + +// |ltseq| predicate +struct ltseq +{ + bool + operator()(const IntSequence &s1, const IntSequence &s2) const + { + return s1 < s2; + } +}; + +/* This is a super class of both full symmetry and general symmetry + sparse tensors. It contains a |multimap| and implements insertions. It + tracks maximum and minimum row, for which there is an item. */ + +class SparseTensor +{ +public: + typedef pair Item; + typedef multimap Map; + typedef Map::const_iterator const_iterator; +protected: + typedef Map::iterator iterator; + + Map m; + const int dim; + const int nr; + const int nc; + int first_nz_row; + int last_nz_row; +public: + SparseTensor(int d, int nnr, int nnc) + : dim(d), nr(nnr), nc(nnc), first_nz_row(nr), last_nz_row(-1) + { + } + SparseTensor(const SparseTensor &t) + : m(t.m), dim(t.dim), nr(t.nr), nc(t.nc) + { + } + virtual ~SparseTensor() + { + } + void insert(const IntSequence &s, int r, double c); + const Map & + getMap() const + { + return m; + } + int + dimen() const + { + return dim; + } + int + nrows() const + { + return nr; + } + int + ncols() const + { + return nc; + } + double + getFillFactor() const + { + return ((double) m.size())/(nrows()*ncols()); + } + double getFoldIndexFillFactor() const; + double getUnfoldIndexFillFactor() const; + int + getNumNonZero() const + { + return m.size(); + } + int + getFirstNonZeroRow() const + { + return first_nz_row; + } + int + getLastNonZeroRow() const + { + return last_nz_row; + } + virtual const Symmetry&getSym() const = 0; + void print() const; + bool isFinite() const; +}; + +/* This is a full symmetry sparse tensor. It implements + |multColumnAndAdd| and in addition to |sparseTensor|, it has |nv| + (number of variables), and symmetry (basically it is a dimension). */ + +class FSSparseTensor : public SparseTensor +{ +public: + typedef SparseTensor::const_iterator const_iterator; +private: + const int nv; + const Symmetry sym; +public: + FSSparseTensor(int d, int nvar, int r); + FSSparseTensor(const FSSparseTensor &t); + void insert(const IntSequence &s, int r, double c); + void multColumnAndAdd(const Tensor &t, Vector &v) const; + const Symmetry & + getSym() const + { + return sym; + } + int + nvar() const + { + return nv; + } + void print() const; +}; + +/* This is a general symmetry sparse tensor. It has |TensorDimens| and + can be constructed as a slice of the full symmetry sparse tensor. The + slicing constructor takes the same form as the slicing |FGSTensor| + constructor from full symmetry sparse tensor. */ + +class GSSparseTensor : public SparseTensor +{ +public: + typedef SparseTensor::const_iterator const_iterator; +private: + const TensorDimens tdims; +public: + GSSparseTensor(const FSSparseTensor &t, const IntSequence &ss, + const IntSequence &coor, const TensorDimens &td); + GSSparseTensor(const GSSparseTensor &t) + : SparseTensor(t), tdims(t.tdims) + { + } + void insert(const IntSequence &s, int r, double c); + const Symmetry & + getSym() const + { + return tdims.getSym(); + } + const TensorDimens & + getDims() const + { + return tdims; + } + void print() const; + +}; + +#endif diff --git a/dynare++/tl/cc/sparse_tensor.hweb b/dynare++/tl/cc/sparse_tensor.hweb deleted file mode 100644 index 7df3de2c1..000000000 --- a/dynare++/tl/cc/sparse_tensor.hweb +++ /dev/null @@ -1,154 +0,0 @@ -@q $Id: sparse_tensor.hweb 522 2005-11-25 15:45:54Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@*2 Sparse tensor. Start of {\tt sparse\_tensor.h} file. - -Here we declare a sparse full and general symmetry tensors with the -multidimensional index along columns. We implement them as a |multimap| -associating to each sequence of coordinates |IntSequence| a set of -pairs (row, number). This is very convenient but not optimal in terms -of memory consumption. So the implementation can be changed. - -The current |multimap| implementation allows insertions. Another -advantage of this approach is that we do not need to calculate column -numbers from the |IntSequence|, since the column is accessed directly -via the key which is |IntSequence|. - -The only operation we need to do with the full symmetry sparse tensor -is a left multiplication of a row oriented single column tensor. The -result of such operation is a column of the same size as the sparse -tensor. Other important operations are slicing operations. We need to -do sparse and dense slices of full symmetry sparse tensors. In fact, -the only constructor of general symmetry sparse tensor is slicing from -the full symmetry sparse. - -@s SparseTensor int -@s FSSparseTensor int -@s GSSparseTensor int - -@c -#ifndef SPARSE_TENSOR_H -#define SPARSE_TENSOR_H - -#include "symmetry.h" -#include "tensor.h" -#include "gs_tensor.h" -#include "Vector.h" - -#include - -using namespace std; - -@<|ltseq| predicate@>; -@<|SparseTensor| class declaration@>; -@<|FSSparseTensor| class declaration@>; -@<|GSSparseTensor| class declaration@>; - -#endif - -@ -@<|ltseq| predicate@>= -struct ltseq { - bool operator()(const IntSequence& s1, const IntSequence& s2) const - {@+ return s1 < s2;@+} -}; - -@ This is a super class of both full symmetry and general symmetry -sparse tensors. It contains a |multimap| and implements insertions. It -tracks maximum and minimum row, for which there is an item. - -@<|SparseTensor| class declaration@>= -class SparseTensor { -public:@; - typedef pair Item; - typedef multimap Map; - typedef Map::const_iterator const_iterator; -protected:@; - typedef Map::iterator iterator; - - Map m; - const int dim; - const int nr; - const int nc; - int first_nz_row; - int last_nz_row; -public:@; - SparseTensor(int d, int nnr, int nnc) - : dim(d), nr(nnr), nc(nnc), first_nz_row(nr), last_nz_row(-1) @+{} - SparseTensor(const SparseTensor& t) - : m(t.m), dim(t.dim), nr(t.nr), nc(t.nc) @+{} - virtual ~SparseTensor() @+{} - void insert(const IntSequence& s, int r, double c); - const Map& getMap() const - {@+ return m;@+} - int dimen() const - {@+ return dim;@+} - int nrows() const - {@+ return nr;@+} - int ncols() const - {@+ return nc;@+} - double getFillFactor() const - {@+ return ((double)m.size())/(nrows()*ncols());@+} - double getFoldIndexFillFactor() const; - double getUnfoldIndexFillFactor() const; - int getNumNonZero() const - {@+ return m.size();@+} - int getFirstNonZeroRow() const - {@+ return first_nz_row;@+} - int getLastNonZeroRow() const - {@+ return last_nz_row;@+} - virtual const Symmetry& getSym() const =0; - void print() const; - bool isFinite() const; -} - -@ This is a full symmetry sparse tensor. It implements -|multColumnAndAdd| and in addition to |sparseTensor|, it has |nv| -(number of variables), and symmetry (basically it is a dimension). - -@<|FSSparseTensor| class declaration@>= -class FSSparseTensor : public SparseTensor { -public:@; - typedef SparseTensor::const_iterator const_iterator; -private:@; - const int nv; - const Symmetry sym; -public:@; - FSSparseTensor(int d, int nvar, int r); - FSSparseTensor(const FSSparseTensor& t); - void insert(const IntSequence& s, int r, double c); - void multColumnAndAdd(const Tensor& t, Vector& v) const; - const Symmetry& getSym() const - {@+ return sym;@+} - int nvar() const - {@+ return nv;@+} - void print() const; -}; - - -@ This is a general symmetry sparse tensor. It has |TensorDimens| and -can be constructed as a slice of the full symmetry sparse tensor. The -slicing constructor takes the same form as the slicing |FGSTensor| -constructor from full symmetry sparse tensor. - -@<|GSSparseTensor| class declaration@>= -class GSSparseTensor : public SparseTensor { -public:@; - typedef SparseTensor::const_iterator const_iterator; -private:@; - const TensorDimens tdims; -public:@; - GSSparseTensor(const FSSparseTensor& t, const IntSequence& ss, - const IntSequence& coor, const TensorDimens& td); - GSSparseTensor(const GSSparseTensor& t) - : SparseTensor(t), tdims(t.tdims) @+{} - void insert(const IntSequence& s, int r, double c); - const Symmetry& getSym() const - {@+ return tdims.getSym();@+} - const TensorDimens& getDims() const - {@+ return tdims;@+} - void print() const; - -}; - -@ End of {\tt sparse\_tensor.h} file. diff --git a/dynare++/tl/cc/stack_container.cc b/dynare++/tl/cc/stack_container.cc new file mode 100644 index 000000000..126069df1 --- /dev/null +++ b/dynare++/tl/cc/stack_container.cc @@ -0,0 +1,662 @@ +// Copyright 2004, Ondra Kamenik + +#include "stack_container.hh" +#include "pyramid_prod2.hh" +#include "ps_tensor.hh" + +double FoldedStackContainer::fill_threshold = 0.00005; +double UnfoldedStackContainer::fill_threshold = 0.00005; + +// |FoldedStackContainer::multAndAdd| sparse code +/* Here we multiply the sparse tensor with the + |FoldedStackContainer|. We have four implementations, + |multAndAddSparse1|, |multAndAddSparse2|, |multAndAddSparse3|, and + |multAndAddSparse4|. The third is not threaded yet and I expect that + it is certainly the slowest. The |multAndAddSparse4| exploits the + sparsity, however, it seems to be still worse than |multAndAddSparse2| + even for really sparse matrices. On the other hand, it can be more + efficient than |multAndAddSparse2| for large problems, since it does + not need that much of memory and can avoid much swapping. Very + preliminary examination shows that |multAndAddSparse2| is the best in + terms of time. */ +void +FoldedStackContainer::multAndAdd(const FSSparseTensor &t, + FGSTensor &out) const +{ + TL_RAISE_IF(t.nvar() != getAllSize(), + "Wrong number of variables of tensor for FoldedStackContainer::multAndAdd"); + multAndAddSparse2(t, out); +} + +// |FoldedStackContainer::multAndAdd| dense code +/* Here we perform the Faa Di Bruno step for a given dimension |dim|, and for + the dense fully symmetric tensor which is scattered in the container + of general symmetric tensors. The implementation is pretty the same as + |@<|UnfoldedStackContainer::multAndAdd| dense code@>|. */ +void +FoldedStackContainer::multAndAdd(int dim, const FGSContainer &c, FGSTensor &out) const +{ + TL_RAISE_IF(c.num() != numStacks(), + "Wrong symmetry length of container for FoldedStackContainer::multAndAdd"); + + THREAD_GROUP gr; + SymmetrySet ss(dim, c.num()); + for (symiterator si(ss); !si.isEnd(); ++si) + { + if (c.check(*si)) + { + THREAD *worker = new WorkerFoldMAADense(*this, *si, c, out); + gr.insert(worker); + } + } + gr.run(); +} + +/* This is analogous to |@<|WorkerUnfoldMAADense::operator()()| + code@>|. */ + +void +WorkerFoldMAADense::operator()() +{ + Permutation iden(dense_cont.num()); + IntSequence coor(sym, iden.getMap()); + const FGSTensor *g = dense_cont.get(sym); + cont.multAndAddStacks(coor, *g, out, &out); +} + +WorkerFoldMAADense::WorkerFoldMAADense(const FoldedStackContainer &container, + const Symmetry &s, + const FGSContainer &dcontainer, + FGSTensor &outten) + : cont(container), sym(s), dense_cont(dcontainer), out(outten) +{ +} + +/* This is analogous to |@<|UnfoldedStackContainer::multAndAddSparse1| + code@>|. */ +void +FoldedStackContainer::multAndAddSparse1(const FSSparseTensor &t, + FGSTensor &out) const +{ + THREAD_GROUP gr; + UFSTensor dummy(0, numStacks(), t.dimen()); + for (Tensor::index ui = dummy.begin(); ui != dummy.end(); ++ui) + { + THREAD *worker = new WorkerFoldMAASparse1(*this, t, out, ui.getCoor()); + gr.insert(worker); + } + gr.run(); +} + +/* This is analogous to |@<|WorkerUnfoldMAASparse1::operator()()| code@>|. + The only difference is that instead of |UPSTensor| as a + result of multiplication of unfolded tensor and tensors from + containers, we have |FPSTensor| with partially folded permuted + symmetry. + + todo: make slice vertically narrowed according to the fill of t, + vertically narrow out accordingly. */ + +void +WorkerFoldMAASparse1::operator()() +{ + const EquivalenceSet &eset = ebundle.get(out.dimen()); + const PermutationSet &pset = tls.pbundle->get(t.dimen()); + Permutation iden(t.dimen()); + + UPSTensor slice(t, cont.getStackSizes(), coor, + PerTensorDimens(cont.getStackSizes(), coor)); + for (int iper = 0; iper < pset.getNum(); iper++) + { + const Permutation &per = pset.get(iper); + IntSequence percoor(coor.size()); + per.apply(coor, percoor); + for (EquivalenceSet::const_iterator it = eset.begin(); + it != eset.end(); ++it) + { + if ((*it).numClasses() == t.dimen()) + { + StackProduct sp(cont, *it, out.getSym()); + if (!sp.isZero(percoor)) + { + KronProdStack kp(sp, percoor); + kp.optimizeOrder(); + const Permutation &oper = kp.getPer(); + if (Permutation(oper, per) == iden) + { + FPSTensor fps(out.getDims(), *it, slice, kp); + { + SYNCHRO syn(&out, "WorkerUnfoldMAASparse1"); + fps.addTo(out); + } + } + } + } + } + } +} + +WorkerFoldMAASparse1::WorkerFoldMAASparse1(const FoldedStackContainer &container, + const FSSparseTensor &ten, + FGSTensor &outten, const IntSequence &c) + : cont(container), t(ten), out(outten), coor(c), ebundle(*(tls.ebundle)) +{ +} + +/* Here is the second implementation of sparse folded |multAndAdd|. It + is pretty similar to implementation of + |@<|UnfoldedStackContainer::multAndAddSparse2| code@>|. We make a + dense folded |slice|, and then call folded |multAndAddStacks|, which + multiplies all the combinations compatible with the slice. */ + +void +FoldedStackContainer::multAndAddSparse2(const FSSparseTensor &t, + FGSTensor &out) const +{ + THREAD_GROUP gr; + FFSTensor dummy_f(0, numStacks(), t.dimen()); + for (Tensor::index fi = dummy_f.begin(); fi != dummy_f.end(); ++fi) + { + THREAD *worker = new WorkerFoldMAASparse2(*this, t, out, fi.getCoor()); + gr.insert(worker); + } + gr.run(); +} + +/* Here we make a sparse slice first and then call |multAndAddStacks| + if the slice is not empty. If the slice is really sparse, we call + sparse version of |multAndAddStacks|. What means ``really sparse'' is + given by |fill_threshold|. It is not tuned yet, a practice shows that + it must be a really low number, since sparse |multAndAddStacks| is + much slower than the dense version. + + Further, we take only nonzero rows of the slice, and accordingly of + the out tensor. We jump over zero initial rows and drop zero tailing + rows. */ + +void +WorkerFoldMAASparse2::operator()() +{ + GSSparseTensor slice(t, cont.getStackSizes(), coor, + TensorDimens(cont.getStackSizes(), coor)); + if (slice.getNumNonZero()) + { + if (slice.getUnfoldIndexFillFactor() > FoldedStackContainer::fill_threshold) + { + FGSTensor dense_slice(slice); + int r1 = slice.getFirstNonZeroRow(); + int r2 = slice.getLastNonZeroRow(); + FGSTensor dense_slice1(r1, r2-r1+1, dense_slice); + FGSTensor out1(r1, r2-r1+1, out); + cont.multAndAddStacks(coor, dense_slice1, out1, &out); + } + else + cont.multAndAddStacks(coor, slice, out, &out); + } +} + +WorkerFoldMAASparse2::WorkerFoldMAASparse2(const FoldedStackContainer &container, + const FSSparseTensor &ten, + FGSTensor &outten, const IntSequence &c) + : cont(container), t(ten), out(outten), coor(c) +{ +} + +/* Here is the third implementation of the sparse folded + |multAndAdd|. It is column-wise implementation, and thus is not a good + candidate for the best performer. + + We go through all columns from the output. For each column we + calculate folded |sumcol| which is a sum of all appropriate columns + for all suitable equivalences. So we go through all suitable + equivalences, for each we construct a |StackProduct| object and + construct |IrregTensor| for a corresponding column of $z$. The + |IrregTensor| is an abstraction for Kronecker multiplication of + stacked columns of the two containers without zeros. Then the column + is added to |sumcol|. Finally, the |sumcol| is multiplied by the + sparse tensor. */ + +void +FoldedStackContainer::multAndAddSparse3(const FSSparseTensor &t, + FGSTensor &out) const +{ + const EquivalenceSet &eset = ebundle.get(out.dimen()); + for (Tensor::index run = out.begin(); run != out.end(); ++run) + { + Vector outcol(out, *run); + FRSingleTensor sumcol(t.nvar(), t.dimen()); + sumcol.zeros(); + for (EquivalenceSet::const_iterator it = eset.begin(); + it != eset.end(); ++it) + { + if ((*it).numClasses() == t.dimen()) + { + StackProduct sp(*this, *it, out.getSym()); + IrregTensorHeader header(sp, run.getCoor()); + IrregTensor irten(header); + irten.addTo(sumcol); + } + } + t.multColumnAndAdd(sumcol, outcol); + } +} + +/* Here is the fourth implementation of sparse + |FoldedStackContainer::multAndAdd|. It is almost equivalent to + |multAndAddSparse2| with the exception that the |FPSTensor| as a + result of a product of a slice and Kronecker product of the stack + derivatives is calculated in the sparse fashion. For further details, see + |@<|FoldedStackContainer::multAndAddStacks| sparse code@>| and + |@<|FPSTensor| sparse constructor@>|. */ + +void +FoldedStackContainer::multAndAddSparse4(const FSSparseTensor &t, FGSTensor &out) const +{ + THREAD_GROUP gr; + FFSTensor dummy_f(0, numStacks(), t.dimen()); + for (Tensor::index fi = dummy_f.begin(); fi != dummy_f.end(); ++fi) + { + THREAD *worker = new WorkerFoldMAASparse4(*this, t, out, fi.getCoor()); + gr.insert(worker); + } + gr.run(); +} + +/* The |WorkerFoldMAASparse4| is the same as |WorkerFoldMAASparse2| + with the exception that we call a sparse version of + |multAndAddStacks|. */ + +void +WorkerFoldMAASparse4::operator()() +{ + GSSparseTensor slice(t, cont.getStackSizes(), coor, + TensorDimens(cont.getStackSizes(), coor)); + if (slice.getNumNonZero()) + cont.multAndAddStacks(coor, slice, out, &out); +} + +WorkerFoldMAASparse4::WorkerFoldMAASparse4(const FoldedStackContainer &container, + const FSSparseTensor &ten, + FGSTensor &outten, const IntSequence &c) + : cont(container), t(ten), out(outten), coor(c) +{ +} + +// |FoldedStackContainer::multAndAddStacks| dense code +/* This is almost the same as + |@<|UnfoldedStackContainer::multAndAddStacks| code@>|. The only + difference is that we do not construct a |UPSTensor| from + |KronProdStack|, but we construct partially folded permuted + symmetry |FPSTensor|. Note that the tensor |g| must be unfolded + in order to be able to multiply with unfolded rows of Kronecker + product. However, columns of such a product are partially + folded giving a rise to the |FPSTensor|. */ +void +FoldedStackContainer::multAndAddStacks(const IntSequence &coor, + const FGSTensor &g, + FGSTensor &out, const void *ad) const +{ + const EquivalenceSet &eset = ebundle.get(out.dimen()); + + UGSTensor ug(g); + UFSTensor dummy_u(0, numStacks(), g.dimen()); + for (Tensor::index ui = dummy_u.begin(); ui != dummy_u.end(); ++ui) + { + IntSequence tmp(ui.getCoor()); + tmp.sort(); + if (tmp == coor) + { + Permutation sort_per(ui.getCoor()); + sort_per.inverse(); + for (EquivalenceSet::const_iterator it = eset.begin(); + it != eset.end(); ++it) + { + if ((*it).numClasses() == g.dimen()) + { + StackProduct sp(*this, *it, sort_per, out.getSym()); + if (!sp.isZero(coor)) + { + KronProdStack kp(sp, coor); + if (ug.getSym().isFull()) + kp.optimizeOrder(); + FPSTensor fps(out.getDims(), *it, sort_per, ug, kp); + { + SYNCHRO syn(ad, "multAndAddStacks"); + fps.addTo(out); + } + } + } + } + } + } +} + +// |FoldedStackContainer::multAndAddStacks| sparse code +/* This is almost the same as + |@<|FoldedStackContainer::multAndAddStacks| dense code@>|. The only + difference is that the Kronecker product of the stacks is multiplied + with sparse slice |GSSparseTensor| (not dense slice |FGSTensor|). The + multiplication is done in |@<|FPSTensor| sparse constructor@>|. */ +void +FoldedStackContainer::multAndAddStacks(const IntSequence &coor, + const GSSparseTensor &g, + FGSTensor &out, const void *ad) const +{ + const EquivalenceSet &eset = ebundle.get(out.dimen()); + UFSTensor dummy_u(0, numStacks(), g.dimen()); + for (Tensor::index ui = dummy_u.begin(); ui != dummy_u.end(); ++ui) + { + IntSequence tmp(ui.getCoor()); + tmp.sort(); + if (tmp == coor) + { + Permutation sort_per(ui.getCoor()); + sort_per.inverse(); + for (EquivalenceSet::const_iterator it = eset.begin(); + it != eset.end(); ++it) + { + if ((*it).numClasses() == g.dimen()) + { + StackProduct sp(*this, *it, sort_per, out.getSym()); + if (!sp.isZero(coor)) + { + KronProdStack kp(sp, coor); + FPSTensor fps(out.getDims(), *it, sort_per, g, kp); + { + SYNCHRO syn(ad, "multAndAddStacks"); + fps.addTo(out); + } + } + } + } + } + } +} + +// |UnfoldedStackContainer::multAndAdd| sparse code +/* Here we simply call either |multAndAddSparse1| or + |multAndAddSparse2|. The first one allows for optimization of + Kronecker products, so it seems to be more efficient. */ +void +UnfoldedStackContainer::multAndAdd(const FSSparseTensor &t, + UGSTensor &out) const +{ + TL_RAISE_IF(t.nvar() != getAllSize(), + "Wrong number of variables of tensor for UnfoldedStackContainer::multAndAdd"); + multAndAddSparse2(t, out); +} + +// |UnfoldedStackContainer::multAndAdd| dense code +/* Here we implement the formula for stacks for fully symmetric tensor + scattered in a number of general symmetry tensors contained in a given + container. The implementations is pretty the same as in + |multAndAddSparse2| but we do not do the slices of sparse tensor, but + only a lookup to the container. + + This means that we do not iterate through a dummy folded tensor to + obtain folded coordinates of stacks, rather we iterate through all + symmetries contained in the container and the coordinates of stacks + are obtained as unfolded identity sequence via the symmetry. The + reason of doing this is that we are unable to calculate symmetry from + stack coordinates as easily as stack coordinates from the symmetry. */ +void +UnfoldedStackContainer::multAndAdd(int dim, const UGSContainer &c, + UGSTensor &out) const +{ + TL_RAISE_IF(c.num() != numStacks(), + "Wrong symmetry length of container for UnfoldedStackContainer::multAndAdd"); + + THREAD_GROUP gr; + SymmetrySet ss(dim, c.num()); + for (symiterator si(ss); !si.isEnd(); ++si) + { + if (c.check(*si)) + { + THREAD *worker = new WorkerUnfoldMAADense(*this, *si, c, out); + gr.insert(worker); + } + } + gr.run(); +} + +void +WorkerUnfoldMAADense::operator()() +{ + Permutation iden(dense_cont.num()); + IntSequence coor(sym, iden.getMap()); + const UGSTensor *g = dense_cont.get(sym); + cont.multAndAddStacks(coor, *g, out, &out); +} + +WorkerUnfoldMAADense::WorkerUnfoldMAADense(const UnfoldedStackContainer &container, + const Symmetry &s, + const UGSContainer &dcontainer, + UGSTensor &outten) + : cont(container), sym(s), dense_cont(dcontainer), out(outten) +{ +} + +/* Here we implement the formula for unfolded tensors. If, for instance, + a coordinate $z$ of a tensor $\left[f_{z^2}\right]$ is partitioned as + $z=[a, b]$, then we perform the following: + $$ + \eqalign{ + \left[f_{z^2}\right]\left(\sum_c\left[\matrix{a_{c(x)}\cr b_{c(y)}}\right] + \otimes\left[\matrix{a_{c(y)}\cr b_{c(y)}}\right]\right)=& + \left[f_{aa}\right]\left(\sum_ca_{c(x)}\otimes a_{c(y)}\right)+ + \left[f_{ab}\right]\left(\sum_ca_{c(x)}\otimes b_{c(y)}\right)+\cr + &\left[f_{ba}\right]\left(\sum_cb_{c(x)}\otimes a_{c(y)}\right)+ + \left[f_{bb}\right]\left(\sum_cb_{c(x)}\otimes b_{c(y)}\right)\cr + } + $$ + This is exactly what happens here. The code is clear. It goes through + all combinations of stacks, and each thread is responsible for + operation for the slice corresponding to the combination of the stacks. */ + +void +UnfoldedStackContainer::multAndAddSparse1(const FSSparseTensor &t, + UGSTensor &out) const +{ + THREAD_GROUP gr; + UFSTensor dummy(0, numStacks(), t.dimen()); + for (Tensor::index ui = dummy.begin(); ui != dummy.end(); ++ui) + { + THREAD *worker = new WorkerUnfoldMAASparse1(*this, t, out, ui.getCoor()); + gr.insert(worker); + } + gr.run(); +} + +/* This does a step of |@<|UnfoldedStackContainer::multAndAddSparse1| code@>| for + a given coordinates. First it makes the slice of the given stack coordinates. + Then it multiplies everything what should be multiplied with the slice. + That is it goes through all equivalences, creates |StackProduct|, then + |KronProdStack|, which is added to |out|. So far everything is clear. + + However, we want to use optimized |KronProdAllOptim| to minimize + a number of flops and memory needed in the Kronecker product. So we go + through all permutations |per|, permute the coordinates to get + |percoor|, go through all equivalences, and make |KronProdStack| and + optimize it. The result of optimization is a permutation |oper|. Now, + we multiply the Kronecker product with the slice, only if the slice + has the same ordering of coordinates as the Kronecker product + |KronProdStack|. However, it is not perfectly true. Since we go + through {\bf all} permutations |per|, there might be two different + permutations leading to the same ordering in |KronProdStack| and thus + the same ordering in the optimized |KronProdStack|. The two cases + would be counted twice, which is wrong. That is why we do not + condition on $\hbox{coor}\circ\hbox{oper}\circ\hbox{per} = + \hbox{coor}$, but we condition on + $\hbox{oper}\circ\hbox{per}=\hbox{id}$. In this way, we rule out + permutations |per| leading to the same ordering of stacks when + applied on |coor|. + + todo: vertically narrow slice and out according to the fill in t. */ + +void +WorkerUnfoldMAASparse1::operator()() +{ + const EquivalenceSet &eset = ebundle.get(out.dimen()); + const PermutationSet &pset = tls.pbundle->get(t.dimen()); + Permutation iden(t.dimen()); + + UPSTensor slice(t, cont.getStackSizes(), coor, + PerTensorDimens(cont.getStackSizes(), coor)); + for (int iper = 0; iper < pset.getNum(); iper++) + { + const Permutation &per = pset.get(iper); + IntSequence percoor(coor.size()); + per.apply(coor, percoor); + for (EquivalenceSet::const_iterator it = eset.begin(); + it != eset.end(); ++it) + { + if ((*it).numClasses() == t.dimen()) + { + StackProduct sp(cont, *it, out.getSym()); + if (!sp.isZero(percoor)) + { + KronProdStack kp(sp, percoor); + kp.optimizeOrder(); + const Permutation &oper = kp.getPer(); + if (Permutation(oper, per) == iden) + { + UPSTensor ups(out.getDims(), *it, slice, kp); + { + SYNCHRO syn(&out, "WorkerUnfoldMAASparse1"); + ups.addTo(out); + } + } + } + } + } + } +} + +WorkerUnfoldMAASparse1::WorkerUnfoldMAASparse1(const UnfoldedStackContainer &container, + const FSSparseTensor &ten, + UGSTensor &outten, const IntSequence &c) + : cont(container), t(ten), out(outten), coor(c), ebundle(*(tls.ebundle)) +{ +} + +/* In here we implement the formula by a bit different way. We use the + fact, using notation of |@<|UnfoldedStackContainer::multAndAddSparse2| + code@>|, that + $$ + \left[f_{ba}\right]\left(\sum_cb_{c(x)}\otimes a_{c(y)}\right)= + \left[f_{ab}\right]\left(\sum_ca_{c(y)}\otimes b_{c(b)}\right)\cdot P + $$ + where $P$ is a suitable permutation of columns. The permutation + corresponds to (in this example) a swap of $a$ and $b$. An advantage + of this approach is that we do not need |UPSTensor| for $f_{ba}$, and + thus we decrease the number of needed slices. + + So we go through all folded indices of stack coordinates, then for + each such index |fi| we make a slice and call |multAndAddStacks|. This + goes through all corresponding unfolded indices to perform the + formula. Each unsorted (unfold) index implies a sorting permutation + |sort_per| which must be used to permute stacks in |StackProduct|, and + permute equivalence classes when |UPSTensor| is formed. In this way + the column permutation $P$ from the formula is factored to the + permutation of |UPSTensor|. */ + +void +UnfoldedStackContainer::multAndAddSparse2(const FSSparseTensor &t, + UGSTensor &out) const +{ + THREAD_GROUP gr; + FFSTensor dummy_f(0, numStacks(), t.dimen()); + for (Tensor::index fi = dummy_f.begin(); fi != dummy_f.end(); ++fi) + { + THREAD *worker = new WorkerUnfoldMAASparse2(*this, t, out, fi.getCoor()); + gr.insert(worker); + } + gr.run(); +} + +/* This does a step of |@<|UnfoldedStackContainer::multAndAddSparse2| code@>| for + a given coordinates. + + todo: implement |multAndAddStacks| for sparse slice as + |@<|FoldedStackContainer::multAndAddStacks| sparse code@>| and do this method as + |@<|WorkerFoldMAASparse2::operator()()| code@>|. */ + +void +WorkerUnfoldMAASparse2::operator()() +{ + GSSparseTensor slice(t, cont.getStackSizes(), coor, + TensorDimens(cont.getStackSizes(), coor)); + if (slice.getNumNonZero()) + { + FGSTensor fslice(slice); + UGSTensor dense_slice(fslice); + int r1 = slice.getFirstNonZeroRow(); + int r2 = slice.getLastNonZeroRow(); + UGSTensor dense_slice1(r1, r2-r1+1, dense_slice); + UGSTensor out1(r1, r2-r1+1, out); + + cont.multAndAddStacks(coor, dense_slice1, out1, &out); + } +} + +WorkerUnfoldMAASparse2::WorkerUnfoldMAASparse2(const UnfoldedStackContainer &container, + const FSSparseTensor &ten, + UGSTensor &outten, const IntSequence &c) + : cont(container), t(ten), out(outten), coor(c) +{ +} + +/* For a given unfolded coordinates of stacks |fi|, and appropriate + tensor $g$, whose symmetry is a symmetry of |fi|, the method + contributes to |out| all tensors in unfolded stack formula involving + stacks chosen by |fi|. + + We go through all |ui| coordinates which yield |fi| after sorting. We + construct a permutation |sort_per| which sorts |ui| to |fi|. We go + through all appropriate equivalences, and construct |StackProduct| + from equivalence classes permuted by |sort_per|, then |UPSTensor| with + implied permutation of columns by the permuted equivalence by + |sort_per|. The |UPSTensor| is then added to |out|. + + We cannot use here the optimized |KronProdStack|, since the symmetry + of |UGSTensor& g| prescribes the ordering of the stacks. However, if + |g| is fully symmetric, we can do the optimization harmlessly. */ + +void +UnfoldedStackContainer::multAndAddStacks(const IntSequence &fi, + const UGSTensor &g, + UGSTensor &out, const void *ad) const +{ + const EquivalenceSet &eset = ebundle.get(out.dimen()); + + UFSTensor dummy_u(0, numStacks(), g.dimen()); + for (Tensor::index ui = dummy_u.begin(); ui != dummy_u.end(); ++ui) + { + IntSequence tmp(ui.getCoor()); + tmp.sort(); + if (tmp == fi) + { + Permutation sort_per(ui.getCoor()); + sort_per.inverse(); + for (EquivalenceSet::const_iterator it = eset.begin(); + it != eset.end(); ++it) + { + if ((*it).numClasses() == g.dimen()) + { + StackProduct sp(*this, *it, sort_per, out.getSym()); + if (!sp.isZero(fi)) + { + KronProdStack kp(sp, fi); + if (g.getSym().isFull()) + kp.optimizeOrder(); + UPSTensor ups(out.getDims(), *it, sort_per, g, kp); + { + SYNCHRO syn(ad, "multAndAddStacks"); + ups.addTo(out); + } + } + } + } + } + } +} diff --git a/dynare++/tl/cc/stack_container.cweb b/dynare++/tl/cc/stack_container.cweb deleted file mode 100644 index 3c7753aa3..000000000 --- a/dynare++/tl/cc/stack_container.cweb +++ /dev/null @@ -1,670 +0,0 @@ -@q $Id: stack_container.cweb 1835 2008-05-19 01:54:48Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@ Start of {\tt stack\_container.cpp} file. - -@c -#include "stack_container.h" -#include "pyramid_prod2.h" -#include "ps_tensor.h" - -double FoldedStackContainer::fill_threshold = 0.00005; -double UnfoldedStackContainer::fill_threshold = 0.00005; -@<|FoldedStackContainer::multAndAdd| sparse code@>; -@<|FoldedStackContainer::multAndAdd| dense code@>; -@<|WorkerFoldMAADense::operator()()| code@>; -@<|WorkerFoldMAADense| constructor code@>; -@<|FoldedStackContainer::multAndAddSparse1| code@>; -@<|WorkerFoldMAASparse1::operator()()| code@>; -@<|WorkerFoldMAASparse1| constructor code@>; -@<|FoldedStackContainer::multAndAddSparse2| code@>; -@<|WorkerFoldMAASparse2::operator()()| code@>; -@<|WorkerFoldMAASparse2| constructor code@>; -@<|FoldedStackContainer::multAndAddSparse3| code@>; -@<|FoldedStackContainer::multAndAddSparse4| code@>; -@<|WorkerFoldMAASparse4::operator()()| code@>; -@<|WorkerFoldMAASparse4| constructor code@>; -@<|FoldedStackContainer::multAndAddStacks| dense code@>; -@<|FoldedStackContainer::multAndAddStacks| sparse code@>; -@# -@<|UnfoldedStackContainer::multAndAdd| sparse code@>; -@<|UnfoldedStackContainer::multAndAdd| dense code@>; -@<|WorkerUnfoldMAADense::operator()()| code@>; -@<|WorkerUnfoldMAADense| constructor code@>; -@<|UnfoldedStackContainer::multAndAddSparse1| code@>; -@<|WorkerUnfoldMAASparse1::operator()()| code@>; -@<|WorkerUnfoldMAASparse1| constructor code@>; -@<|UnfoldedStackContainer::multAndAddSparse2| code@>; -@<|WorkerUnfoldMAASparse2::operator()()| code@>; -@<|WorkerUnfoldMAASparse2| constructor code@>; -@<|UnfoldedStackContainer::multAndAddStacks| code@>; - - -@ Here we multiply the sparse tensor with the -|FoldedStackContainer|. We have four implementations, -|multAndAddSparse1|, |multAndAddSparse2|, |multAndAddSparse3|, and -|multAndAddSparse4|. The third is not threaded yet and I expect that -it is certainly the slowest. The |multAndAddSparse4| exploits the -sparsity, however, it seems to be still worse than |multAndAddSparse2| -even for really sparse matrices. On the other hand, it can be more -efficient than |multAndAddSparse2| for large problems, since it does -not need that much of memory and can avoid much swapping. Very -preliminary examination shows that |multAndAddSparse2| is the best in -terms of time. - -@s FSSparseTensor int -@s IrregTensorHeader int -@s IrregTensor int - -@<|FoldedStackContainer::multAndAdd| sparse code@>= -void FoldedStackContainer::multAndAdd(const FSSparseTensor& t, - FGSTensor& out) const -{ - TL_RAISE_IF(t.nvar() != getAllSize(), - "Wrong number of variables of tensor for FoldedStackContainer::multAndAdd"); - multAndAddSparse2(t, out); -} - -@ Here we perform the Faa Di Bruno step for a given dimension |dim|, and for -the dense fully symmetric tensor which is scattered in the container -of general symmetric tensors. The implementation is pretty the same as -|@<|UnfoldedStackContainer::multAndAdd| dense code@>|. - -@<|FoldedStackContainer::multAndAdd| dense code@>= -void FoldedStackContainer::multAndAdd(int dim, const FGSContainer& c, FGSTensor& out) const -{ - TL_RAISE_IF(c.num() != numStacks(), - "Wrong symmetry length of container for FoldedStackContainer::multAndAdd"); - - THREAD_GROUP@, gr; - SymmetrySet ss(dim, c.num()); - for (symiterator si(ss); !si.isEnd(); ++si) { - if (c.check(*si)) { - THREAD* worker = new WorkerFoldMAADense(*this, *si, c, out); - gr.insert(worker); - } - } - gr.run(); -} - -@ This is analogous to |@<|WorkerUnfoldMAADense::operator()()| -code@>|. - -@<|WorkerFoldMAADense::operator()()| code@>= -void WorkerFoldMAADense::operator()() -{ - Permutation iden(dense_cont.num()); - IntSequence coor(sym, iden.getMap()); - const FGSTensor* g = dense_cont.get(sym); - cont.multAndAddStacks(coor, *g, out, &out); -} - -@ -@<|WorkerFoldMAADense| constructor code@>= -WorkerFoldMAADense::WorkerFoldMAADense(const FoldedStackContainer& container, - const Symmetry& s, - const FGSContainer& dcontainer, - FGSTensor& outten) - : cont(container), sym(s), dense_cont(dcontainer), out(outten) -{} - -@ This is analogous to |@<|UnfoldedStackContainer::multAndAddSparse1| -code@>|. -@<|FoldedStackContainer::multAndAddSparse1| code@>= -void FoldedStackContainer::multAndAddSparse1(const FSSparseTensor& t, - FGSTensor& out) const -{ - THREAD_GROUP@, gr; - UFSTensor dummy(0, numStacks(), t.dimen()); - for (Tensor::index ui = dummy.begin(); ui != dummy.end(); ++ui) { - THREAD* worker = new WorkerFoldMAASparse1(*this, t, out, ui.getCoor()); - gr.insert(worker); - } - gr.run(); -} - -@ This is analogous to |@<|WorkerUnfoldMAASparse1::operator()()| code@>|. -The only difference is that instead of |UPSTensor| as a -result of multiplication of unfolded tensor and tensors from -containers, we have |FPSTensor| with partially folded permuted -symmetry. - -todo: make slice vertically narrowed according to the fill of t, -vertically narrow out accordingly. - -@<|WorkerFoldMAASparse1::operator()()| code@>= -void WorkerFoldMAASparse1::operator()() -{ - const EquivalenceSet& eset = ebundle.get(out.dimen()); - const PermutationSet& pset = tls.pbundle->get(t.dimen()); - Permutation iden(t.dimen()); - - UPSTensor slice(t, cont.getStackSizes(), coor, - PerTensorDimens(cont.getStackSizes(), coor)); - for (int iper = 0; iper < pset.getNum(); iper++) { - const Permutation& per = pset.get(iper); - IntSequence percoor(coor.size()); - per.apply(coor, percoor); - for (EquivalenceSet::const_iterator it = eset.begin(); - it != eset.end(); ++it) { - if ((*it).numClasses() == t.dimen()) { - StackProduct sp(cont, *it, out.getSym()); - if (! sp.isZero(percoor)) { - KronProdStack kp(sp, percoor); - kp.optimizeOrder(); - const Permutation& oper = kp.getPer(); - if (Permutation(oper, per) == iden) { - FPSTensor fps(out.getDims(), *it, slice, kp); - { - SYNCHRO@, syn(&out, "WorkerUnfoldMAASparse1"); - fps.addTo(out); - } - } - } - } - } - } -} - -@ -@<|WorkerFoldMAASparse1| constructor code@>= -WorkerFoldMAASparse1::WorkerFoldMAASparse1(const FoldedStackContainer& container, - const FSSparseTensor& ten, - FGSTensor& outten, const IntSequence& c) - : cont(container), t(ten), out(outten), coor(c), ebundle(*(tls.ebundle)) @+{} - - -@ Here is the second implementation of sparse folded |multAndAdd|. It -is pretty similar to implementation of -|@<|UnfoldedStackContainer::multAndAddSparse2| code@>|. We make a -dense folded |slice|, and then call folded |multAndAddStacks|, which -multiplies all the combinations compatible with the slice. - -@<|FoldedStackContainer::multAndAddSparse2| code@>= -void FoldedStackContainer::multAndAddSparse2(const FSSparseTensor& t, - FGSTensor& out) const -{ - THREAD_GROUP@, gr; - FFSTensor dummy_f(0, numStacks(), t.dimen()); - for (Tensor::index fi = dummy_f.begin(); fi != dummy_f.end(); ++fi) { - THREAD* worker = new WorkerFoldMAASparse2(*this, t, out, fi.getCoor()); - gr.insert(worker); - } - gr.run(); -} - -@ Here we make a sparse slice first and then call |multAndAddStacks| -if the slice is not empty. If the slice is really sparse, we call -sparse version of |multAndAddStacks|. What means ``really sparse'' is -given by |fill_threshold|. It is not tuned yet, a practice shows that -it must be a really low number, since sparse |multAndAddStacks| is -much slower than the dense version. - -Further, we take only nonzero rows of the slice, and accordingly of -the out tensor. We jump over zero initial rows and drop zero tailing -rows. - -@<|WorkerFoldMAASparse2::operator()()| code@>= -void WorkerFoldMAASparse2::operator()() -{ - GSSparseTensor slice(t, cont.getStackSizes(), coor, - TensorDimens(cont.getStackSizes(), coor)); - if (slice.getNumNonZero()) { - if (slice.getUnfoldIndexFillFactor() > FoldedStackContainer::fill_threshold) { - FGSTensor dense_slice(slice); - int r1 = slice.getFirstNonZeroRow(); - int r2 = slice.getLastNonZeroRow(); - FGSTensor dense_slice1(r1, r2-r1+1, dense_slice); - FGSTensor out1(r1, r2-r1+1, out); - cont.multAndAddStacks(coor, dense_slice1, out1, &out); - } else - cont.multAndAddStacks(coor, slice, out, &out); - } -} - -@ -@<|WorkerFoldMAASparse2| constructor code@>= -WorkerFoldMAASparse2::WorkerFoldMAASparse2(const FoldedStackContainer& container, - const FSSparseTensor& ten, - FGSTensor& outten, const IntSequence& c) - : cont(container), t(ten), out(outten), coor(c) -{} - - -@ Here is the third implementation of the sparse folded -|multAndAdd|. It is column-wise implementation, and thus is not a good -candidate for the best performer. - -We go through all columns from the output. For each column we -calculate folded |sumcol| which is a sum of all appropriate columns -for all suitable equivalences. So we go through all suitable -equivalences, for each we construct a |StackProduct| object and -construct |IrregTensor| for a corresponding column of $z$. The -|IrregTensor| is an abstraction for Kronecker multiplication of -stacked columns of the two containers without zeros. Then the column -is added to |sumcol|. Finally, the |sumcol| is multiplied by the -sparse tensor. - -@<|FoldedStackContainer::multAndAddSparse3| code@>= -void FoldedStackContainer::multAndAddSparse3(const FSSparseTensor& t, - FGSTensor& out) const -{ - const EquivalenceSet& eset = ebundle.get(out.dimen()); - for (Tensor::index run = out.begin(); run != out.end(); ++run) { - Vector outcol(out, *run); - FRSingleTensor sumcol(t.nvar(), t.dimen()); - sumcol.zeros(); - for (EquivalenceSet::const_iterator it = eset.begin(); - it != eset.end(); ++it) { - if ((*it).numClasses() == t.dimen()) { - StackProduct sp(*this, *it, out.getSym()); - IrregTensorHeader header(sp, run.getCoor()); - IrregTensor irten(header); - irten.addTo(sumcol); - } - } - t.multColumnAndAdd(sumcol, outcol); - } -} - -@ Here is the fourth implementation of sparse -|FoldedStackContainer::multAndAdd|. It is almost equivalent to -|multAndAddSparse2| with the exception that the |FPSTensor| as a -result of a product of a slice and Kronecker product of the stack -derivatives is calculated in the sparse fashion. For further details, see -|@<|FoldedStackContainer::multAndAddStacks| sparse code@>| and -|@<|FPSTensor| sparse constructor@>|. - -@<|FoldedStackContainer::multAndAddSparse4| code@>= -void FoldedStackContainer::multAndAddSparse4(const FSSparseTensor& t, FGSTensor& out) const -{ - THREAD_GROUP@, gr; - FFSTensor dummy_f(0, numStacks(), t.dimen()); - for (Tensor::index fi = dummy_f.begin(); fi != dummy_f.end(); ++fi) { - THREAD* worker = new WorkerFoldMAASparse4(*this, t, out, fi.getCoor()); - gr.insert(worker); - } - gr.run(); -} - -@ The |WorkerFoldMAASparse4| is the same as |WorkerFoldMAASparse2| -with the exception that we call a sparse version of -|multAndAddStacks|. - -@<|WorkerFoldMAASparse4::operator()()| code@>= -void WorkerFoldMAASparse4::operator()() -{ - GSSparseTensor slice(t, cont.getStackSizes(), coor, - TensorDimens(cont.getStackSizes(), coor)); - if (slice.getNumNonZero()) - cont.multAndAddStacks(coor, slice, out, &out); -} - -@ -@<|WorkerFoldMAASparse4| constructor code@>= -WorkerFoldMAASparse4::WorkerFoldMAASparse4(const FoldedStackContainer& container, - const FSSparseTensor& ten, - FGSTensor& outten, const IntSequence& c) - : cont(container), t(ten), out(outten), coor(c) -{} - - -@ This is almost the same as -|@<|UnfoldedStackContainer::multAndAddStacks| code@>|. The only -difference is that we do not construct a |UPSTensor| from -|KronProdStack|, but we construct partially folded permuted -symmetry |FPSTensor|. Note that the tensor |g| must be unfolded -in order to be able to multiply with unfolded rows of Kronecker -product. However, columns of such a product are partially -folded giving a rise to the |FPSTensor|. - -@<|FoldedStackContainer::multAndAddStacks| dense code@>= -void FoldedStackContainer::multAndAddStacks(const IntSequence& coor, - const FGSTensor& g, - FGSTensor& out, const void* ad) const -{ - const EquivalenceSet& eset = ebundle.get(out.dimen()); - - UGSTensor ug(g); - UFSTensor dummy_u(0, numStacks(), g.dimen()); - for (Tensor::index ui = dummy_u.begin(); ui != dummy_u.end(); ++ui) { - IntSequence tmp(ui.getCoor()); - tmp.sort(); - if (tmp == coor) { - Permutation sort_per(ui.getCoor()); - sort_per.inverse(); - for (EquivalenceSet::const_iterator it = eset.begin(); - it != eset.end(); ++it) { - if ((*it).numClasses() == g.dimen()) { - StackProduct sp(*this, *it, sort_per, out.getSym()); - if (! sp.isZero(coor)) { - KronProdStack kp(sp, coor); - if (ug.getSym().isFull()) - kp.optimizeOrder(); - FPSTensor fps(out.getDims(), *it, sort_per, ug, kp); - { - SYNCHRO@, syn(ad, "multAndAddStacks"); - fps.addTo(out); - } - } - } - } - } - } -} - -@ This is almost the same as -|@<|FoldedStackContainer::multAndAddStacks| dense code@>|. The only -difference is that the Kronecker product of the stacks is multiplied -with sparse slice |GSSparseTensor| (not dense slice |FGSTensor|). The -multiplication is done in |@<|FPSTensor| sparse constructor@>|. - -@<|FoldedStackContainer::multAndAddStacks| sparse code@>= -void FoldedStackContainer::multAndAddStacks(const IntSequence& coor, - const GSSparseTensor& g, - FGSTensor& out, const void* ad) const -{ - const EquivalenceSet& eset = ebundle.get(out.dimen()); - UFSTensor dummy_u(0, numStacks(), g.dimen()); - for (Tensor::index ui = dummy_u.begin(); ui != dummy_u.end(); ++ui) { - IntSequence tmp(ui.getCoor()); - tmp.sort(); - if (tmp == coor) { - Permutation sort_per(ui.getCoor()); - sort_per.inverse(); - for (EquivalenceSet::const_iterator it = eset.begin(); - it != eset.end(); ++it) { - if ((*it).numClasses() == g.dimen()) { - StackProduct sp(*this, *it, sort_per, out.getSym()); - if (! sp.isZero(coor)) { - KronProdStack kp(sp, coor); - FPSTensor fps(out.getDims(), *it, sort_per, g, kp); - { - SYNCHRO@, syn(ad, "multAndAddStacks"); - fps.addTo(out); - } - } - } - } - } - } -} - -@ Here we simply call either |multAndAddSparse1| or -|multAndAddSparse2|. The first one allows for optimization of -Kronecker products, so it seems to be more efficient. - -@<|UnfoldedStackContainer::multAndAdd| sparse code@>= -void UnfoldedStackContainer::multAndAdd(const FSSparseTensor& t, - UGSTensor& out) const -{ - TL_RAISE_IF(t.nvar() != getAllSize(), - "Wrong number of variables of tensor for UnfoldedStackContainer::multAndAdd"); - multAndAddSparse2(t, out); -} - -@ Here we implement the formula for stacks for fully symmetric tensor -scattered in a number of general symmetry tensors contained in a given -container. The implementations is pretty the same as in -|multAndAddSparse2| but we do not do the slices of sparse tensor, but -only a lookup to the container. - -This means that we do not iterate through a dummy folded tensor to -obtain folded coordinates of stacks, rather we iterate through all -symmetries contained in the container and the coordinates of stacks -are obtained as unfolded identity sequence via the symmetry. The -reason of doing this is that we are unable to calculate symmetry from -stack coordinates as easily as stack coordinates from the symmetry. - -@<|UnfoldedStackContainer::multAndAdd| dense code@>= -void UnfoldedStackContainer::multAndAdd(int dim, const UGSContainer& c, - UGSTensor& out) const -{ - TL_RAISE_IF(c.num() != numStacks(), - "Wrong symmetry length of container for UnfoldedStackContainer::multAndAdd"); - - THREAD_GROUP@, gr; - SymmetrySet ss(dim, c.num()); - for (symiterator si(ss); !si.isEnd(); ++si) { - if (c.check(*si)) { - THREAD* worker = new WorkerUnfoldMAADense(*this, *si, c, out); - gr.insert(worker); - } - } - gr.run(); -} - -@ -@<|WorkerUnfoldMAADense::operator()()| code@>= -void WorkerUnfoldMAADense::operator()() -{ - Permutation iden(dense_cont.num()); - IntSequence coor(sym, iden.getMap()); - const UGSTensor* g = dense_cont.get(sym); - cont.multAndAddStacks(coor, *g, out, &out); -} - -@ -@<|WorkerUnfoldMAADense| constructor code@>= -WorkerUnfoldMAADense::WorkerUnfoldMAADense(const UnfoldedStackContainer& container, - const Symmetry& s, - const UGSContainer& dcontainer, - UGSTensor& outten) - : cont(container), sym(s), dense_cont(dcontainer), out(outten)@+ {} - - -@ Here we implement the formula for unfolded tensors. If, for instance, -a coordinate $z$ of a tensor $\left[f_{z^2}\right]$ is partitioned as -$z=[a, b]$, then we perform the following: -$$ -\eqalign{ -\left[f_{z^2}\right]\left(\sum_c\left[\matrix{a_{c(x)}\cr b_{c(y)}}\right] -\otimes\left[\matrix{a_{c(y)}\cr b_{c(y)}}\right]\right)=& -\left[f_{aa}\right]\left(\sum_ca_{c(x)}\otimes a_{c(y)}\right)+ -\left[f_{ab}\right]\left(\sum_ca_{c(x)}\otimes b_{c(y)}\right)+\cr -&\left[f_{ba}\right]\left(\sum_cb_{c(x)}\otimes a_{c(y)}\right)+ - \left[f_{bb}\right]\left(\sum_cb_{c(x)}\otimes b_{c(y)}\right)\cr -} -$$ -This is exactly what happens here. The code is clear. It goes through -all combinations of stacks, and each thread is responsible for -operation for the slice corresponding to the combination of the stacks. - -@<|UnfoldedStackContainer::multAndAddSparse1| code@>= -void UnfoldedStackContainer::multAndAddSparse1(const FSSparseTensor& t, - UGSTensor& out) const -{ - THREAD_GROUP@, gr; - UFSTensor dummy(0, numStacks(), t.dimen()); - for (Tensor::index ui = dummy.begin(); ui != dummy.end(); ++ui) { - THREAD* worker = new WorkerUnfoldMAASparse1(*this, t, out, ui.getCoor()); - gr.insert(worker); - } - gr.run(); -} - -@ This does a step of |@<|UnfoldedStackContainer::multAndAddSparse1| code@>| for -a given coordinates. First it makes the slice of the given stack coordinates. -Then it multiplies everything what should be multiplied with the slice. -That is it goes through all equivalences, creates |StackProduct|, then -|KronProdStack|, which is added to |out|. So far everything is clear. - -However, we want to use optimized |KronProdAllOptim| to minimize -a number of flops and memory needed in the Kronecker product. So we go -through all permutations |per|, permute the coordinates to get -|percoor|, go through all equivalences, and make |KronProdStack| and -optimize it. The result of optimization is a permutation |oper|. Now, -we multiply the Kronecker product with the slice, only if the slice -has the same ordering of coordinates as the Kronecker product -|KronProdStack|. However, it is not perfectly true. Since we go -through {\bf all} permutations |per|, there might be two different -permutations leading to the same ordering in |KronProdStack| and thus -the same ordering in the optimized |KronProdStack|. The two cases -would be counted twice, which is wrong. That is why we do not -condition on $\hbox{coor}\circ\hbox{oper}\circ\hbox{per} = -\hbox{coor}$, but we condition on -$\hbox{oper}\circ\hbox{per}=\hbox{id}$. In this way, we rule out -permutations |per| leading to the same ordering of stacks when -applied on |coor|. - -todo: vertically narrow slice and out according to the fill in t. - -@<|WorkerUnfoldMAASparse1::operator()()| code@>= -void WorkerUnfoldMAASparse1::operator()() -{ - const EquivalenceSet& eset = ebundle.get(out.dimen()); - const PermutationSet& pset = tls.pbundle->get(t.dimen()); - Permutation iden(t.dimen()); - - UPSTensor slice(t, cont.getStackSizes(), coor, - PerTensorDimens(cont.getStackSizes(), coor)); - for (int iper = 0; iper < pset.getNum(); iper++) { - const Permutation& per = pset.get(iper); - IntSequence percoor(coor.size()); - per.apply(coor, percoor); - for (EquivalenceSet::const_iterator it = eset.begin(); - it != eset.end(); ++it) { - if ((*it).numClasses() == t.dimen()) { - StackProduct sp(cont, *it, out.getSym()); - if (! sp.isZero(percoor)) { - KronProdStack kp(sp, percoor); - kp.optimizeOrder(); - const Permutation& oper = kp.getPer(); - if (Permutation(oper, per) == iden) { - UPSTensor ups(out.getDims(), *it, slice, kp); - { - SYNCHRO@, syn(&out, "WorkerUnfoldMAASparse1"); - ups.addTo(out); - } - } - } - } - } - } -} - -@ -@<|WorkerUnfoldMAASparse1| constructor code@>= -WorkerUnfoldMAASparse1::WorkerUnfoldMAASparse1(const UnfoldedStackContainer& container, - const FSSparseTensor& ten, - UGSTensor& outten, const IntSequence& c) - : cont(container), t(ten), out(outten), coor(c), ebundle(*(tls.ebundle)) @+{} - - -@ In here we implement the formula by a bit different way. We use the -fact, using notation of |@<|UnfoldedStackContainer::multAndAddSparse2| -code@>|, that -$$ -\left[f_{ba}\right]\left(\sum_cb_{c(x)}\otimes a_{c(y)}\right)= -\left[f_{ab}\right]\left(\sum_ca_{c(y)}\otimes b_{c(b)}\right)\cdot P -$$ -where $P$ is a suitable permutation of columns. The permutation -corresponds to (in this example) a swap of $a$ and $b$. An advantage -of this approach is that we do not need |UPSTensor| for $f_{ba}$, and -thus we decrease the number of needed slices. - -So we go through all folded indices of stack coordinates, then for -each such index |fi| we make a slice and call |multAndAddStacks|. This -goes through all corresponding unfolded indices to perform the -formula. Each unsorted (unfold) index implies a sorting permutation -|sort_per| which must be used to permute stacks in |StackProduct|, and -permute equivalence classes when |UPSTensor| is formed. In this way -the column permutation $P$ from the formula is factored to the -permutation of |UPSTensor|. - -@<|UnfoldedStackContainer::multAndAddSparse2| code@>= -void UnfoldedStackContainer::multAndAddSparse2(const FSSparseTensor& t, - UGSTensor& out) const -{ - THREAD_GROUP@, gr; - FFSTensor dummy_f(0, numStacks(), t.dimen()); - for (Tensor::index fi = dummy_f.begin(); fi != dummy_f.end(); ++fi) { - THREAD* worker = new WorkerUnfoldMAASparse2(*this, t, out, fi.getCoor()); - gr.insert(worker); - } - gr.run(); -} - -@ This does a step of |@<|UnfoldedStackContainer::multAndAddSparse2| code@>| for -a given coordinates. - -todo: implement |multAndAddStacks| for sparse slice as -|@<|FoldedStackContainer::multAndAddStacks| sparse code@>| and do this method as -|@<|WorkerFoldMAASparse2::operator()()| code@>|. - -@<|WorkerUnfoldMAASparse2::operator()()| code@>= -void WorkerUnfoldMAASparse2::operator()() -{ - GSSparseTensor slice(t, cont.getStackSizes(), coor, - TensorDimens(cont.getStackSizes(), coor)); - if (slice.getNumNonZero()) { - FGSTensor fslice(slice); - UGSTensor dense_slice(fslice); - int r1 = slice.getFirstNonZeroRow(); - int r2 = slice.getLastNonZeroRow(); - UGSTensor dense_slice1(r1, r2-r1+1, dense_slice); - UGSTensor out1(r1, r2-r1+1, out); - - cont.multAndAddStacks(coor, dense_slice1, out1, &out); - } -} - -@ -@<|WorkerUnfoldMAASparse2| constructor code@>= -WorkerUnfoldMAASparse2::WorkerUnfoldMAASparse2(const UnfoldedStackContainer& container, - const FSSparseTensor& ten, - UGSTensor& outten, const IntSequence& c) - : cont(container), t(ten), out(outten), coor(c) @+{} - - -@ For a given unfolded coordinates of stacks |fi|, and appropriate -tensor $g$, whose symmetry is a symmetry of |fi|, the method -contributes to |out| all tensors in unfolded stack formula involving -stacks chosen by |fi|. - -We go through all |ui| coordinates which yield |fi| after sorting. We -construct a permutation |sort_per| which sorts |ui| to |fi|. We go -through all appropriate equivalences, and construct |StackProduct| -from equivalence classes permuted by |sort_per|, then |UPSTensor| with -implied permutation of columns by the permuted equivalence by -|sort_per|. The |UPSTensor| is then added to |out|. - -We cannot use here the optimized |KronProdStack|, since the symmetry -of |UGSTensor& g| prescribes the ordering of the stacks. However, if -|g| is fully symmetric, we can do the optimization harmlessly. - -@<|UnfoldedStackContainer::multAndAddStacks| code@>= -void UnfoldedStackContainer::multAndAddStacks(const IntSequence& fi, - const UGSTensor& g, - UGSTensor& out, const void* ad) const -{ - const EquivalenceSet& eset = ebundle.get(out.dimen()); - - UFSTensor dummy_u(0, numStacks(), g.dimen()); - for (Tensor::index ui = dummy_u.begin(); ui != dummy_u.end(); ++ui) { - IntSequence tmp(ui.getCoor()); - tmp.sort(); - if (tmp == fi) { - Permutation sort_per(ui.getCoor()); - sort_per.inverse(); - for (EquivalenceSet::const_iterator it = eset.begin(); - it != eset.end(); ++it) { - if ((*it).numClasses() == g.dimen()) { - StackProduct sp(*this, *it, sort_per, out.getSym()); - if (! sp.isZero(fi)) { - KronProdStack kp(sp, fi); - if (g.getSym().isFull()) - kp.optimizeOrder(); - UPSTensor ups(out.getDims(), *it, sort_per, g, kp); - { - SYNCHRO@, syn(ad, "multAndAddStacks"); - ups.addTo(out); - } - } - } - } - } - } -} - -@ End of {\tt stack\_container.cpp} file. diff --git a/dynare++/tl/cc/stack_container.hh b/dynare++/tl/cc/stack_container.hh new file mode 100644 index 000000000..0276b4a49 --- /dev/null +++ b/dynare++/tl/cc/stack_container.hh @@ -0,0 +1,744 @@ +// Copyright 2004, Ondra Kamenik + +// Stack of containers. + +/* Here we develop abstractions for stacked containers of tensors. For + instance, in perturbation methods for SDGE we need function + $$z(y,u,u',\sigma)=\left[\matrix{G(y,u,u',\sigma)\cr g(y,u,\sigma)\cr y\cr u}\right]$$ + and we need to calculate one step of Faa Di Bruno formula + $$\left[B_{s^k}\right]_{\alpha_1\ldots\alpha_l}=\left[f_{z^l}\right]_{\beta_1\ldots\beta_l} + \sum_{c\in M_{l,k}}\prod_{m=1}^l\left[z_{s^k(c_m)}\right]^{\beta_m}_{c_m(\alpha)}$$ + where we have containers for derivatives of $G$ and $g$. + + The main purpose of this file is to define abstractions for stack of + containers and possibly raw variables, and code |multAndAdd| method + calculating (one step of) the Faa Di Bruno formula for folded and + unfolded tensors. Note also, that tensors $\left[f_{z^l}\right]$ are + sparse. + + The abstractions are built as follows. At the top, there is an + interface describing stack of columns. It contains pure virtual + methods needed for manipulating the container stack. For technical + reasons it is a template. Both versions (folded, and unfolded) provide + all interface necessary for implementation of |multAndAdd|. The second + way of inheritance is first general implementation of the interface + |StackContainer|, and then specific (|ZContainer| for our specific + $z$). The only method which is virtual also after |StackContainer| is + |getType|, which is implemented in the specialization and determines + behaviour of the stack. The complete classes are obtained by + inheriting from the both branches, as it is drawn below: + + \def\drawpenta#1#2#3#4#5{% + \hbox{$ + \hgrid=40pt\vgrid=20pt% + \sarrowlength=25pt% + \gridcommdiag{% + &&\hbox{#1}&&\cr + &\llap{virtual}\arrow(-1,-1)&&\arrow(1,-1)\rlap{virtual}&\cr + \hbox{#2}&&&&\hbox{#3}\cr + \arrow(0,-1)&&&&\cr + \hbox{#4}&&& + {\multiply\sarrowlength by 63\divide\sarrowlength by 50\arrow(-1,-2)}&\cr + &\arrow(1,-1)&&&\cr + &&\hbox{#5}&&\cr + }$}} + + \centerline{ + \drawpenta{|StackContainerInterface|}{|StackContainer|}% + {|FoldedStackContainer|}{|ZContainer|}{|FoldedZContainer|} + } + + \centerline{ + \drawpenta{|StackContainerInterface|}{|StackContainer|}% + {|UnfoldedStackContainer|}{|ZContainer|}{|UnfoldedZContainer|} + } + + We have also two supporting classes |StackProduct| and |KronProdStack| + and a number of worker classes used as threads. */ + +#ifndef STACK_CONTAINER_H +#define STACK_CONTAINER_H + +#include "int_sequence.hh" +#include "equivalence.hh" +#include "tl_static.hh" +#include "t_container.hh" +#include "kron_prod.hh" +#include "permutation.hh" +#include "sthread.hh" + +/* Here is the general interface to stack container. The subclasses + maintain |IntSequence| of stack sizes, i.e. size of $G$, $g$, $y$, and + $u$. Then a convenience |IntSequence| of stack offsets. Then vector of + pointers to containers, in our example $G$, and $g$. + + A non-virtual subclass must implement |getType| which determines + dependency of stack items on symmetries. There are three possible types + for a symmetry. Either the stack item derivative wrt. the symmetry is + a matrix, or a unit matrix, or zero. + + Method |isZero| returns true if the derivative of a given stack item + wrt. to given symmetry is zero as defined by |getType| or the + derivative is not present in the container. In this way, we can + implement the formula conditional some of the tensors are zero, which + is not true (they are only missing). + + Method |createPackedColumn| returns a vector of stack derivatives with + respect to the given symmetry and of the given column, where all zeros + from zero types, or unit matrices are deleted. See {\tt + kron\_prod2.hweb} for explanation. */ + +template +class StackContainerInterface +{ +public: + typedef TensorContainer<_Ttype> _Ctype; + typedef enum { matrix, unit, zero} itype; +protected: + const EquivalenceBundle &ebundle; +public: + StackContainerInterface() + : ebundle(*(tls.ebundle)) + { + } + virtual ~StackContainerInterface() + { + } + virtual const IntSequence&getStackSizes() const = 0; + virtual IntSequence&getStackSizes() = 0; + virtual const IntSequence&getStackOffsets() const = 0; + virtual IntSequence&getStackOffsets() = 0; + virtual int numConts() const = 0; + virtual const _Ctype *getCont(int i) const = 0; + virtual itype getType(int i, const Symmetry &s) const = 0; + virtual int numStacks() const = 0; + virtual bool isZero(int i, const Symmetry &s) const = 0; + virtual const _Ttype *getMatrix(int i, const Symmetry &s) const = 0; + virtual int getLengthOfMatrixStacks(const Symmetry &s) const = 0; + virtual int getUnitPos(const Symmetry &s) const = 0; + virtual Vector *createPackedColumn(const Symmetry &s, + const IntSequence &coor, + int &iu) const = 0; + int + getAllSize() const + { + return getStackOffsets()[numStacks()-1] + + getStackSizes()[numStacks()-1]; + } +}; + +/* Here is |StackContainer|, which implements almost all interface + |StackContainerInterface| but one method |getType| which is left for + implementation to specializations. */ + +template +class StackContainer : virtual public StackContainerInterface<_Ttype> +{ +public: + typedef StackContainerInterface<_Ttype> _Stype; + typedef typename StackContainerInterface<_Ttype>::_Ctype _Ctype; + typedef typename StackContainerInterface<_Ttype>::itype itype; +protected: + int num_conts; + IntSequence stack_sizes; + IntSequence stack_offsets; + const _Ctype **const conts; +public: + StackContainer(int ns, int nc) + : num_conts(nc), stack_sizes(ns, 0), stack_offsets(ns, 0), + conts(new const _Ctype *[nc]) + { + } + virtual ~StackContainer() + { + delete [] conts; + } + const IntSequence & + getStackSizes() const + { + return stack_sizes; + } + IntSequence & + getStackSizes() + { + return stack_sizes; + } + const IntSequence & + getStackOffsets() const + { + return stack_offsets; + } + IntSequence & + getStackOffsets() + { + return stack_offsets; + } + int + numConts() const + { + return num_conts; + } + const _Ctype * + getCont(int i) const + { + return conts[i]; + } + virtual itype getType(int i, const Symmetry &s) const = 0; + int + numStacks() const + { + return stack_sizes.size(); + } + bool + isZero(int i, const Symmetry &s) const + { + TL_RAISE_IF(i < 0 || i >= numStacks(), + "Wrong index to stack in StackContainer::isZero."); + return (getType(i, s) == _Stype::zero + || (getType(i, s) == _Stype::matrix && !conts[i]->check(s))); + } + + const _Ttype * + getMatrix(int i, const Symmetry &s) const + { + TL_RAISE_IF(isZero(i, s) || getType(i, s) == _Stype::unit, + "Matrix is not returned in StackContainer::getMatrix"); + return conts[i]->get(s); + } + + int + getLengthOfMatrixStacks(const Symmetry &s) const + { + int res = 0; + int i = 0; + while (i < numStacks() && getType(i, s) == _Stype::matrix) + res += stack_sizes[i++]; + return res; + } + + int + getUnitPos(const Symmetry &s) const + { + if (s.dimen() != 1) + return -1; + int i = numStacks()-1; + while (i >= 0 && getType(i, s) != _Stype::unit) + i--; + return i; + } + + Vector * + createPackedColumn(const Symmetry &s, + const IntSequence &coor, int &iu) const + { + TL_RAISE_IF(s.dimen() != coor.size(), + "Incompatible coordinates for symmetry in StackContainer::createPackedColumn"); + + int len = getLengthOfMatrixStacks(s); + iu = -1; + int i = 0; + if (-1 != (i = getUnitPos(s))) + { + iu = stack_offsets[i] + coor[0]; + len++; + } + + Vector *res = new Vector(len); + i = 0; + while (i < numStacks() && getType(i, s) == _Stype::matrix) + { + const _Ttype *t = getMatrix(i, s); + Tensor::index ind(t, coor); + Vector subres(*res, stack_offsets[i], stack_sizes[i]); + subres = ConstVector(ConstGeneralMatrix(*t), *ind); + i++; + } + if (iu != -1) + (*res)[len-1] = 1; + + return res; + } + +protected: + void + calculateOffsets() + { + stack_offsets[0] = 0; + for (int i = 1; i < stack_offsets.size(); i++) + stack_offsets[i] = stack_offsets[i-1] + stack_sizes[i-1]; + } +}; + +class WorkerFoldMAADense; +class WorkerFoldMAASparse1; +class WorkerFoldMAASparse2; +class WorkerFoldMAASparse4; +class FoldedStackContainer : virtual public StackContainerInterface +{ + friend class WorkerFoldMAADense; + friend class WorkerFoldMAASparse1; + friend class WorkerFoldMAASparse2; + friend class WorkerFoldMAASparse4; +public: + static double fill_threshold; + void + multAndAdd(int dim, const TensorContainer &c, + FGSTensor &out) const + { + if (c.check(Symmetry(dim))) + multAndAdd(*(c.get(Symmetry(dim))), out); + } + void multAndAdd(const FSSparseTensor &t, FGSTensor &out) const; + void multAndAdd(int dim, const FGSContainer &c, FGSTensor &out) const; +protected: + void multAndAddSparse1(const FSSparseTensor &t, FGSTensor &out) const; + void multAndAddSparse2(const FSSparseTensor &t, FGSTensor &out) const; + void multAndAddSparse3(const FSSparseTensor &t, FGSTensor &out) const; + void multAndAddSparse4(const FSSparseTensor &t, FGSTensor &out) const; + void multAndAddStacks(const IntSequence &fi, const FGSTensor &g, + FGSTensor &out, const void *ad) const; + void multAndAddStacks(const IntSequence &fi, const GSSparseTensor &g, + FGSTensor &out, const void *ad) const; +}; + +class WorkerUnfoldMAADense; +class WorkerUnfoldMAASparse1; +class WorkerUnfoldMAASparse2; +class UnfoldedStackContainer : virtual public StackContainerInterface +{ + friend class WorkerUnfoldMAADense; + friend class WorkerUnfoldMAASparse1; + friend class WorkerUnfoldMAASparse2; +public: + static double fill_threshold; + void + multAndAdd(int dim, const TensorContainer &c, + UGSTensor &out) const + { + if (c.check(Symmetry(dim))) + multAndAdd(*(c.get(Symmetry(dim))), out); + } + void multAndAdd(const FSSparseTensor &t, UGSTensor &out) const; + void multAndAdd(int dim, const UGSContainer &c, UGSTensor &out) const; +protected: + void multAndAddSparse1(const FSSparseTensor &t, UGSTensor &out) const; + void multAndAddSparse2(const FSSparseTensor &t, UGSTensor &out) const; + void multAndAddStacks(const IntSequence &fi, const UGSTensor &g, + UGSTensor &out, const void *ad) const; +}; + +/* Here is the specialization of the |StackContainer|. We implement + here the $z$ needed in SDGE context. We implement |getType| and define + a constructor feeding the data and sizes. + + Note that it has two containers, the first is dependent on four + variables $G(y^*,u,u',\sigma)$, and the second dependent on three + variables $g(y^*,u,\sigma)$. So that we would be able to stack them, + we make the second container $g$ be dependent on four variables, the + third being $u'$ a dummy and always returning zero if dimension of + $u'$ is positive. */ + +template +class ZContainer : public StackContainer<_Ttype> +{ +public: + typedef StackContainer<_Ttype> _Tparent; + typedef StackContainerInterface<_Ttype> _Stype; + typedef typename _Tparent::_Ctype _Ctype; + typedef typename _Tparent::itype itype; + ZContainer(const _Ctype *gss, int ngss, const _Ctype *g, int ng, + int ny, int nu) + : _Tparent(4, 2) + { + _Tparent::stack_sizes[0] = ngss; _Tparent::stack_sizes[1] = ng; + _Tparent::stack_sizes[2] = ny; _Tparent::stack_sizes[3] = nu; + _Tparent::conts[0] = gss; + _Tparent::conts[1] = g; + _Tparent::calculateOffsets(); + } + + /* Here we say, what happens if we derive $z$. recall the top of the + file, how $z$ looks, and code is clear. */ + + itype + getType(int i, const Symmetry &s) const + { + if (i == 0) + return _Stype::matrix; + if (i == 1) + if (s[2] > 0) + return _Stype::zero; + else + return _Stype::matrix; + if (i == 2) + if (s == Symmetry(1, 0, 0, 0)) + return _Stype::unit; + else + return _Stype::zero; + if (i == 3) + if (s == Symmetry(0, 1, 0, 0)) + return _Stype::unit; + else + return _Stype::zero; + + TL_RAISE("Wrong stack index in ZContainer::getType"); + return _Stype::zero; + } + +}; + +class FoldedZContainer : public ZContainer, + public FoldedStackContainer +{ +public: + typedef TensorContainer _Ctype; + FoldedZContainer(const _Ctype *gss, int ngss, const _Ctype *g, int ng, + int ny, int nu) + : ZContainer(gss, ngss, g, ng, ny, nu) + { + } +}; + +class UnfoldedZContainer : public ZContainer, + public UnfoldedStackContainer +{ +public: + typedef TensorContainer _Ctype; + UnfoldedZContainer(const _Ctype *gss, int ngss, const _Ctype *g, int ng, + int ny, int nu) + : ZContainer(gss, ngss, g, ng, ny, nu) + { + } +}; + +/* Here we have another specialization of container used in context of + SDGE. We define a container for + $$G(y,u,u',\sigma)=g^{**}(g^*(y,u,\sigma),u',\sigma)$$ + + For some reason, the symmetry of $g^{**}$ has length $4$ although it + is really dependent on three variables. (To now the reason, consult + |@<|ZContainer| class declaration@>|.) So, it has four stack, the + third one is dummy, and always returns zero. The first stack + corresponds to a container of $g^*$. */ + +template +class GContainer : public StackContainer<_Ttype> +{ +public: + typedef StackContainer<_Ttype> _Tparent; + typedef StackContainerInterface<_Ttype> _Stype; + typedef typename StackContainer<_Ttype>::_Ctype _Ctype; + typedef typename StackContainer<_Ttype>::itype itype; + GContainer(const _Ctype *gs, int ngs, int nu) + : StackContainer<_Ttype>(4, 1) + { + _Tparent::stack_sizes[0] = ngs; _Tparent::stack_sizes[1] = nu; + _Tparent::stack_sizes[2] = nu; _Tparent::stack_sizes[3] = 1; + _Tparent::conts[0] = gs; + _Tparent::calculateOffsets(); + } + + /* Here we define the dependencies in + $g^{**}(g^*(y,u,\sigma),u',\sigma)$. Also note, that first derivative + of $g^*$ wrt $\sigma$ is always zero, so we also add this + information. */ + + itype + getType(int i, const Symmetry &s) const + { + if (i == 0) + if (s[2] > 0 || s == Symmetry(0, 0, 0, 1)) + return _Stype::zero; + else + return _Stype::matrix; + if (i == 1) + if (s == Symmetry(0, 0, 1, 0)) + return _Stype::unit; + else + return _Stype::zero; + if (i == 2) + return _Stype::zero; + if (i == 3) + if (s == Symmetry(0, 0, 0, 1)) + return _Stype::unit; + else + return _Stype::zero; + + TL_RAISE("Wrong stack index in GContainer::getType"); + return _Stype::zero; + } + +}; + +class FoldedGContainer : public GContainer, + public FoldedStackContainer +{ +public: + typedef TensorContainer _Ctype; + FoldedGContainer(const _Ctype *gs, int ngs, int nu) + : GContainer(gs, ngs, nu) + { + } +}; + +class UnfoldedGContainer : public GContainer, + public UnfoldedStackContainer +{ +public: + typedef TensorContainer _Ctype; + UnfoldedGContainer(const _Ctype *gs, int ngs, int nu) + : GContainer(gs, ngs, nu) + { + } +}; + +/* Here we have a support class for product of |StackContainer|s. It + only adds a dimension to |StackContainer|. It selects the symmetries + according to equivalence classes passed to the constructor. The + equivalence can have permuted classes by some given + permutation. Nothing else is interesting. */ + +template +class StackProduct +{ +public: + typedef StackContainerInterface<_Ttype> _Stype; + typedef typename _Stype::_Ctype _Ctype; + typedef typename _Stype::itype itype; +protected: + const _Stype &stack_cont; + InducedSymmetries syms; + Permutation per; +public: + StackProduct(const _Stype &sc, const Equivalence &e, + const Symmetry &os) + : stack_cont(sc), syms(e, os), per(e) + { + } + StackProduct(const _Stype &sc, const Equivalence &e, + const Permutation &p, const Symmetry &os) + : stack_cont(sc), syms(e, p, os), per(e, p) + { + } + int + dimen() const + { + return syms.size(); + } + int + getAllSize() const + { + return stack_cont.getAllSize(); + } + const Symmetry & + getProdSym(int ip) const + { + return syms[ip]; + } + bool + isZero(const IntSequence &istacks) const + { + TL_RAISE_IF(istacks.size() != dimen(), + "Wrong istacks coordinates for StackProduct::isZero"); + + bool res = false; + int i = 0; + while (i < dimen() && !(res = stack_cont.isZero(istacks[i], syms[i]))) + i++; + return res; + } + + itype + getType(int is, int ip) const + { + TL_RAISE_IF(is < 0 || is >= stack_cont.numStacks(), + "Wrong index to stack in StackProduct::getType"); + TL_RAISE_IF(ip < 0 || ip >= dimen(), + "Wrong index to stack container in StackProduct::getType"); + return stack_cont.getType(is, syms[ip]); + } + + const _Ttype * + getMatrix(int is, int ip) const + { + return stack_cont.getMatrix(is, syms[ip]); + } + + void + createPackedColumns(const IntSequence &coor, + Vector **vs, IntSequence &iu) const + { + TL_RAISE_IF(iu.size() != dimen(), + "Wrong storage length for unit flags in StackProduct::createPackedColumn"); + TL_RAISE_IF(coor.size() != per.size(), + "Wrong size of index coor in StackProduct::createPackedColumn"); + IntSequence perindex(coor.size()); + per.apply(coor, perindex); + int off = 0; + for (int i = 0; i < dimen(); i++) + { + IntSequence percoor(perindex, off, syms[i].dimen() + off); + vs[i] = stack_cont.createPackedColumn(syms[i], percoor, iu[i]); + off += syms[i].dimen(); + } + } + + int + getSize(int is) const + { + return stack_cont.getStackSizes()[is]; + } + + int + numMatrices(const IntSequence &istacks) const + { + TL_RAISE_IF(istacks.size() != dimen(), + "Wrong size of stack coordinates in StackContainer::numMatrices"); + int ret = 0; + int ip = 0; + while (ip < dimen() && getType(istacks[ip], ip) == _Stype::matrix) + { + ret++; + ip++; + } + return ret; + } +}; + +/* Here we only inherit from Kronecker product |KronProdAllOptim|, only to + allow for a constructor constructing from |StackProduct|. */ + +template +class KronProdStack : public KronProdAllOptim +{ +public: + typedef StackProduct<_Ttype> _Ptype; + typedef StackContainerInterface<_Ttype> _Stype; + + /* Here we construct |KronProdAllOptim| from |StackContainer| and given + selections of stack items from stack containers in the product. We + only decide whether to insert matrix, or unit matrix. + + At this point, we do not call |KronProdAllOptim::optimizeOrder|, so + the |KronProdStack| behaves like |KronProdAll| (i.e. no optimization + is done). */ + + KronProdStack(const _Ptype &sp, const IntSequence &istack) + : KronProdAllOptim(sp.dimen()) + { + TL_RAISE_IF(sp.dimen() != istack.size(), + "Wrong stack product dimension for KronProdStack constructor"); + + for (int i = 0; i < sp.dimen(); i++) + { + TL_RAISE_IF(sp.getType(istack[i], i) == _Stype::zero, + "Attempt to construct KronProdStack from zero matrix"); + if (sp.getType(istack[i], i) == _Stype::unit) + setUnit(i, sp.getSize(istack[i])); + if (sp.getType(istack[i], i) == _Stype::matrix) + { + const TwoDMatrix *m = sp.getMatrix(istack[i], i); + TL_RAISE_IF(m->nrows() != sp.getSize(istack[i]), + "Wrong size of returned matrix in KronProdStack constructor"); + setMat(i, *m); + } + } + } +}; + +class WorkerFoldMAADense : public THREAD +{ + const FoldedStackContainer &cont; + Symmetry sym; + const FGSContainer &dense_cont; + FGSTensor &out; +public: + WorkerFoldMAADense(const FoldedStackContainer &container, + const Symmetry &s, + const FGSContainer &dcontainer, + FGSTensor &outten); + void operator()(); +}; + +class WorkerFoldMAASparse1 : public THREAD +{ + const FoldedStackContainer &cont; + const FSSparseTensor &t; + FGSTensor &out; + IntSequence coor; + const EquivalenceBundle &ebundle; +public: + WorkerFoldMAASparse1(const FoldedStackContainer &container, + const FSSparseTensor &ten, + FGSTensor &outten, const IntSequence &c); + void operator()(); +}; + +class WorkerFoldMAASparse2 : public THREAD +{ + const FoldedStackContainer &cont; + const FSSparseTensor &t; + FGSTensor &out; + IntSequence coor; +public: + WorkerFoldMAASparse2(const FoldedStackContainer &container, + const FSSparseTensor &ten, + FGSTensor &outten, const IntSequence &c); + void operator()(); +}; + +class WorkerFoldMAASparse4 : public THREAD +{ + const FoldedStackContainer &cont; + const FSSparseTensor &t; + FGSTensor &out; + IntSequence coor; +public: + WorkerFoldMAASparse4(const FoldedStackContainer &container, + const FSSparseTensor &ten, + FGSTensor &outten, const IntSequence &c); + void operator()(); +}; + +class WorkerUnfoldMAADense : public THREAD +{ + const UnfoldedStackContainer &cont; + Symmetry sym; + const UGSContainer &dense_cont; + UGSTensor &out; +public: + WorkerUnfoldMAADense(const UnfoldedStackContainer &container, + const Symmetry &s, + const UGSContainer &dcontainer, + UGSTensor &outten); + void operator()(); +}; + +class WorkerUnfoldMAASparse1 : public THREAD +{ + const UnfoldedStackContainer &cont; + const FSSparseTensor &t; + UGSTensor &out; + IntSequence coor; + const EquivalenceBundle &ebundle; +public: + WorkerUnfoldMAASparse1(const UnfoldedStackContainer &container, + const FSSparseTensor &ten, + UGSTensor &outten, const IntSequence &c); + void operator()(); +}; + +class WorkerUnfoldMAASparse2 : public THREAD +{ + const UnfoldedStackContainer &cont; + const FSSparseTensor &t; + UGSTensor &out; + IntSequence coor; +public: + WorkerUnfoldMAASparse2(const UnfoldedStackContainer &container, + const FSSparseTensor &ten, + UGSTensor &outten, const IntSequence &c); + void operator()(); +}; + +#endif diff --git a/dynare++/tl/cc/stack_container.hweb b/dynare++/tl/cc/stack_container.hweb deleted file mode 100644 index 77ca511bb..000000000 --- a/dynare++/tl/cc/stack_container.hweb +++ /dev/null @@ -1,771 +0,0 @@ -@q $Id: stack_container.hweb 745 2006-05-09 13:20:00Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@*2 Stack of containers. Start of {\tt stack\_container.h} file. - -Here we develop abstractions for stacked containers of tensors. For -instance, in perturbation methods for SDGE we need function -$$z(y,u,u',\sigma)=\left[\matrix{G(y,u,u',\sigma)\cr g(y,u,\sigma)\cr y\cr u}\right]$$ -and we need to calculate one step of Faa Di Bruno formula -$$\left[B_{s^k}\right]_{\alpha_1\ldots\alpha_l}=\left[f_{z^l}\right]_{\beta_1\ldots\beta_l} -\sum_{c\in M_{l,k}}\prod_{m=1}^l\left[z_{s^k(c_m)}\right]^{\beta_m}_{c_m(\alpha)}$$ -where we have containers for derivatives of $G$ and $g$. - -The main purpose of this file is to define abstractions for stack of -containers and possibly raw variables, and code |multAndAdd| method -calculating (one step of) the Faa Di Bruno formula for folded and -unfolded tensors. Note also, that tensors $\left[f_{z^l}\right]$ are -sparse. - -The abstractions are built as follows. At the top, there is an -interface describing stack of columns. It contains pure virtual -methods needed for manipulating the container stack. For technical -reasons it is a template. Both versions (folded, and unfolded) provide -all interface necessary for implementation of |multAndAdd|. The second -way of inheritance is first general implementation of the interface -|StackContainer|, and then specific (|ZContainer| for our specific -$z$). The only method which is virtual also after |StackContainer| is -|getType|, which is implemented in the specialization and determines -behaviour of the stack. The complete classes are obtained by -inheriting from the both branches, as it is drawn below: - -\def\drawpenta#1#2#3#4#5{% -\hbox{$ -\hgrid=40pt\vgrid=20pt% -\sarrowlength=25pt% -\gridcommdiag{% -&&\hbox{#1}&&\cr -&\llap{virtual}\arrow(-1,-1)&&\arrow(1,-1)\rlap{virtual}&\cr -\hbox{#2}&&&&\hbox{#3}\cr -\arrow(0,-1)&&&&\cr -\hbox{#4}&&& -{\multiply\sarrowlength by 63\divide\sarrowlength by 50\arrow(-1,-2)}&\cr -&\arrow(1,-1)&&&\cr -&&\hbox{#5}&&\cr -}$}} - -\centerline{ -\drawpenta{|StackContainerInterface|}{|StackContainer|}% - {|FoldedStackContainer|}{|ZContainer|}{|FoldedZContainer|} -} - -\centerline{ -\drawpenta{|StackContainerInterface|}{|StackContainer|}% - {|UnfoldedStackContainer|}{|ZContainer|}{|UnfoldedZContainer|} -} - -We have also two supporting classes |StackProduct| and |KronProdStack| -and a number of worker classes used as threads. - -@s StackContainerInterface int -@s StackContainer int -@s ZContainer int -@s FoldedStackContainer int -@s UnfoldedStackContainer int -@s FoldedZContainer int -@s UnfoldedZContainer int -@s WorkerFoldMAADense int -@s WorkerFoldMAASparse1 int -@s WorkerFoldMAASparse2 int -@s WorkerFoldMAASparse4 int -@s WorkerUnfoldMAADense int -@s WorkerUnfoldMAASparse1 int -@s WorkerUnfoldMAASparse2 int -@s GContainer int -@s FoldedGContainer int -@s UnfoldedGContainer int -@s StackProduct int -@s KronProdStack int - -@c -#ifndef STACK_CONTAINER_H -#define STACK_CONTAINER_H - -#include "int_sequence.h" -#include "equivalence.h" -#include "tl_static.h" -#include "t_container.h" -#include "kron_prod.h" -#include "permutation.h" -#include "sthread.h" - -@<|StackContainerInterface| class declaration@>; -@<|StackContainer| class declaration@>; -@<|FoldedStackContainer| class declaration@>; -@<|UnfoldedStackContainer| class declaration@>; -@<|ZContainer| class declaration@>; -@<|FoldedZContainer| class declaration@>; -@<|UnfoldedZContainer| class declaration@>; -@<|GContainer| class declaration@>; -@<|FoldedGContainer| class declaration@>; -@<|UnfoldedGContainer| class declaration@>; -@<|StackProduct| class declaration@>; -@<|KronProdStack| class declaration@>; -@<|WorkerFoldMAADense| class declaration@>; -@<|WorkerFoldMAASparse1| class declaration@>; -@<|WorkerFoldMAASparse2| class declaration@>; -@<|WorkerFoldMAASparse4| class declaration@>; -@<|WorkerUnfoldMAADense| class declaration@>; -@<|WorkerUnfoldMAASparse1| class declaration@>; -@<|WorkerUnfoldMAASparse2| class declaration@>; - -#endif - -@ Here is the general interface to stack container. The subclasses -maintain |IntSequence| of stack sizes, i.e. size of $G$, $g$, $y$, and -$u$. Then a convenience |IntSequence| of stack offsets. Then vector of -pointers to containers, in our example $G$, and $g$. - -A non-virtual subclass must implement |getType| which determines -dependency of stack items on symmetries. There are three possible types -for a symmetry. Either the stack item derivative wrt. the symmetry is -a matrix, or a unit matrix, or zero. - -Method |isZero| returns true if the derivative of a given stack item -wrt. to given symmetry is zero as defined by |getType| or the -derivative is not present in the container. In this way, we can -implement the formula conditional some of the tensors are zero, which -is not true (they are only missing). - -Method |createPackedColumn| returns a vector of stack derivatives with -respect to the given symmetry and of the given column, where all zeros -from zero types, or unit matrices are deleted. See {\tt -kron\_prod2.hweb} for explanation. - -@<|StackContainerInterface| class declaration@>= -template @; -class StackContainerInterface { -public:@; - typedef TensorContainer<_Ttype> _Ctype; - typedef enum {@+ matrix, unit, zero@+} itype; -protected:@; - const EquivalenceBundle& ebundle; -public:@; - StackContainerInterface() - : ebundle(*(tls.ebundle))@+ {} - virtual ~StackContainerInterface()@+ {} - virtual const IntSequence& getStackSizes() const =0; - virtual IntSequence& getStackSizes() =0; - virtual const IntSequence& getStackOffsets() const =0; - virtual IntSequence& getStackOffsets() =0; - virtual int numConts() const =0; - virtual const _Ctype* getCont(int i) const =0; - virtual itype getType(int i, const Symmetry& s) const =0; - virtual int numStacks() const =0; - virtual bool isZero(int i, const Symmetry& s) const =0; - virtual const _Ttype* getMatrix(int i, const Symmetry& s) const =0; - virtual int getLengthOfMatrixStacks(const Symmetry& s) const =0; - virtual int getUnitPos(const Symmetry& s) const =0; - virtual Vector* createPackedColumn(const Symmetry& s, - const IntSequence& coor, - int& iu) const =0; - int getAllSize() const - {@+ return getStackOffsets()[numStacks()-1] - + getStackSizes()[numStacks()-1];@+} -}; - -@ Here is |StackContainer|, which implements almost all interface -|StackContainerInterface| but one method |getType| which is left for -implementation to specializations. - -@<|StackContainer| class declaration@>= -template @; -class StackContainer : virtual public StackContainerInterface<_Ttype> { -public:@; - typedef StackContainerInterface<_Ttype> _Stype; - typedef typename StackContainerInterface<_Ttype>::_Ctype _Ctype; - typedef typename StackContainerInterface<_Ttype>::itype itype; -protected:@; - int num_conts; - IntSequence stack_sizes; - IntSequence stack_offsets; - const _Ctype** const conts; -public:@; - StackContainer(int ns, int nc) - : num_conts(nc), stack_sizes(ns, 0), stack_offsets(ns, 0), - conts(new const _Ctype*[nc])@+ {} - virtual ~StackContainer() @+{delete [] conts;} - const IntSequence& getStackSizes() const - {@+ return stack_sizes;@+} - IntSequence& getStackSizes() - {@+ return stack_sizes;@+} - const IntSequence& getStackOffsets() const - {@+ return stack_offsets;@+} - IntSequence& getStackOffsets() - {@+ return stack_offsets;@+} - int numConts() const - {@+ return num_conts;} - const _Ctype* getCont(int i) const - {@+ return conts[i];@+} - virtual itype getType(int i, const Symmetry& s) const =0; - int numStacks() const - {@+ return stack_sizes.size();@+} - @<|StackContainer::isZero| code@>; - @<|StackContainer::getMatrix| code@>; - @<|StackContainer::getLengthOfMatrixStacks| code@>; - @<|StackContainer::getUnitPos| code@>; - @<|StackContainer::createPackedColumn| code@>; -protected:@; - @<|StackContainer::calculateOffsets| code@>; -}; - -@ -@<|StackContainer::isZero| code@>= -bool isZero(int i, const Symmetry& s) const -{ - TL_RAISE_IF(i < 0 || i >= numStacks(), - "Wrong index to stack in StackContainer::isZero."); - return (getType(i, s) == _Stype::zero || - (getType(i, s) == _Stype::matrix && !conts[i]->check(s))); -} - -@ -@<|StackContainer::getMatrix| code@>= -const _Ttype* getMatrix(int i, const Symmetry& s) const -{ - TL_RAISE_IF(isZero(i, s) || getType(i, s) == _Stype::unit, - "Matrix is not returned in StackContainer::getMatrix"); - return conts[i]->get(s); -} - -@ -@<|StackContainer::getLengthOfMatrixStacks| code@>= -int getLengthOfMatrixStacks(const Symmetry& s) const -{ - int res = 0; - int i = 0; - while (i < numStacks() && getType(i, s) == _Stype::matrix) - res += stack_sizes[i++]; - return res; -} - - -@ -@<|StackContainer::getUnitPos| code@>= -int getUnitPos(const Symmetry& s) const -{ - if (s.dimen() != 1) - return -1; - int i = numStacks()-1; - while (i >= 0 && getType(i, s) != _Stype::unit) - i--; - return i; -} - - -@ -@<|StackContainer::createPackedColumn| code@>= -Vector* createPackedColumn(const Symmetry& s, - const IntSequence& coor, int& iu) const -{ - TL_RAISE_IF(s.dimen() != coor.size(), - "Incompatible coordinates for symmetry in StackContainer::createPackedColumn"); - - int len = getLengthOfMatrixStacks(s); - iu = -1; - int i = 0; - if (-1 != (i = getUnitPos(s))) { - iu = stack_offsets[i] + coor[0]; - len++; - } - - Vector* res = new Vector(len); - i = 0; - while (i < numStacks() && getType(i, s) == _Stype::matrix) { - const _Ttype* t = getMatrix(i, s); - Tensor::index ind(t, coor); - Vector subres(*res, stack_offsets[i], stack_sizes[i]); - subres = ConstVector(ConstGeneralMatrix(*t), *ind); - i++; - } - if (iu != -1) - (*res)[len-1] = 1; - - return res; -} - -@ -@<|StackContainer::calculateOffsets| code@>= -void calculateOffsets() -{ - stack_offsets[0] = 0; - for (int i = 1; i < stack_offsets.size(); i++) - stack_offsets[i] = stack_offsets[i-1] + stack_sizes[i-1]; -} - -@ -@<|FoldedStackContainer| class declaration@>= -class WorkerFoldMAADense; -class WorkerFoldMAASparse1; -class WorkerFoldMAASparse2; -class WorkerFoldMAASparse4; -class FoldedStackContainer : virtual public StackContainerInterface { - friend class WorkerFoldMAADense; - friend class WorkerFoldMAASparse1; - friend class WorkerFoldMAASparse2; - friend class WorkerFoldMAASparse4; -public:@; - static double fill_threshold; - void multAndAdd(int dim, const TensorContainer& c , - FGSTensor& out) const - {@+ if (c.check(Symmetry(dim))) multAndAdd(*(c.get(Symmetry(dim))), out);@+} - void multAndAdd(const FSSparseTensor& t, FGSTensor& out) const; - void multAndAdd(int dim, const FGSContainer& c, FGSTensor& out) const; -protected:@; - void multAndAddSparse1(const FSSparseTensor& t, FGSTensor& out) const; - void multAndAddSparse2(const FSSparseTensor& t, FGSTensor& out) const; - void multAndAddSparse3(const FSSparseTensor& t, FGSTensor& out) const; - void multAndAddSparse4(const FSSparseTensor& t, FGSTensor& out) const; - void multAndAddStacks(const IntSequence& fi, const FGSTensor& g, - FGSTensor& out, const void* ad) const; - void multAndAddStacks(const IntSequence& fi, const GSSparseTensor& g, - FGSTensor& out, const void* ad) const; -}; - - -@ -@<|UnfoldedStackContainer| class declaration@>= -class WorkerUnfoldMAADense; -class WorkerUnfoldMAASparse1; -class WorkerUnfoldMAASparse2; -class UnfoldedStackContainer : virtual public StackContainerInterface { - friend class WorkerUnfoldMAADense; - friend class WorkerUnfoldMAASparse1; - friend class WorkerUnfoldMAASparse2; -public:@; - static double fill_threshold; - void multAndAdd(int dim, const TensorContainer& c , - UGSTensor& out) const - {@+ if (c.check(Symmetry(dim))) multAndAdd(*(c.get(Symmetry(dim))), out);@+} - void multAndAdd(const FSSparseTensor& t, UGSTensor& out) const; - void multAndAdd(int dim, const UGSContainer& c, UGSTensor& out) const; -protected:@; - void multAndAddSparse1(const FSSparseTensor& t, UGSTensor& out) const; - void multAndAddSparse2(const FSSparseTensor& t, UGSTensor& out) const; - void multAndAddStacks(const IntSequence& fi, const UGSTensor& g, - UGSTensor& out, const void* ad) const; -}; - -@ Here is the specialization of the |StackContainer|. We implement -here the $z$ needed in SDGE context. We implement |getType| and define -a constructor feeding the data and sizes. - -Note that it has two containers, the first is dependent on four -variables $G(y^*,u,u',\sigma)$, and the second dependent on three -variables $g(y^*,u,\sigma)$. So that we would be able to stack them, -we make the second container $g$ be dependent on four variables, the -third being $u'$ a dummy and always returning zero if dimension of -$u'$ is positive. - -@<|ZContainer| class declaration@>= -template @; -class ZContainer : public StackContainer<_Ttype> { -public:@; - typedef StackContainer<_Ttype> _Tparent; - typedef StackContainerInterface<_Ttype> _Stype; - typedef typename _Tparent::_Ctype _Ctype; - typedef typename _Tparent::itype itype; - ZContainer(const _Ctype* gss, int ngss, const _Ctype* g, int ng, - int ny, int nu) - : _Tparent(4, 2) - { - _Tparent::stack_sizes[0] = ngss; _Tparent::stack_sizes[1] = ng; - _Tparent::stack_sizes[2] = ny; _Tparent::stack_sizes[3] = nu; - _Tparent::conts[0] = gss; - _Tparent::conts[1] = g; - _Tparent::calculateOffsets(); - } - - @<|ZContainer::getType| code@>; -}; - -@ Here we say, what happens if we derive $z$. recall the top of the -file, how $z$ looks, and code is clear. - -@<|ZContainer::getType| code@>= -itype getType(int i, const Symmetry& s) const -{ - if (i == 0) - return _Stype::matrix; - if (i == 1) - if (s[2] > 0) - return _Stype::zero; - else - return _Stype::matrix; - if (i == 2) - if (s == Symmetry(1,0,0,0)) - return _Stype::unit; - else - return _Stype::zero; - if (i == 3) - if (s == Symmetry(0,1,0,0)) - return _Stype::unit; - else - return _Stype::zero; - - TL_RAISE("Wrong stack index in ZContainer::getType"); - return _Stype::zero; -} - -@ -@<|FoldedZContainer| class declaration@>= -class FoldedZContainer : public ZContainer, - public FoldedStackContainer { -public:@; - typedef TensorContainer _Ctype; - FoldedZContainer(const _Ctype* gss, int ngss, const _Ctype* g, int ng, - int ny, int nu) - : ZContainer(gss, ngss, g, ng, ny, nu)@+ {} -}; - -@ -@<|UnfoldedZContainer| class declaration@>= -class UnfoldedZContainer : public ZContainer, - public UnfoldedStackContainer { -public:@; - typedef TensorContainer _Ctype; - UnfoldedZContainer(const _Ctype* gss, int ngss, const _Ctype* g, int ng, - int ny, int nu) - : ZContainer(gss, ngss, g, ng, ny, nu)@+ {} -}; - -@ Here we have another specialization of container used in context of -SDGE. We define a container for -$$G(y,u,u',\sigma)=g^{**}(g^*(y,u,\sigma),u',\sigma)$$ - -For some reason, the symmetry of $g^{**}$ has length $4$ although it -is really dependent on three variables. (To now the reason, consult -|@<|ZContainer| class declaration@>|.) So, it has four stack, the -third one is dummy, and always returns zero. The first stack -corresponds to a container of $g^*$. - -@<|GContainer| class declaration@>= -template @; -class GContainer : public StackContainer<_Ttype> { -public:@; - typedef StackContainer<_Ttype> _Tparent; - typedef StackContainerInterface<_Ttype> _Stype; - typedef typename StackContainer<_Ttype>::_Ctype _Ctype; - typedef typename StackContainer<_Ttype>::itype itype; - GContainer(const _Ctype* gs, int ngs, int nu) - : StackContainer<_Ttype>(4, 1) - { - _Tparent::stack_sizes[0] = ngs; _Tparent::stack_sizes[1] = nu; - _Tparent::stack_sizes[2] = nu; _Tparent::stack_sizes[3] = 1; - _Tparent::conts[0] = gs; - _Tparent::calculateOffsets(); - } - - @<|GContainer::getType| code@>; -}; - -@ Here we define the dependencies in -$g^{**}(g^*(y,u,\sigma),u',\sigma)$. Also note, that first derivative -of $g^*$ wrt $\sigma$ is always zero, so we also add this -information. - -@<|GContainer::getType| code@>= -itype getType(int i, const Symmetry& s) const -{ - if (i == 0) - if (s[2] > 0 || s == Symmetry(0,0,0,1)) - return _Stype::zero; - else - return _Stype::matrix; - if (i == 1) - if (s == Symmetry(0,0,1,0)) - return _Stype::unit; - else - return _Stype::zero; - if (i == 2) - return _Stype::zero; - if (i == 3) - if (s == Symmetry(0,0,0,1)) - return _Stype::unit; - else - return _Stype::zero; - - TL_RAISE("Wrong stack index in GContainer::getType"); - return _Stype::zero; -} - - -@ -@<|FoldedGContainer| class declaration@>= -class FoldedGContainer : public GContainer, - public FoldedStackContainer { -public:@; - typedef TensorContainer _Ctype; - FoldedGContainer(const _Ctype* gs, int ngs, int nu) - : GContainer(gs, ngs, nu)@+ {} -}; - -@ -@<|UnfoldedGContainer| class declaration@>= -class UnfoldedGContainer : public GContainer, - public UnfoldedStackContainer { -public:@; - typedef TensorContainer _Ctype; - UnfoldedGContainer(const _Ctype* gs, int ngs, int nu) - : GContainer(gs, ngs, nu)@+ {} -}; - - -@ Here we have a support class for product of |StackContainer|s. It -only adds a dimension to |StackContainer|. It selects the symmetries -according to equivalence classes passed to the constructor. The -equivalence can have permuted classes by some given -permutation. Nothing else is interesting. - -@<|StackProduct| class declaration@>= -template @; -class StackProduct { -public:@; - typedef StackContainerInterface<_Ttype> _Stype; - typedef typename _Stype::_Ctype _Ctype; - typedef typename _Stype::itype itype; -protected:@; - const _Stype& stack_cont; - InducedSymmetries syms; - Permutation per; -public:@; - StackProduct(const _Stype& sc, const Equivalence& e, - const Symmetry& os) - : stack_cont(sc), syms(e, os), per(e)@+ {} - StackProduct(const _Stype& sc, const Equivalence& e, - const Permutation& p, const Symmetry& os) - : stack_cont(sc), syms(e, p, os), per(e, p)@+ {} - int dimen() const - {@+ return syms.size();@+} - int getAllSize() const - {@+ return stack_cont.getAllSize();@+} - const Symmetry& getProdSym(int ip) const - {@+ return syms[ip];@+} - @<|StackProduct::isZero| code@>; - @<|StackProduct::getType| code@>; - @<|StackProduct::getMatrix| code@>; - @<|StackProduct::createPackedColumns| code@>; - @<|StackProduct::getSize| code@>; - @<|StackProduct::numMatrices| code@>; -}; - -@ -@<|StackProduct::isZero| code@>= -bool isZero(const IntSequence& istacks) const -{ - TL_RAISE_IF(istacks.size() != dimen(), - "Wrong istacks coordinates for StackProduct::isZero"); - - bool res = false; - int i = 0; - while (i < dimen() && !(res = stack_cont.isZero(istacks[i], syms[i]))) - i++; - return res; -} - -@ -@<|StackProduct::getType| code@>= -itype getType(int is, int ip) const -{ - TL_RAISE_IF(is < 0 || is >= stack_cont.numStacks(), - "Wrong index to stack in StackProduct::getType"); - TL_RAISE_IF(ip < 0 || ip >= dimen(), - "Wrong index to stack container in StackProduct::getType"); - return stack_cont.getType(is, syms[ip]); -} - -@ -@<|StackProduct::getMatrix| code@>= -const _Ttype* getMatrix(int is, int ip) const -{ - return stack_cont.getMatrix(is, syms[ip]); -} - -@ -@<|StackProduct::createPackedColumns| code@>= -void createPackedColumns(const IntSequence& coor, - Vector** vs, IntSequence& iu) const -{ - TL_RAISE_IF(iu.size() != dimen(), - "Wrong storage length for unit flags in StackProduct::createPackedColumn"); - TL_RAISE_IF(coor.size() != per.size(), - "Wrong size of index coor in StackProduct::createPackedColumn"); - IntSequence perindex(coor.size()); - per.apply(coor, perindex); - int off = 0; - for (int i = 0; i < dimen(); i++) { - IntSequence percoor(perindex, off, syms[i].dimen() + off); - vs[i] = stack_cont.createPackedColumn(syms[i], percoor, iu[i]); - off += syms[i].dimen(); - } -} - -@ -@<|StackProduct::getSize| code@>= -int getSize(int is) const -{ - return stack_cont.getStackSizes()[is]; -} - - -@ -@<|StackProduct::numMatrices| code@>= -int numMatrices(const IntSequence& istacks) const -{ - TL_RAISE_IF(istacks.size() != dimen(), - "Wrong size of stack coordinates in StackContainer::numMatrices"); - int ret = 0; - int ip = 0; - while (ip < dimen() && getType(istacks[ip], ip) == _Stype::matrix) { - ret++; - ip++; - } - return ret; -} - -@ Here we only inherit from Kronecker product |KronProdAllOptim|, only to -allow for a constructor constructing from |StackProduct|. - -@<|KronProdStack| class declaration@>= -template -class KronProdStack : public KronProdAllOptim { -public:@; - typedef StackProduct<_Ttype> _Ptype; - typedef StackContainerInterface<_Ttype> _Stype; - @<|KronProdStack| constructor code@>; -}; - -@ Here we construct |KronProdAllOptim| from |StackContainer| and given -selections of stack items from stack containers in the product. We -only decide whether to insert matrix, or unit matrix. - -At this point, we do not call |KronProdAllOptim::optimizeOrder|, so -the |KronProdStack| behaves like |KronProdAll| (i.e. no optimization -is done). - -@<|KronProdStack| constructor code@>= -KronProdStack(const _Ptype& sp, const IntSequence& istack) - : KronProdAllOptim(sp.dimen()) -{ - TL_RAISE_IF(sp.dimen() != istack.size(), - "Wrong stack product dimension for KronProdStack constructor"); - - for (int i = 0; i < sp.dimen(); i++) { - TL_RAISE_IF(sp.getType(istack[i], i) == _Stype::zero, - "Attempt to construct KronProdStack from zero matrix"); - if (sp.getType(istack[i], i) == _Stype::unit) - setUnit(i, sp.getSize(istack[i])); - if (sp.getType(istack[i], i) == _Stype::matrix) { - const TwoDMatrix* m = sp.getMatrix(istack[i], i); - TL_RAISE_IF(m->nrows() != sp.getSize(istack[i]), - "Wrong size of returned matrix in KronProdStack constructor"); - setMat(i, *m); - } - } -} - - -@ -@<|WorkerFoldMAADense| class declaration@>= -class WorkerFoldMAADense : public THREAD { - const FoldedStackContainer& cont; - Symmetry sym; - const FGSContainer& dense_cont; - FGSTensor& out; -public:@; - WorkerFoldMAADense(const FoldedStackContainer& container, - const Symmetry& s, - const FGSContainer& dcontainer, - FGSTensor& outten); - void operator()(); -}; - -@ -@<|WorkerFoldMAASparse1| class declaration@>= -class WorkerFoldMAASparse1 : public THREAD { - const FoldedStackContainer& cont; - const FSSparseTensor& t; - FGSTensor& out; - IntSequence coor; - const EquivalenceBundle& ebundle; -public:@; - WorkerFoldMAASparse1(const FoldedStackContainer& container, - const FSSparseTensor& ten, - FGSTensor& outten, const IntSequence& c); - void operator()(); -}; - -@ -@<|WorkerFoldMAASparse2| class declaration@>= -class WorkerFoldMAASparse2 : public THREAD { - const FoldedStackContainer& cont; - const FSSparseTensor& t; - FGSTensor& out; - IntSequence coor; -public:@; - WorkerFoldMAASparse2(const FoldedStackContainer& container, - const FSSparseTensor& ten, - FGSTensor& outten, const IntSequence& c); - void operator()(); -}; - -@ -@<|WorkerFoldMAASparse4| class declaration@>= -class WorkerFoldMAASparse4 : public THREAD { - const FoldedStackContainer& cont; - const FSSparseTensor& t; - FGSTensor& out; - IntSequence coor; -public:@; - WorkerFoldMAASparse4(const FoldedStackContainer& container, - const FSSparseTensor& ten, - FGSTensor& outten, const IntSequence& c); - void operator()(); -}; - -@ -@<|WorkerUnfoldMAADense| class declaration@>= -class WorkerUnfoldMAADense : public THREAD { - const UnfoldedStackContainer& cont; - Symmetry sym; - const UGSContainer& dense_cont; - UGSTensor& out; -public:@; - WorkerUnfoldMAADense(const UnfoldedStackContainer& container, - const Symmetry& s, - const UGSContainer& dcontainer, - UGSTensor& outten); - void operator()(); -}; - -@ -@<|WorkerUnfoldMAASparse1| class declaration@>= -class WorkerUnfoldMAASparse1 : public THREAD { - const UnfoldedStackContainer& cont; - const FSSparseTensor& t; - UGSTensor& out; - IntSequence coor; - const EquivalenceBundle& ebundle; -public:@; - WorkerUnfoldMAASparse1(const UnfoldedStackContainer& container, - const FSSparseTensor& ten, - UGSTensor& outten, const IntSequence& c); - void operator()(); -}; - -@ -@<|WorkerUnfoldMAASparse2| class declaration@>= -class WorkerUnfoldMAASparse2 : public THREAD { - const UnfoldedStackContainer& cont; - const FSSparseTensor& t; - UGSTensor& out; - IntSequence coor; -public:@; - WorkerUnfoldMAASparse2(const UnfoldedStackContainer& container, - const FSSparseTensor& ten, - UGSTensor& outten, const IntSequence& c); - void operator()(); -}; - - -@ End of {\tt stack\_container.h} file. diff --git a/dynare++/tl/cc/sthread.cc b/dynare++/tl/cc/sthread.cc new file mode 100644 index 000000000..9ea13ebe9 --- /dev/null +++ b/dynare++/tl/cc/sthread.cc @@ -0,0 +1,232 @@ +// Copyright 2004, Ondra Kamenik + +/* We set the default values for + |max_parallel_threads| for both |posix| and |empty| implementation and + both joinable and detach group. For |posix| this defaults to + uniprocessor machine with hyper-threading, this is 2. */ + +#include +#include "sthread.hh" + +#ifdef HAVE_PTHREAD +namespace sthread +{ + template<> + int thread_group::max_parallel_threads = 2; + template<> + int detach_thread_group::max_parallel_threads = 2; + + // POSIX specializations methods + void *posix_thread_function(void *c); + template <> + void + thread_traits::run(_Ctype *c) + { + pthread_create(&(c->getThreadIden()), NULL, posix_thread_function, (void *) c); + } + + void *posix_detach_thread_function(void *c); + + template <> + void + thread_traits::detach_run(_Dtype *c) + { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&(c->getThreadIden()), &attr, posix_detach_thread_function, (void *) c); + pthread_attr_destroy(&attr); + } + + template <> + void + thread_traits::exit() + { + pthread_exit(NULL); + } + + template <> + void + thread_traits::join(_Ctype *c) + { + pthread_join(c->getThreadIden(), NULL); + } + + template <> + void + mutex_traits::init(pthread_mutex_t &m) + { + pthread_mutex_init(&m, NULL); + } + + template <> + void + mutex_traits::lock(pthread_mutex_t &m) + { + pthread_mutex_lock(&m); + } + + template <> + void + mutex_traits::unlock(pthread_mutex_t &m) + { + pthread_mutex_unlock(&m); + } + + template <> + void + cond_traits::init(_Tcond &cond) + { + pthread_cond_init(&cond, NULL); + } + + template <> + void + cond_traits::broadcast(_Tcond &cond) + { + pthread_cond_broadcast(&cond); + } + + template <> + void + cond_traits::wait(_Tcond &cond, _Tmutex &mutex) + { + pthread_cond_wait(&cond, &mutex); + } + + template <> + void + cond_traits::destroy(_Tcond &cond) + { + pthread_cond_destroy(&cond); + } + + /* Here we instantiate the static map, and construct |PosixSynchro| + using that map. */ + + static posix_synchro::mutex_map_t posix_mm; + + PosixSynchro::PosixSynchro(const void *c, const char *id) + : posix_synchro(c, id, posix_mm) + { + } + + /* This function is of the type |void* function(void*)| as required by + POSIX, but it typecasts its argument and runs |operator()()|. */ + + void * + posix_thread_function(void *c) + { + thread_traits::_Ctype *ct + = (thread_traits::_Ctype *)c; + try + { + ct->operator()(); + } + catch (...) + { + ct->exit(); + } + return NULL; + } + + void * + posix_detach_thread_function(void *c) + { + thread_traits::_Dtype *ct + = (thread_traits::_Dtype *)c; + condition_counter *counter = ct->counter; + try + { + ct->operator()(); + } + catch (...) + { + ct->exit(); + } + if (counter) + counter->decrease(); + return NULL; + } +} +#else +namespace sthread +{ + template<> + int thread_group::max_parallel_threads = 1; + template<> + int detach_thread_group::max_parallel_threads = 1; + + // non-threading specialization methods + /* The only trait methods we need to work are |thread_traits::run| and + |thread_traits::detach_run|, which directly call + |operator()()|. Anything other is empty. */ + + template <> + void + thread_traits::run(_Ctype *c) + { + c->operator()(); + } + template <> + void + thread_traits::detach_run(_Dtype *c) + { + c->operator()(); + } + + template <> + void + thread_traits::exit() + { + } + + template <> + void + thread_traits::join(_Ctype *c) + { + } + + template <> + void + mutex_traits::init(Empty &m) + { + } + + template <> + void + mutex_traits::lock(Empty &m) + { + } + + template <> + void + mutex_traits::unlock(Empty &m) + { + } + + template <> + void + cond_traits::init(_Tcond &cond) + { + } + + template <> + void + cond_traits::broadcast(_Tcond &cond) + { + } + + template <> + void + cond_traits::wait(_Tcond &cond, _Tmutex &mutex) + { + } + + template <> + void + cond_traits::destroy(_Tcond &cond) + { + } +} +#endif diff --git a/dynare++/tl/cc/sthread.cweb b/dynare++/tl/cc/sthread.cweb deleted file mode 100644 index 44c2311e1..000000000 --- a/dynare++/tl/cc/sthread.cweb +++ /dev/null @@ -1,224 +0,0 @@ -@q $Id: sthread.cweb 2269 2008-11-23 14:33:22Z michel $ @> -@q Copyright 2004, Ondra Kamenik @> - -@ Start of {\tt sthreads.h} file. We set the default values for -|max_parallel_threads| for both |posix| and |empty| implementation and -both joinable and detach group. For |posix| this defaults to -uniprocessor machine with hyper-threading, this is 2. - -@c -#include -#include "sthread.h" - -#ifdef HAVE_PTHREAD -namespace sthread { - template<> - int thread_group::max_parallel_threads = 2; - template<> - int detach_thread_group::max_parallel_threads = 2; - @; -} -#else -namespace sthread { - template<> - int thread_group::max_parallel_threads = 1; - template<> - int detach_thread_group::max_parallel_threads = 1; - @; -} -#endif - -@ -@= - @<|thread_traits| method codes@>; - @<|mutex_traits| method codes@>; - @<|cond_traits| method codes@>; - @<|PosixSynchro| constructor@>; - @<|posix_thread_function| code@>; - @<|posix_detach_thread_function| code@>; - -@ -@<|thread_traits| method codes@>= -void* posix_thread_function(void* c); -template <> -void thread_traits::run(_Ctype* c) -{ - pthread_create(&(c->getThreadIden()), NULL, posix_thread_function, (void*) c); -} -@# -void* posix_detach_thread_function(void* c); - -template <> -void thread_traits::detach_run(_Dtype* c) -{ - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - pthread_create(&(c->getThreadIden()), &attr, posix_detach_thread_function, (void*) c); - pthread_attr_destroy(&attr); -} -@# - -template <> -void thread_traits::exit() -{ - pthread_exit(NULL); -} -@# -template <> -void thread_traits::join(_Ctype* c) -{ - pthread_join(c->getThreadIden(), NULL); -} - -@ -@<|mutex_traits| method codes@>= -template <> -void mutex_traits::init(pthread_mutex_t& m) -{ - pthread_mutex_init(&m, NULL); -} -@# -template <> -void mutex_traits::lock(pthread_mutex_t& m) -{ - pthread_mutex_lock(&m); -} -@# -template <> -void mutex_traits::unlock(pthread_mutex_t& m) -{ - pthread_mutex_unlock(&m); -} - -@ -@<|cond_traits| method codes@>= -template <> -void cond_traits::init(_Tcond& cond) -{ - pthread_cond_init(&cond, NULL); -} -@# -template <> -void cond_traits::broadcast(_Tcond& cond) -{ - pthread_cond_broadcast(&cond); -} -@# -template <> -void cond_traits::wait(_Tcond& cond, _Tmutex& mutex) -{ - pthread_cond_wait(&cond, &mutex); -} -@# -template <> -void cond_traits::destroy(_Tcond& cond) -{ - pthread_cond_destroy(&cond); -} - - -@ Here we instantiate the static map, and construct |PosixSynchro| -using that map. - -@<|PosixSynchro| constructor@>= -static posix_synchro::mutex_map_t posix_mm; - -PosixSynchro::PosixSynchro(const void* c, const char* id) - : posix_synchro(c, id, posix_mm) {} - -@ This function is of the type |void* function(void*)| as required by -POSIX, but it typecasts its argument and runs |operator()()|. -@<|posix_thread_function| code@>= -void* posix_thread_function(void* c) -{ - thread_traits::_Ctype* ct = - (thread_traits::_Ctype*)c; - try { - ct->operator()(); - } catch (...) { - ct->exit(); - } - return NULL; -} - -@ -@<|posix_detach_thread_function| code@>= -void* posix_detach_thread_function(void* c) -{ - thread_traits::_Dtype* ct = - (thread_traits::_Dtype*)c; - condition_counter* counter = ct->counter; - try { - ct->operator()(); - } catch (...) { - ct->exit(); - } - if (counter) - counter->decrease(); - return NULL; -} - - -@ The only trait methods we need to work are |thread_traits::run| and -|thread_traits::detach_run|, which directly call -|operator()()|. Anything other is empty. - -@= -template <> -void thread_traits::run(_Ctype* c) -{ - c->operator()(); -} -template <> -void thread_traits::detach_run(_Dtype* c) -{ - c->operator()(); -} -@# -template <> -void thread_traits::exit() -{ -} -@# -template <> -void thread_traits::join(_Ctype* c) -{ -} -@# -template <> -void mutex_traits::init(Empty& m) -{ -} -@# -template <> -void mutex_traits::lock(Empty& m) -{ -} -@# -template <> -void mutex_traits::unlock(Empty& m) -{ -} -@# -template <> -void cond_traits::init(_Tcond& cond) -{ -} -@# -template <> -void cond_traits::broadcast(_Tcond& cond) -{ -} -@# -template <> -void cond_traits::wait(_Tcond& cond, _Tmutex& mutex) -{ -} -@# -template <> -void cond_traits::destroy(_Tcond& cond) -{ -} - -@ End of {\tt sthreads.h} file. diff --git a/dynare++/tl/cc/sthread.hh b/dynare++/tl/cc/sthread.hh new file mode 100644 index 000000000..ae9062734 --- /dev/null +++ b/dynare++/tl/cc/sthread.hh @@ -0,0 +1,627 @@ +// Copyright 2004, Ondra Kamenik + +// Simple threads. + +/* This file defines types making a simple interface to + multi-threading. It follows the classical C++ idioms for traits. We + have three sorts of traits. The first is a |thread_traits|, which make + interface to thread functions (run, exit, create and join), the second + is |mutex_traits|, which make interface to mutexes (create, lock, + unlock), and third is |cond_traits|, which make interface to + conditions (create, wait, broadcast, and destroy). At present, there + are two implementations. The first are POSIX threads, mutexes, and + conditions, the second is serial (no parallelization). + + The file provides the following interfaces templated by the types + implementing the threading (like types |pthread_t|, and |pthread_mutex_t| + for POSIX thread and mutex): + \unorderedlist + \li |thread| is a pure virtual class, which must be inherited and a + method |operator()()| be implemented as the running code of the + thread. This code is run as a new thread by calling |run| method. + \li |thread_group| allows insertion of |thread|s and running all of + them simultaneously joining them. The number of maximum parallel + threads can be controlled. See below. + \li |synchro| object locks a piece of code to be executed only serially + for a given data and specified entry-point. It locks the code until it + is destructed. So, the typical use is to create the |synchro| object + on the stack of a function which is to be synchronized. The + synchronization can be subjected to specific data (then a pointer can + be passed to |synchro|'s constructor), and can be subjected to + specific entry-point (then |const char*| is passed to the + constructor). + \li |detach_thread| inherits from |thread| and models a detached + thread in contrast to |thread| which models the joinable thread. + \li |detach_thread_group| groups the detached threads and runs them. They + are not joined, they are synchronized by means of a counter counting + running threads. A change of the counter is checked by waiting on an + associated condition. + \endunorderedlist + + What implementation is selected is governed (at present) by + |HAVE_PTHREAD|. If it is defined, then POSIX threads are linked. If + it is not defined, then serial implementation is taken. In accordance + with this, the header file defines macros |THREAD|, |THREAD_GROUP|, + and |SYNCHRO| as the picked specialization of |thread| (or |detach_thread|), + |thread_group| (or |detach_thread_group|), and |synchro|. + + The type of implementation is controlled by |thread_impl| integer + template parameter, this can be |posix| or |empty|. + + The number of maximum parallel threads is controlled via a static + member of |thread_group| and |detach_thread_group| classes. */ + +#ifndef STHREAD_H +#define STHREAD_H + +#ifdef HAVE_PTHREAD +# include +#else +/* Give valid types for POSIX thread types, otherwise the templates fail in empty mode. + Don't use typedefs because on some systems |pthread_t| and friends are typedefs even + without the include. */ +# define pthread_t void * +# define pthread_mutex_t void * +# define pthread_cond_t void * +#endif + +#include +#include +#include + +namespace sthread +{ + using namespace std; + + class Empty + { + }; + // classical IF template + /* Here is the classical IF template. */ + template + struct IF + { + typedef Then RET; + }; + + template + struct IF + { + typedef Else RET; + }; + + enum { posix, empty}; + template + class thread_traits; + template + class detach_thread; + + /* The class of |thread| is clear. The user implements |operator()()|, + the method |run| runs the user's code as joinable thread, |exit| kills the + execution. */ + template + class thread + { + typedef thread_traits _Ttraits; + typedef typename _Ttraits::_Tthread _Tthread; + _Tthread th; + public: + virtual ~thread() + { + } + _Tthread & + getThreadIden() + { + return th; + } + const _Tthread & + getThreadIden() const + { + return th; + } + virtual void operator()() = 0; + void + run() + { + _Ttraits::run(this); + } + void + detach_run() + { + _Ttraits::detach_run(this); + } + void + exit() + { + _Ttraits::exit(); + } + }; + + /* The |thread_group| is also clear. We allow a user to insert the + |thread|s, and then launch |run|, which will run all the threads not + allowing more than |max_parallel_threads| joining them at the + end. This static member can be set from outside. */ + + template + class thread_group + { + typedef thread_traits _Ttraits; + typedef thread _Ctype; + list<_Ctype *> tlist; + typedef typename list<_Ctype *>::iterator iterator; + public: + static int max_parallel_threads; + void + insert(_Ctype *c) + { + tlist.push_back(c); + } + /* The thread group class maintains list of pointers to threads. It + takes responsibility of deallocating the threads. So we implement the + destructor. */ + ~thread_group() + { + while (!tlist.empty()) + { + delete tlist.front(); + tlist.pop_front(); + } + } + /* Here we run the threads ensuring that not more than + |max_parallel_threads| are run in parallel. More over, we do not want + to run a too low number of threads, since it is wasting with resource + (if there are). Therefore, we run in parallel |max_parallel_threads| + batches as long as the remaining threads are greater than the double + number. And then the remaining batch (less than |2*max_parallel_threads|) + is run half by half. */ + + void + run() + { + int rem = tlist.size(); + iterator pfirst = tlist.begin(); + while (rem > 2*max_parallel_threads) + { + pfirst = run_portion(pfirst, max_parallel_threads); + rem -= max_parallel_threads; + } + if (rem > max_parallel_threads) + { + pfirst = run_portion(pfirst, rem/2); + rem -= rem/2; + } + run_portion(pfirst, rem); + } + + private: + /* This runs a given number of threads in parallel starting from the + given iterator. It returns the first iterator not run. */ + + iterator + run_portion(iterator start, int n) + { + int c = 0; + for (iterator i = start; c < n; ++i, c++) + { + (*i)->run(); + } + iterator ret; + c = 0; + for (ret = start; c < n; ++ret, c++) + { + _Ttraits::join(*ret); + } + return ret; + } + }; + + /* Clear. We have only |run|, |detach_run|, |exit| and |join|, since + this is only a simple interface. */ + + template + struct thread_traits + { + typedef typename IF::RET _Tthread; + typedef thread _Ctype; + typedef detach_thread _Dtype; + static void run(_Ctype *c); + static void detach_run(_Dtype *c); + static void exit(); + static void join(_Ctype *c); + }; + + /* Clear. We have only |init|, |lock|, and |unlock|. */ + struct ltmmkey; + typedef pair mmkey; + + template + struct mutex_traits + { + typedef typename IF::RET _Tmutex; + typedef map, ltmmkey> mutex_int_map; + static void init(_Tmutex &m); + static void lock(_Tmutex &m); + static void unlock(_Tmutex &m); + }; + + /* Here we define a map of mutexes keyed by a pair of address, and a + string. A purpose of the map of mutexes is that, if synchronizing, we + need to publish mutexes locking some piece of codes (characterized by + the string) accessing the data (characterized by the pointer). So, if + any thread needs to pass a |synchro| object, it creates its own with + the same address and string, and must look to some public storage to + unlock the mutex. If the |synchro| object is created for the first + time, the mutex is created and inserted to the map. We count the + references to the mutex (number of waiting threads) to know, when it + is save to remove the mutex from the map. This is the only purpose of + counting the references. Recall, that the mutex is keyed by an address + of the data, and without removing, the number of mutexes would only + grow. + + The map itself needs its own mutex to avoid concurrent insertions and + deletions. */ + + struct ltmmkey + { + bool + operator()(const mmkey &k1, const mmkey &k2) const + { + return k1.first < k2.first + || (k1.first == k2.first && strcmp(k1.second, k2.second) < 0); + } + }; + + template + class mutex_map : + public mutex_traits::mutex_int_map + { + typedef typename mutex_traits::_Tmutex _Tmutex; + typedef mutex_traits _Mtraits; + typedef pair<_Tmutex, int> mmval; + typedef map _Tparent; + typedef typename _Tparent::iterator iterator; + typedef typename _Tparent::value_type _mvtype; + _Tmutex m; + public: + mutex_map() + { + _Mtraits::init(m); + } + void + insert(const void *c, const char *id, const _Tmutex &m) + { + _Tparent::insert(_mvtype(mmkey(c, id), mmval(m, 0))); + } + bool + check(const void *c, const char *id) const + { + return _Tparent::find(mmkey(c, id)) != _Tparent::end(); + } + /* This returns a pointer to the pair of mutex and count reference number. */ + mmval * + get(const void *c, const char *id) + { + iterator it = _Tparent::find(mmkey(c, id)); + if (it == _Tparent::end()) + return NULL; + return &((*it).second); + } + + /* This removes unconditionally the mutex from the map regardless its + number of references. The only user of this class should be |synchro| + class, it implementation must not remove referenced mutex. */ + + void + remove(const void *c, const char *id) + { + iterator it = _Tparent::find(mmkey(c, id)); + if (it != _Tparent::end()) + this->erase(it); + } + void + lock_map() + { + _Mtraits::lock(m); + } + void + unlock_map() + { + _Mtraits::unlock(m); + } + + }; + + /* This is the |synchro| class. The constructor of this class tries to + lock a mutex for a particular address (identification of data) and + string (identification of entry-point). If the mutex is already + locked, it waits until it is unlocked and then returns. The destructor + releases the lock. The typical use is to construct the object on the + stacked of the code being synchronized. */ + + template + class synchro + { + typedef typename mutex_traits::_Tmutex _Tmutex; + typedef mutex_traits _Mtraits; + public: + typedef mutex_map mutex_map_t; + private: + const void *caller; + const char *iden; + mutex_map_t &mutmap; + public: + synchro(const void *c, const char *id, mutex_map_t &mmap) + : caller(c), iden(id), mutmap(mmap) + { + lock(); + } + ~synchro() + { + unlock(); + } + private: + /* The |lock| function acquires the mutex in the map. First it tries to + get an exclusive access to the map. Then it increases a number of + references of the mutex (if it does not exists, it inserts it). Then + unlocks the map, and finally tries to lock the mutex of the map. */ + + void + lock() + { + mutmap.lock_map(); + if (!mutmap.check(caller, iden)) + { + _Tmutex mut; + _Mtraits::init(mut); + mutmap.insert(caller, iden, mut); + } + mutmap.get(caller, iden)->second++; + mutmap.unlock_map(); + _Mtraits::lock(mutmap.get(caller, iden)->first); + } + + /* The |unlock| function first locks the map. Then releases the lock, + and decreases a number of references. If it is zero, it removes the + mutex. */ + + void + unlock() + { + mutmap.lock_map(); + if (mutmap.check(caller, iden)) + { + _Mtraits::unlock(mutmap.get(caller, iden)->first); + mutmap.get(caller, iden)->second--; + if (mutmap.get(caller, iden)->second == 0) + mutmap.remove(caller, iden); + } + mutmap.unlock_map(); + } + }; + + /* These are traits for conditions. We need |init|, |broadcast|, |wait| + and |destroy|. */ + + template + struct cond_traits + { + typedef typename IF::RET _Tcond; + typedef typename mutex_traits::_Tmutex _Tmutex; + static void init(_Tcond &cond); + static void broadcast(_Tcond &cond); + static void wait(_Tcond &cond, _Tmutex &mutex); + static void destroy(_Tcond &cond); + }; + + /* Here is the condition counter. It is a counter which starts at 0, + and can be increased and decreased. A thread can wait until the + counter is changed, this is implemented by condition. After the wait + is done, another (or the same) thread, by calling |waitForChange| + waits for another change. This can be dangerous, since it is possible + to wait for a change which will not happen, because all the threads + which can cause the change (by increase of decrease) might had + finished. */ + + template + class condition_counter + { + typedef typename mutex_traits::_Tmutex _Tmutex; + typedef typename cond_traits::_Tcond _Tcond; + int counter; + _Tmutex mut; + _Tcond cond; + bool changed; + public: + /* We initialize the counter to 0, and |changed| flag to |true|, since + the counter was change from undefined value to 0. */ + + condition_counter() + : counter(0), changed(true) + { + mutex_traits::init(mut); + cond_traits::init(cond); + } + + /* In destructor, we only release the resources associated with the + condition. */ + + ~condition_counter() + { + cond_traits::destroy(cond); + } + + /* When increasing, we lock the mutex, advance the counter, remember it + is changed, broadcast, and release the mutex. */ + + void + increase() + { + mutex_traits::lock(mut); + counter++; + changed = true; + cond_traits::broadcast(cond); + mutex_traits::unlock(mut); + } + + /* Same as increase. */ + void + decrease() + { + mutex_traits::lock(mut); + counter--; + changed = true; + cond_traits::broadcast(cond); + mutex_traits::unlock(mut); + } + + /* We lock the mutex, and if there was a change since the last call of + |waitForChange|, we return immediately, otherwise we wait for the + change. The mutex is released. */ + + int + waitForChange() + { + mutex_traits::lock(mut); + if (!changed) + { + cond_traits::wait(cond, mut); + } + changed = false; + int res = counter; + mutex_traits::unlock(mut); + return res; + } + }; + + /* The detached thread is the same as joinable |thread|. We only + re-implement |run| method to call |thread_traits::detach_run|, and add + a method which installs a counter. The counter is increased and + decreased on the body of the new thread. */ + + template + class detach_thread : public thread + { + public: + condition_counter *counter; + detach_thread() : counter(NULL) + { + } + void + installCounter(condition_counter *c) + { + counter = c; + } + void + run() + { + thread_traits::detach_run(this); + } + }; + + /* The detach thread group is (by interface) the same as + |thread_group|. The extra thing we have here is the |counter|. The + implementation of |insert| and |run| is different. */ + + template + class detach_thread_group + { + typedef thread_traits _Ttraits; + typedef cond_traits _Ctraits; + typedef detach_thread _Ctype; + list<_Ctype *> tlist; + typedef typename list<_Ctype *>::iterator iterator; + condition_counter counter; + public: + static int max_parallel_threads; + + /* When inserting, the counter is installed to the thread. */ + void + insert(_Ctype *c) + { + tlist.push_back(c); + c->installCounter(&counter); + } + + /* The destructor is clear. */ + ~detach_thread_group() + { + while (!tlist.empty()) + { + delete tlist.front(); + tlist.pop_front(); + } + } + + /* We cycle through all threads in the group, and in each cycle we wait + for the change in the |counter|. If the counter indicates less than + maximum parallel threads running, then a new thread is run, and the + iterator in the list is moved. + + At the end we have to wait for all thread to finish. */ + + void + run() + { + int mpt = max_parallel_threads; + iterator it = tlist.begin(); + while (it != tlist.end()) + { + if (counter.waitForChange() < mpt) + { + counter.increase(); + (*it)->run(); + ++it; + } + } + while (counter.waitForChange() > 0) + { + } + } + }; + +#ifdef HAVE_PTHREAD + // POSIX thread specializations + /* Here we only define the specializations for POSIX threads. Then we + define the macros. Note that the |PosixSynchro| class construct itself + from the static map defined in {\tt sthreads.cpp}. */ + typedef detach_thread PosixThread; + typedef detach_thread_group PosixThreadGroup; + typedef synchro posix_synchro; + class PosixSynchro : public posix_synchro + { + public: + PosixSynchro(const void *c, const char *id); + }; + +# define THREAD sthread::PosixThread +# define THREAD_GROUP sthread::PosixThreadGroup +# define SYNCHRO sthread::PosixSynchro + +#else + // No threading specializations@>= + /* Here we define an empty class and use it as thread and + mutex. |NoSynchro| class is also empty, but an empty constructor is + declared. The empty destructor is declared only to avoid ``unused + variable warning''. */ + typedef thread NoThread; + typedef thread_group NoThreadGroup; + typedef synchro no_synchro; + class NoSynchro + { + public: + NoSynchro(const void *c, const char *id) + { + } + ~NoSynchro() + { + } + }; + +# define THREAD sthread::NoThread +# define THREAD_GROUP sthread::NoThreadGroup +# define SYNCHRO sthread::NoSynchro + +#endif +}; + +#endif diff --git a/dynare++/tl/cc/sthread.hweb b/dynare++/tl/cc/sthread.hweb deleted file mode 100644 index a8331ca5e..000000000 --- a/dynare++/tl/cc/sthread.hweb +++ /dev/null @@ -1,625 +0,0 @@ -@q $Id: sthread.hweb 411 2005-08-11 12:26:13Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@*2 Simple threads. Start of {\tt sthreads.h} file. - -This file defines types making a simple interface to -multi-threading. It follows the classical C++ idioms for traits. We -have three sorts of traits. The first is a |thread_traits|, which make -interface to thread functions (run, exit, create and join), the second -is |mutex_traits|, which make interface to mutexes (create, lock, -unlock), and third is |cond_traits|, which make interface to -conditions (create, wait, broadcast, and destroy). At present, there -are two implementations. The first are POSIX threads, mutexes, and -conditions, the second is serial (no parallelization). - -The file provides the following interfaces templated by the types -implementing the threading (like types |pthread_t|, and |pthread_mutex_t| -for POSIX thread and mutex): -\unorderedlist -\li |thread| is a pure virtual class, which must be inherited and a -method |operator()()| be implemented as the running code of the -thread. This code is run as a new thread by calling |run| method. -\li |thread_group| allows insertion of |thread|s and running all of -them simultaneously joining them. The number of maximum parallel -threads can be controlled. See below. -\li |synchro| object locks a piece of code to be executed only serially -for a given data and specified entry-point. It locks the code until it -is destructed. So, the typical use is to create the |synchro| object -on the stack of a function which is to be synchronized. The -synchronization can be subjected to specific data (then a pointer can -be passed to |synchro|'s constructor), and can be subjected to -specific entry-point (then |const char*| is passed to the -constructor). -\li |detach_thread| inherits from |thread| and models a detached -thread in contrast to |thread| which models the joinable thread. -\li |detach_thread_group| groups the detached threads and runs them. They -are not joined, they are synchronized by means of a counter counting -running threads. A change of the counter is checked by waiting on an -associated condition. -\endunorderedlist - -What implementation is selected is governed (at present) by -|HAVE_PTHREAD|. If it is defined, then POSIX threads are linked. If -it is not defined, then serial implementation is taken. In accordance -with this, the header file defines macros |THREAD|, |THREAD_GROUP|, -and |SYNCHRO| as the picked specialization of |thread| (or |detach_thread|), -|thread_group| (or |detach_thread_group|), and |synchro|. - -The type of implementation is controlled by |thread_impl| integer -template parameter, this can be |posix| or |empty|. - -The number of maximum parallel threads is controlled via a static -member of |thread_group| and |detach_thread_group| classes. - -@s _Tthread int -@s thread_traits int -@s thread int -@s thread_group int -@s detach_thread int -@s detach_thread_group int -@s cond_traits int -@s condition_counter int -@s mutex_traits int -@s mutex_map int -@s synchro int -@s _Tmutex int -@s pthread_t int -@s pthread_mutex_t int -@s pthread_cond_t int -@s pthread_attr_t int -@s IF int -@s Then int -@s Else int -@s RET int -@s thread_impl int - -@c -#ifndef STHREAD_H -#define STHREAD_H - -#ifdef HAVE_PTHREAD -# include -#else -/* Give valid types for POSIX thread types, otherwise the templates fail in empty mode. - Don't use typedefs because on some systems |pthread_t| and friends are typedefs even - without the include. */ -# define pthread_t void * -# define pthread_mutex_t void * -# define pthread_cond_t void * -#endif - -#include -#include -#include - -namespace sthread { - using namespace std; - - class Empty {}; - @; - enum {@+ posix, empty@+}; - template class thread_traits; - template class detach_thread; - @<|thread| template class declaration@>; - @<|thread_group| template class declaration@>; - @<|thread_traits| template class declaration@>; - @<|mutex_traits| template class declaration@>; - @<|mutex_map| template class declaration@>; - @<|synchro| template class declaration@>; - @<|cond_traits| template class declaration@>; - @<|condition_counter| template class declaration@>; - @<|detach_thread| template class declaration@>; - @<|detach_thread_group| template class declaration@>; -#ifdef HAVE_PTHREAD - @; -#else - @; -#endif -}; - -#endif - -@ Here is the classical IF template. -@= -template -struct IF { - typedef Then RET; -}; - -template -struct IF { - typedef Else RET; -}; - - - -@ The class of |thread| is clear. The user implements |operator()()|, -the method |run| runs the user's code as joinable thread, |exit| kills the -execution. - -@<|thread| template class declaration@>= -template -class thread { - typedef thread_traits _Ttraits; - typedef typename _Ttraits::_Tthread _Tthread; - _Tthread th; -public:@; - virtual ~thread() {} - _Tthread& getThreadIden() - {@+ return th;@+} - const _Tthread& getThreadIden() const - {@+ return th;@+} - virtual void operator()() = 0; - void run() - {@+ _Ttraits::run(this);@+} - void detach_run() - {@+ _Ttraits::detach_run(this);@+} - void exit() - {@+ _Ttraits::exit();@+} -}; - -@ The |thread_group| is also clear. We allow a user to insert the -|thread|s, and then launch |run|, which will run all the threads not -allowing more than |max_parallel_threads| joining them at the -end. This static member can be set from outside. - -@<|thread_group| template class declaration@>= -template -class thread_group { - typedef thread_traits _Ttraits; - typedef thread _Ctype; - list<_Ctype*> tlist; - typedef typename list<_Ctype*>::iterator iterator; -public:@; - static int max_parallel_threads; - void insert(_Ctype* c) - {@+ tlist.push_back(c);@+} - @<|thread_group| destructor code@>; - @<|thread_group::run| code@>; -private:@; - @<|thread_group::run_portion| code@>; -}; - -@ The thread group class maintains list of pointers to threads. It -takes responsibility of deallocating the threads. So we implement the -destructor. -@<|thread_group| destructor code@>= -~thread_group() -{ - while (! tlist.empty()) { - delete tlist.front(); - tlist.pop_front(); - } -} - -@ This runs a given number of threads in parallel starting from the -given iterator. It returns the first iterator not run. - -@<|thread_group::run_portion| code@>= -iterator run_portion(iterator start, int n) -{ - int c = 0; - for (iterator i = start; c < n; ++i, c++) { - (*i)->run(); - } - iterator ret; - c = 0; - for (ret = start; c < n; ++ret, c++) { - _Ttraits::join(*ret); - } - return ret; -} - - -@ Here we run the threads ensuring that not more than -|max_parallel_threads| are run in parallel. More over, we do not want -to run a too low number of threads, since it is wasting with resource -(if there are). Therefore, we run in parallel |max_parallel_threads| -batches as long as the remaining threads are greater than the double -number. And then the remaining batch (less than |2*max_parallel_threads|) -is run half by half. - -@<|thread_group::run| code@>= -void run() -{ - int rem = tlist.size(); - iterator pfirst = tlist.begin(); - while (rem > 2*max_parallel_threads) { - pfirst = run_portion(pfirst, max_parallel_threads); - rem -= max_parallel_threads; - } - if (rem > max_parallel_threads) { - pfirst = run_portion(pfirst, rem/2); - rem -= rem/2; - } - run_portion(pfirst, rem); -} - - - - -@ Clear. We have only |run|, |detach_run|, |exit| and |join|, since -this is only a simple interface. - -@<|thread_traits| template class declaration@>= -template -struct thread_traits { - typedef typename IF::RET _Tthread; - typedef thread _Ctype; - typedef detach_thread _Dtype; - static void run(_Ctype* c); - static void detach_run(_Dtype* c); - static void exit(); - static void join(_Ctype* c); -}; - -@ Clear. We have only |init|, |lock|, and |unlock|. -@<|mutex_traits| template class declaration@>= -struct ltmmkey; -typedef pair mmkey; -@# -template -struct mutex_traits { - typedef typename IF::RET _Tmutex; - typedef map, ltmmkey> mutex_int_map; - static void init(_Tmutex& m); - static void lock(_Tmutex& m); - static void unlock(_Tmutex& m); -}; - -@ Here we define a map of mutexes keyed by a pair of address, and a -string. A purpose of the map of mutexes is that, if synchronizing, we -need to publish mutexes locking some piece of codes (characterized by -the string) accessing the data (characterized by the pointer). So, if -any thread needs to pass a |synchro| object, it creates its own with -the same address and string, and must look to some public storage to -unlock the mutex. If the |synchro| object is created for the first -time, the mutex is created and inserted to the map. We count the -references to the mutex (number of waiting threads) to know, when it -is save to remove the mutex from the map. This is the only purpose of -counting the references. Recall, that the mutex is keyed by an address -of the data, and without removing, the number of mutexes would only -grow. - -The map itself needs its own mutex to avoid concurrent insertions and -deletions. - -@s mutex_int_map int - -@<|mutex_map| template class declaration@>= -struct ltmmkey { - bool operator()(const mmkey& k1, const mmkey& k2) const - {return k1.first < k2.first || - (k1.first == k2.first && strcmp(k1.second, k2.second) < 0);} -}; -@# -template -class mutex_map - : public mutex_traits::mutex_int_map -{ - typedef typename mutex_traits::_Tmutex _Tmutex; - typedef mutex_traits _Mtraits; - typedef pair<_Tmutex, int> mmval; - typedef map _Tparent; - typedef typename _Tparent::iterator iterator; - typedef typename _Tparent::value_type _mvtype; - _Tmutex m; -public:@; - mutex_map() - {@+ _Mtraits::init(m);@+} - void insert(const void* c, const char* id, const _Tmutex& m) - {@+ _Tparent::insert(_mvtype(mmkey(c,id), mmval(m,0)));@+} - bool check(const void* c, const char* id) const - {@+ return _Tparent::find(mmkey(c, id)) != _Tparent::end();@+} - @<|mutex_map::get| code@>; - @<|mutex_map::remove| code@>; - void lock_map() - {@+ _Mtraits::lock(m);@+} - void unlock_map() - {@+ _Mtraits::unlock(m);@+} - -}; - -@ This returns a pointer to the pair of mutex and count reference number. -@<|mutex_map::get| code@>= -mmval* get(const void* c, const char* id) -{ - iterator it = _Tparent::find(mmkey(c, id)); - if (it == _Tparent::end()) - return NULL; - return &((*it).second); -} - -@ This removes unconditionally the mutex from the map regardless its -number of references. The only user of this class should be |synchro| -class, it implementation must not remove referenced mutex. - -@<|mutex_map::remove| code@>= -void remove(const void* c, const char* id) -{ - iterator it = _Tparent::find(mmkey(c, id)); - if (it != _Tparent::end()) - this->erase(it); -} - -@ This is the |synchro| class. The constructor of this class tries to -lock a mutex for a particular address (identification of data) and -string (identification of entry-point). If the mutex is already -locked, it waits until it is unlocked and then returns. The destructor -releases the lock. The typical use is to construct the object on the -stacked of the code being synchronized. - -@<|synchro| template class declaration@>= -template -class synchro { - typedef typename mutex_traits::_Tmutex _Tmutex; - typedef mutex_traits _Mtraits; -public:@; - typedef mutex_map mutex_map_t; -private:@; - const void* caller; - const char* iden; - mutex_map_t& mutmap; -public:@; - synchro(const void* c, const char* id, mutex_map_t& mmap) - : caller(c), iden(id), mutmap(mmap) - {@+ lock();@+} - ~synchro() - {@+ unlock();@+} -private:@; - @<|synchro::lock| code@>; - @<|synchro::unlock| code@>; -}; - -@ The |lock| function acquires the mutex in the map. First it tries to -get an exclusive access to the map. Then it increases a number of -references of the mutex (if it does not exists, it inserts it). Then -unlocks the map, and finally tries to lock the mutex of the map. - -@<|synchro::lock| code@>= -void lock() { - mutmap.lock_map(); - if (!mutmap.check(caller, iden)) { - _Tmutex mut; - _Mtraits::init(mut); - mutmap.insert(caller, iden, mut); - } - mutmap.get(caller, iden)->second++; - mutmap.unlock_map(); - _Mtraits::lock(mutmap.get(caller, iden)->first); -} - -@ The |unlock| function first locks the map. Then releases the lock, -and decreases a number of references. If it is zero, it removes the -mutex. - -@<|synchro::unlock| code@>= -void unlock() { - mutmap.lock_map(); - if (mutmap.check(caller, iden)) { - _Mtraits::unlock(mutmap.get(caller, iden)->first); - mutmap.get(caller, iden)->second--; - if (mutmap.get(caller, iden)->second == 0) - mutmap.remove(caller, iden); - } - mutmap.unlock_map(); -} - -@ These are traits for conditions. We need |init|, |broadcast|, |wait| -and |destroy|. - -@<|cond_traits| template class declaration@>= -template -struct cond_traits { - typedef typename IF::RET _Tcond; - typedef typename mutex_traits::_Tmutex _Tmutex; - static void init(_Tcond& cond); - static void broadcast(_Tcond& cond); - static void wait(_Tcond& cond, _Tmutex& mutex); - static void destroy(_Tcond& cond); -}; - -@ Here is the condition counter. It is a counter which starts at 0, -and can be increased and decreased. A thread can wait until the -counter is changed, this is implemented by condition. After the wait -is done, another (or the same) thread, by calling |waitForChange| -waits for another change. This can be dangerous, since it is possible -to wait for a change which will not happen, because all the threads -which can cause the change (by increase of decrease) might had -finished. - -@<|condition_counter| template class declaration@>= -template -class condition_counter { - typedef typename mutex_traits::_Tmutex _Tmutex; - typedef typename cond_traits::_Tcond _Tcond; - int counter; - _Tmutex mut; - _Tcond cond; - bool changed; -public:@; - @<|condition_counter| constructor code@>; - @<|condition_counter| destructor code@>; - @<|condition_counter::increase| code@>; - @<|condition_counter::decrease| code@>; - @<|condition_counter::waitForChange| code@>; -}; - -@ We initialize the counter to 0, and |changed| flag to |true|, since -the counter was change from undefined value to 0. - -@<|condition_counter| constructor code@>= -condition_counter() - : counter(0), changed(true) -{ - mutex_traits::init(mut); - cond_traits::init(cond); -} - -@ In destructor, we only release the resources associated with the -condition. - -@<|condition_counter| destructor code@>= -~condition_counter() -{ - cond_traits::destroy(cond); -} - -@ When increasing, we lock the mutex, advance the counter, remember it -is changed, broadcast, and release the mutex. - -@<|condition_counter::increase| code@>= -void increase() -{ - mutex_traits::lock(mut); - counter++; - changed = true; - cond_traits::broadcast(cond); - mutex_traits::unlock(mut); -} - -@ Same as increase. -@<|condition_counter::decrease| code@>= -void decrease() -{ - mutex_traits::lock(mut); - counter--; - changed = true; - cond_traits::broadcast(cond); - mutex_traits::unlock(mut); -} - -@ We lock the mutex, and if there was a change since the last call of -|waitForChange|, we return immediately, otherwise we wait for the -change. The mutex is released. - -@<|condition_counter::waitForChange| code@>= -int waitForChange() -{ - mutex_traits::lock(mut); - if (!changed) { - cond_traits::wait(cond, mut); - } - changed = false; - int res = counter; - mutex_traits::unlock(mut); - return res; -} - - -@ The detached thread is the same as joinable |thread|. We only -re-implement |run| method to call |thread_traits::detach_run|, and add -a method which installs a counter. The counter is increased and -decreased on the body of the new thread. - -@<|detach_thread| template class declaration@>= -template -class detach_thread : public thread { -public:@; - condition_counter* counter; - detach_thread() : counter(NULL) {} - void installCounter(condition_counter* c) - {@+ counter = c;@+} - void run() - {@+thread_traits::detach_run(this);@+} -}; - -@ The detach thread group is (by interface) the same as -|thread_group|. The extra thing we have here is the |counter|. The -implementation of |insert| and |run| is different. - -@<|detach_thread_group| template class declaration@>= -template -class detach_thread_group { - typedef thread_traits _Ttraits; - typedef cond_traits _Ctraits; - typedef detach_thread _Ctype; - list<_Ctype *> tlist; - typedef typename list<_Ctype*>::iterator iterator; - condition_counter counter; -public:@; - static int max_parallel_threads; - @<|detach_thread_group::insert| code@>; - @<|detach_thread_group| destructor code@>; - @<|detach_thread_group::run| code@>; -}; - -@ When inserting, the counter is installed to the thread. -@<|detach_thread_group::insert| code@>= -void insert(_Ctype* c) -{ - tlist.push_back(c); - c->installCounter(&counter); -} - -@ The destructor is clear. -@<|detach_thread_group| destructor code@>= -~detach_thread_group() -{ - while (!tlist.empty()) { - delete tlist.front(); - tlist.pop_front(); - } -} - -@ We cycle through all threads in the group, and in each cycle we wait -for the change in the |counter|. If the counter indicates less than -maximum parallel threads running, then a new thread is run, and the -iterator in the list is moved. - -At the end we have to wait for all thread to finish. - -@<|detach_thread_group::run| code@>= -void run() -{ - int mpt = max_parallel_threads; - iterator it = tlist.begin(); - while (it != tlist.end()) { - if (counter.waitForChange() < mpt) { - counter.increase(); - (*it)->run(); - ++it; - } - } - while (counter.waitForChange() > 0) {} -} - - -@ Here we only define the specializations for POSIX threads. Then we -define the macros. Note that the |PosixSynchro| class construct itself -from the static map defined in {\tt sthreads.cpp}. - -@= -typedef detach_thread PosixThread; -typedef detach_thread_group PosixThreadGroup; -typedef synchro posix_synchro; -class PosixSynchro : public posix_synchro { -public:@; - PosixSynchro(const void* c, const char* id); -}; -@# -#define THREAD@, sthread::PosixThread -#define THREAD_GROUP@, sthread::PosixThreadGroup -#define SYNCHRO@, sthread::PosixSynchro - -@ Here we define an empty class and use it as thread and -mutex. |NoSynchro| class is also empty, but an empty constructor is -declared. The empty destructor is declared only to avoid ``unused -variable warning''. - -@= -typedef thread NoThread; -typedef thread_group NoThreadGroup; -typedef synchro no_synchro; -class NoSynchro { -public:@; - NoSynchro(const void* c, const char* id) {} - ~NoSynchro() {} -}; -@# -#define THREAD@, sthread::NoThread -#define THREAD_GROUP@, sthread::NoThreadGroup -#define SYNCHRO@, sthread::NoSynchro - -@ End of {\tt sthreads.h} file. diff --git a/dynare++/tl/cc/symmetry.cc b/dynare++/tl/cc/symmetry.cc new file mode 100644 index 000000000..8aafd2161 --- /dev/null +++ b/dynare++/tl/cc/symmetry.cc @@ -0,0 +1,144 @@ +// Copyright (C) 2004-2011, Ondra Kamenik + +#include "symmetry.hh" +#include "permutation.hh" + +#include + +/* Construct symmetry as numbers of successively equal items in the sequence. */ + +Symmetry::Symmetry(const IntSequence &s) + : IntSequence(s.getNumDistinct(), 0) +{ + int p = 0; + if (s.size() > 0) + operator[](p) = 1; + for (int i = 1; i < s.size(); i++) + { + if (s[i] != s[i-1]) + p++; + operator[](p)++; + } +} + +/* Find a class of the symmetry containing a given index. */ + +int +Symmetry::findClass(int i) const +{ + int j = 0; + int sum = 0; + do + { + sum += operator[](j); + j++; + } + while (j < size() && sum <= i); + + return j-1; +} + +/* The symmetry is full if it allows for any permutation of indices. It + means, that there is at most one non-zero index. */ + +bool +Symmetry::isFull() const +{ + int count = 0; + for (int i = 0; i < num(); i++) + if (operator[](i) != 0) + count++; + return count <= 1; +} + +/* Here we construct the beginning of the |symiterator|. The first + symmetry index is 0. If length is 2, the second index is the + dimension, otherwise we create the subordinal symmetry set and its + beginning as subordinal |symiterator|. */ + +symiterator::symiterator(SymmetrySet &ss) + : s(ss), subit(NULL), subs(NULL), end_flag(false) +{ + s.sym()[0] = 0; + if (s.size() == 2) + { + s.sym()[1] = s.dimen(); + } + else + { + subs = new SymmetrySet(s, s.dimen()); + subit = new symiterator(*subs); + } +} + +symiterator::~symiterator() +{ + if (subit) + delete subit; + if (subs) + delete subs; +} + +/* Here we move to the next symmetry. We do so only, if we are not at + the end. If length is 2, we increase lower index and decrease upper + index, otherwise we increase the subordinal symmetry. If we got to the + end, we recreate the subordinal symmetry set and set the subordinal + iterator to the beginning. At the end we test, if we are not at the + end. This is recognized if the lowest index exceeded the dimension. */ + +symiterator & +symiterator::operator++() +{ + if (!end_flag) + { + if (s.size() == 2) + { + s.sym()[0]++; + s.sym()[1]--; + } + else + { + ++(*subit); + if (subit->isEnd()) + { + delete subit; + delete subs; + s.sym()[0]++; + subs = new SymmetrySet(s, s.dimen()-s.sym()[0]); + subit = new symiterator(*subs); + } + } + if (s.sym()[0] == s.dimen()+1) + end_flag = true; + } + return *this; +} + +InducedSymmetries::InducedSymmetries(const Equivalence &e, const Symmetry &s) +{ + for (Equivalence::const_seqit i = e.begin(); i != e.end(); ++i) + { + push_back(Symmetry(s, *i)); + } +} + +// |InducedSymmetries| permuted constructor code +InducedSymmetries::InducedSymmetries(const Equivalence &e, const Permutation &p, + const Symmetry &s) +{ + for (int i = 0; i < e.numClasses(); i++) + { + Equivalence::const_seqit it = e.find(p.getMap()[i]); + push_back(Symmetry(s, *it)); + } +} + +/* Debug print. */ + +void +InducedSymmetries::print() const +{ + printf("Induced symmetries: %lu\n", (unsigned long) size()); + for (unsigned int i = 0; i < size(); i++) + operator[](i).print(); +} diff --git a/dynare++/tl/cc/symmetry.cweb b/dynare++/tl/cc/symmetry.cweb deleted file mode 100644 index 3b516c293..000000000 --- a/dynare++/tl/cc/symmetry.cweb +++ /dev/null @@ -1,153 +0,0 @@ -@q Copyright (C) 2004-2011, Ondra Kamenik @> - -@ Start of {\tt symmetry.cpp} file. - -@c -#include "symmetry.h" -#include "permutation.h" - -#include - -@<|Symmetry| constructor code@>; -@<|Symmetry::findClass| code@>; -@<|Symmetry::isFull| code@>; -@<|symiterator| constructor code@>; -@<|symiterator| destructor code@>; -@<|symiterator::operator++| code@>; -@<|InducedSymmetries| constructor code@>; -@<|InducedSymmetries| permuted constructor code@>; -@<|InducedSymmetries::print| code@>; - -@ Construct symmetry as numbers of successively equal items in the sequence. - -@<|Symmetry| constructor code@>= -Symmetry::Symmetry(const IntSequence& s) - : IntSequence(s.getNumDistinct(), 0) -{ - int p = 0; - if (s.size() > 0) - operator[](p) = 1; - for (int i = 1; i < s.size(); i++) { - if (s[i] != s[i-1]) - p++; - operator[](p)++; - } -} - -@ Find a class of the symmetry containing a given index. -@<|Symmetry::findClass| code@>= -int Symmetry::findClass(int i) const -{ - int j = 0; - int sum = 0; - do { - sum += operator[](j); - j++; - } while (j < size() && sum <= i); - - return j-1; -} - -@ The symmetry is full if it allows for any permutation of indices. It -means, that there is at most one non-zero index. - -@<|Symmetry::isFull| code@>= -bool Symmetry::isFull() const -{ - int count = 0; - for (int i = 0; i < num(); i++) - if (operator[](i) != 0) - count++; - return count <=1; -} - - -@ Here we construct the beginning of the |symiterator|. The first -symmetry index is 0. If length is 2, the second index is the -dimension, otherwise we create the subordinal symmetry set and its -beginning as subordinal |symiterator|. - -@<|symiterator| constructor code@>= -symiterator::symiterator(SymmetrySet& ss) - : s(ss), subit(NULL), subs(NULL), end_flag(false) -{ - s.sym()[0] = 0; - if (s.size() == 2) { - s.sym()[1] = s.dimen(); - } else { - subs = new SymmetrySet(s, s.dimen()); - subit = new symiterator(*subs); - } -} - - -@ -@<|symiterator| destructor code@>= -symiterator::~symiterator( ) -{ - if (subit) - delete subit; - if (subs) - delete subs; -} - -@ Here we move to the next symmetry. We do so only, if we are not at -the end. If length is 2, we increase lower index and decrease upper -index, otherwise we increase the subordinal symmetry. If we got to the -end, we recreate the subordinal symmetry set and set the subordinal -iterator to the beginning. At the end we test, if we are not at the -end. This is recognized if the lowest index exceeded the dimension. - -@<|symiterator::operator++| code@>= -symiterator& symiterator::operator++() -{ - if (!end_flag) { - if (s.size() == 2) { - s.sym()[0]++; - s.sym()[1]--; - } else { - ++(*subit); - if (subit->isEnd()) { - delete subit; - delete subs; - s.sym()[0]++; - subs = new SymmetrySet(s, s.dimen()-s.sym()[0]); - subit = new symiterator(*subs); - } - } - if (s.sym()[0] == s.dimen()+1) - end_flag=true; - } - return *this; -} - -@ -@<|InducedSymmetries| constructor code@>= -InducedSymmetries::InducedSymmetries(const Equivalence& e, const Symmetry& s) -{ - for (Equivalence::const_seqit i = e.begin(); i != e.end(); ++i) { - push_back(Symmetry(s, *i)); - } -} - -@ -@<|InducedSymmetries| permuted constructor code@>= -InducedSymmetries::InducedSymmetries(const Equivalence& e, const Permutation& p, - const Symmetry& s) -{ - for (int i = 0; i < e.numClasses(); i++) { - Equivalence::const_seqit it = e.find(p.getMap()[i]); - push_back(Symmetry(s, *it)); - } -} - -@ Debug print. -@<|InducedSymmetries::print| code@>= -void InducedSymmetries::print() const -{ - printf("Induced symmetries: %lu\n", (unsigned long) size()); - for (unsigned int i = 0; i < size(); i++) - operator[](i).print(); -} - -@ End of {\tt symmetry.cpp} file. \ No newline at end of file diff --git a/dynare++/tl/cc/symmetry.hh b/dynare++/tl/cc/symmetry.hh new file mode 100644 index 000000000..ce615ef30 --- /dev/null +++ b/dynare++/tl/cc/symmetry.hh @@ -0,0 +1,227 @@ +// Copyright 2004, Ondra Kamenik + +// Symmetry. + +/* Symmetry is an abstraction for a term of the form $y^3u^2$. It manages + only indices, not the variable names. So if one uses this + abstraction, he must keep in mind that $y$ is the first, and $u$ is + the second. + + In fact, the symmetry is a special case of equivalence, but its + implementation is much simpler. We do not need an abstraction for the + term $yyuyu$ but due to Green theorem we can have term $y^3u^2$. That + is why the equivalence is too general for our purposes. + + One of a main purposes of the tensor library is to calculate something like: + $$\left[B_{y^2u^3}\right]_{\alpha_1\alpha_2\beta_1\beta_2\beta_3} + =\left[g_{y^l}\right]_{\gamma_1\ldots\gamma_l} + \left(\sum_{c\in M_{l,5}} + \prod_{m=1}^l\left[g_{c_m}\right]^{\gamma_m}_{c_m(\alpha,\beta)}\right)$$ + If, for instance, $l=3$, and $c=\{\{0,4\},\{1,2\},\{3\}\}$, then we + have to calculate + $$\left[g_{y^3}\right]_{\gamma_1\gamma_2\gamma_3} + \left[g_{yu}\right]^{\gamma_1}_{\alpha_1\beta_3} + \left[g_{yu}\right]^{\gamma_2}_{\alpha_2\beta_1} + \left[g_u\right]^{\gamma_3}_{\beta_2} + $$ + + We must be able to calculate a symmetry induced by symmetry $y^2u^3$ + and by an equivalence class from equivalence $c$. For equivalence + class $\{0,4\}$ the induced symmetry is $yu$, since we pick first and + fifth variable from $y^2u^3$. For a given outer symmetry, the class + |InducedSymmetries| does this for all classes of a given equivalence. + + We need also to cycle through all possible symmetries yielding the + given dimension. For this purpose we define classes |SymmetrySet| and + |symiterator|. + + The symmetry is implemented as |IntSequence|, in fact, it inherits + from it. */ + +#ifndef SYMMETRY_H +#define SYMMETRY_H + +#include "equivalence.hh" +#include "int_sequence.hh" + +#include +#include + +/* Clear. The method |isFull| returns true if and only if the symmetry + allows for any permutation of indices. */ + +class Symmetry : public IntSequence +{ +public: + /* We provide three constructors for symmetries of the form $y^n$, + $y^nu^m$, $y^nu^m\sigma^k$. Also a copy constructor, and finally a + constructor of implied symmetry for a symmetry and an equivalence + class. It is already implemented in |IntSequence| so we only call + appropriate constructor of |IntSequence|. We also provide the + subsymmetry, which takes the given length of symmetry from the end. + + The last constructor constructs a symmetry from an integer sequence + (supposed to be ordered) as a symmetry counting successively equal + items. For instance the sequence $(a,a,a,b,c,c,d,d,d,d)$ produces + symmetry $(3,1,2,4)$. */ + Symmetry(int len, const char *dummy) + : IntSequence(len, 0) + { + } + Symmetry(int i1) + : IntSequence(1, i1) + { + } + Symmetry(int i1, int i2) + : IntSequence(2) + { + operator[](0) = i1; operator[](1) = i2; + } + Symmetry(int i1, int i2, int i3) + : IntSequence(3) + { + operator[](0) = i1; + operator[](1) = i2; + operator[](2) = i3; + } + Symmetry(int i1, int i2, int i3, int i4) + : IntSequence(4) + { + operator[](0) = i1; + operator[](1) = i2; + operator[](2) = i3; + operator[](3) = i4; + } + Symmetry(const Symmetry &s) + : IntSequence(s) + { + } + Symmetry(const Symmetry &s, const OrdSequence &cl) + : IntSequence(s, cl.getData()) + { + } + Symmetry(Symmetry &s, int len) + : IntSequence(s, s.size()-len, s.size()) + { + } + Symmetry(const IntSequence &s); + + int + num() const + { + return size(); + } + int + dimen() const + { + return sum(); + } + int findClass(int i) const; + bool isFull() const; +}; + +/* The class |SymmetrySet| defines a set of symmetries of the given + length having given dimension. It does not store all the symmetries, + rather it provides a storage for one symmetry, which is changed as an + adjoint iterator moves. + + The iterator class is |symiterator|. It is implemented + recursively. The iterator object, when created, creates subordinal + iterator, which iterates over a symmetry set whose length is one less, + and dimension is the former dimension. When the subordinal iterator + goes to its end, the superordinal iterator increases left most index in + the symmetry, resets the subordinal symmetry set with different + dimension, and iterates through the subordinal symmetry set until its + end, and so on. That's why we provide also |SymmetrySet| constructor + for construction of a subordinal symmetry set. + + The typical usage of the abstractions for |SymmetrySet| and + |symiterator| is as follows: + + \kern0.3cm + \centerline{|for (symiterator si(SymmetrySet(6, 4)); !si.isEnd(); ++si) {body}|} + \kern0.3cm + + \noindent It goes through all symmetries of size 4 having dimension + 6. One can use |*si| as the symmetry in the body. */ + +class SymmetrySet +{ + Symmetry run; + int dim; +public: + SymmetrySet(int d, int length) + : run(length, ""), dim(d) + { + } + SymmetrySet(SymmetrySet &s, int d) + : run(s.run, s.size()-1), dim(d) + { + } + int + dimen() const + { + return dim; + } + const Symmetry & + sym() const + { + return run; + } + Symmetry & + sym() + { + return run; + } + int + size() const + { + return run.size(); + } +}; + +/* The logic of |symiterator| was described in |@<|SymmetrySet| class + declaration@>|. Here we only comment that: the class has a reference + to the |SymmetrySet| only to know dimension and for access of its + symmetry storage. Further we have pointers to subordinal |symiterator| + and its |SymmetrySet|. These are pointers, since the recursion ends at + length equal to 2, in which case these pointers are |NULL|. + + The constructor creates the iterator which initializes to the first + symmetry (beginning). */ + +class symiterator +{ + SymmetrySet &s; + symiterator *subit; + SymmetrySet *subs; + bool end_flag; +public: + symiterator(SymmetrySet &ss); + ~symiterator(); + symiterator &operator++(); + bool + isEnd() const + { + return end_flag; + } + const Symmetry & + operator*() const + { + return s.sym(); + } +}; + +/* This simple abstraction just constructs a vector of induced + symmetries from the given equivalence and outer symmetry. A + permutation might optionally permute the classes of the equivalence. */ + +class InducedSymmetries : public vector +{ +public: + InducedSymmetries(const Equivalence &e, const Symmetry &s); + InducedSymmetries(const Equivalence &e, const Permutation &p, const Symmetry &s); + void print() const; +}; + +#endif diff --git a/dynare++/tl/cc/symmetry.hweb b/dynare++/tl/cc/symmetry.hweb deleted file mode 100644 index 244548f92..000000000 --- a/dynare++/tl/cc/symmetry.hweb +++ /dev/null @@ -1,208 +0,0 @@ -@q $Id: symmetry.hweb 841 2006-07-27 14:41:11Z tamas $ @> -@q Copyright 2004, Ondra Kamenik @> - -@*2 Symmetry. This is {\tt symmetry.h} file - -Symmetry is an abstraction for a term of the form $y^3u^2$. It manages -only indices, not the variable names. So if one uses this -abstraction, he must keep in mind that $y$ is the first, and $u$ is -the second. - -In fact, the symmetry is a special case of equivalence, but its -implementation is much simpler. We do not need an abstraction for the -term $yyuyu$ but due to Green theorem we can have term $y^3u^2$. That -is why the equivalence is too general for our purposes. - -One of a main purposes of the tensor library is to calculate something like: -$$\left[B_{y^2u^3}\right]_{\alpha_1\alpha_2\beta_1\beta_2\beta_3} -=\left[g_{y^l}\right]_{\gamma_1\ldots\gamma_l} -\left(\sum_{c\in M_{l,5}} -\prod_{m=1}^l\left[g_{c_m}\right]^{\gamma_m}_{c_m(\alpha,\beta)}\right)$$ -If, for instance, $l=3$, and $c=\{\{0,4\},\{1,2\},\{3\}\}$, then we -have to calculate -$$\left[g_{y^3}\right]_{\gamma_1\gamma_2\gamma_3} -\left[g_{yu}\right]^{\gamma_1}_{\alpha_1\beta_3} -\left[g_{yu}\right]^{\gamma_2}_{\alpha_2\beta_1} -\left[g_u\right]^{\gamma_3}_{\beta_2} -$$ - -We must be able to calculate a symmetry induced by symmetry $y^2u^3$ -and by an equivalence class from equivalence $c$. For equivalence -class $\{0,4\}$ the induced symmetry is $yu$, since we pick first and -fifth variable from $y^2u^3$. For a given outer symmetry, the class -|InducedSymmetries| does this for all classes of a given equivalence. - -We need also to cycle through all possible symmetries yielding the -given dimension. For this purpose we define classes |SymmetrySet| and -|symiterator|. - -The symmetry is implemented as |IntSequence|, in fact, it inherits -from it. - -@s Symmetry int -@s IntSequence int -@s SymmetrySet int -@s symiterator int -@s OrdSequence int -@s InducedSymmetries int - -@c -#ifndef SYMMETRY_H -#define SYMMETRY_H - -#include "equivalence.h" -#include "int_sequence.h" - -#include -#include - -@<|Symmetry| class declaration@>; -@<|SymmetrySet| class declaration@>; -@<|symiterator| class declaration@>; -@<|InducedSymmetries| class declaration@>; - -#endif - -@ Clear. The method |isFull| returns true if and only if the symmetry -allows for any permutation of indices. - -@<|Symmetry| class declaration@>= -class Symmetry : public IntSequence { -public:@/ - @<|Symmetry| constructors@>; - int num() const - {@+return size();@+} - int dimen() const - {@+return sum();@+} - int findClass(int i) const; - bool isFull() const; -}; - -@ We provide three constructors for symmetries of the form $y^n$, -$y^nu^m$, $y^nu^m\sigma^k$. Also a copy constructor, and finally a -constructor of implied symmetry for a symmetry and an equivalence -class. It is already implemented in |IntSequence| so we only call -appropriate constructor of |IntSequence|. We also provide the -subsymmetry, which takes the given length of symmetry from the end. - -The last constructor constructs a symmetry from an integer sequence -(supposed to be ordered) as a symmetry counting successively equal -items. For instance the sequence $(a,a,a,b,c,c,d,d,d,d)$ produces -symmetry $(3,1,2,4)$. - -@<|Symmetry| constructors@>= - Symmetry(int len, const char* dummy) - : IntSequence(len, 0)@+ {} - Symmetry(int i1) - : IntSequence(1, i1)@+ {} - Symmetry(int i1, int i2) - : IntSequence(2) {@+operator[](0) = i1;@+ operator[](1) = i2;@+} - Symmetry(int i1, int i2 ,int i3) - : IntSequence(3) - {@+ - operator[](0) = i1;@+ - operator[](1) = i2;@+ - operator[](2) = i3;@+ - } - Symmetry(int i1, int i2 ,int i3, int i4) - : IntSequence(4) - {@+ - operator[](0) = i1;@+ - operator[](1) = i2;@+ - operator[](2) = i3;@+ - operator[](3) = i4;@+ - } - Symmetry(const Symmetry& s) - : IntSequence(s)@+ {} - Symmetry(const Symmetry& s, const OrdSequence& cl) - : IntSequence(s, cl.getData())@+ {} - Symmetry(Symmetry& s, int len) - : IntSequence(s, s.size()-len, s.size())@+ {} - Symmetry(const IntSequence& s); - -@ The class |SymmetrySet| defines a set of symmetries of the given -length having given dimension. It does not store all the symmetries, -rather it provides a storage for one symmetry, which is changed as an -adjoint iterator moves. - -The iterator class is |symiterator|. It is implemented -recursively. The iterator object, when created, creates subordinal -iterator, which iterates over a symmetry set whose length is one less, -and dimension is the former dimension. When the subordinal iterator -goes to its end, the superordinal iterator increases left most index in -the symmetry, resets the subordinal symmetry set with different -dimension, and iterates through the subordinal symmetry set until its -end, and so on. That's why we provide also |SymmetrySet| constructor -for construction of a subordinal symmetry set. - -The typical usage of the abstractions for |SymmetrySet| and -|symiterator| is as follows: - -\kern0.3cm -\centerline{|for (symiterator si(SymmetrySet(6, 4)); !si.isEnd(); ++si) {body}|} -\kern0.3cm - -\noindent It goes through all symmetries of size 4 having dimension -6. One can use |*si| as the symmetry in the body. - -@<|SymmetrySet| class declaration@>= -class SymmetrySet { - Symmetry run; - int dim; -public:@; - SymmetrySet(int d, int length) - : run(length, ""), dim(d)@+ {} - SymmetrySet(SymmetrySet& s, int d) - : run(s.run, s.size()-1), dim(d)@+ {} - int dimen() const - {@+ return dim;@+} - const Symmetry& sym() const - {@+ return run;@+} - Symmetry& sym() - {@+ return run;@+} - int size() const - {@+ return run.size();@+} -}; - -@ The logic of |symiterator| was described in |@<|SymmetrySet| class -declaration@>|. Here we only comment that: the class has a reference -to the |SymmetrySet| only to know dimension and for access of its -symmetry storage. Further we have pointers to subordinal |symiterator| -and its |SymmetrySet|. These are pointers, since the recursion ends at -length equal to 2, in which case these pointers are |NULL|. - -The constructor creates the iterator which initializes to the first -symmetry (beginning). - -@<|symiterator| class declaration@>= -class symiterator { - SymmetrySet& s; - symiterator* subit; - SymmetrySet* subs; - bool end_flag; -public:@; - symiterator(SymmetrySet& ss); - ~symiterator(); - symiterator& operator++(); - bool isEnd() const - {@+ return end_flag;@+} - const Symmetry& operator*() const - {@+ return s.sym();@+} -}; - - -@ This simple abstraction just constructs a vector of induced -symmetries from the given equivalence and outer symmetry. A -permutation might optionally permute the classes of the equivalence. - -@<|InducedSymmetries| class declaration@>= -class InducedSymmetries : public vector { -public:@; - InducedSymmetries(const Equivalence& e, const Symmetry& s); - InducedSymmetries(const Equivalence& e, const Permutation& p, const Symmetry& s); - void print() const; -}; - - - -@ End of {\tt symmetry.h} file. diff --git a/dynare++/tl/cc/t_container.cc b/dynare++/tl/cc/t_container.cc new file mode 100644 index 000000000..007f958e8 --- /dev/null +++ b/dynare++/tl/cc/t_container.cc @@ -0,0 +1,127 @@ +// Copyright 2004, Ondra Kamenik + +#include "t_container.hh" +#include "kron_prod.hh" +#include "ps_tensor.hh" +#include "pyramid_prod.hh" + +const int FGSContainer::num_one_time = 10; + +// |UGSContainer| conversion from |FGSContainer| +UGSContainer::UGSContainer(const FGSContainer &c) + : TensorContainer(c.num()) +{ + for (FGSContainer::const_iterator it = c.begin(); + it != c.end(); ++it) + { + UGSTensor *unfolded = new UGSTensor(*((*it).second)); + insert(unfolded); + } +} + +/* We set |l| to dimension of |t|, this is a tensor which multiplies + tensors from the container from the left. Also we set |k| to a + dimension of the resulting tensor. We go through all equivalences on + |k| element set and pickup only those which have $l$ classes. + + In each loop, we fetch all necessary tensors for the product to the + vector |ts|. Then we form Kronecker product |KronProdAll| and feed it + with tensors from |ts|. Then we form unfolded permuted symmetry tensor + |UPSTensor| as matrix product of |t| and Kronecker product |kp|. Then + we add the permuted data to |out|. This is done by |UPSTensor| method + |addTo|. */ + +void +UGSContainer::multAndAdd(const UGSTensor &t, UGSTensor &out) const +{ + int l = t.dimen(); + int k = out.dimen(); + const EquivalenceSet &eset = ebundle.get(k); + + for (EquivalenceSet::const_iterator it = eset.begin(); + it != eset.end(); ++it) + { + if ((*it).numClasses() == l) + { + vector ts + = fetchTensors(out.getSym(), *it); + KronProdAllOptim kp(l); + for (int i = 0; i < l; i++) + kp.setMat(i, *(ts[i])); + kp.optimizeOrder(); + UPSTensor ups(out.getDims(), *it, t, kp); + ups.addTo(out); + } + } +} + +// |FGSContainer| conversion from |UGSContainer| +FGSContainer::FGSContainer(const UGSContainer &c) + : TensorContainer(c.num()) +{ + for (UGSContainer::const_iterator it = c.begin(); + it != c.end(); ++it) + { + FGSTensor *folded = new FGSTensor(*((*it).second)); + insert(folded); + } +} + +// |FGSContainer::multAndAdd| folded code +/* Here we perform one step of the Faa Di Bruno operation. We call the + |multAndAdd| for unfolded tensor. */ +void +FGSContainer::multAndAdd(const FGSTensor &t, FGSTensor &out) const +{ + UGSTensor ut(t); + multAndAdd(ut, out); +} + +// |FGSContainer::multAndAdd| unfolded code +/* This is the same as |@<|UGSContainer::multAndAdd| code@>| + but we do not construct |UPSTensor| from the Kronecker + product, but |FPSTensor|. */ +void +FGSContainer::multAndAdd(const UGSTensor &t, FGSTensor &out) const +{ + int l = t.dimen(); + int k = out.dimen(); + const EquivalenceSet &eset = ebundle.get(k); + + for (EquivalenceSet::const_iterator it = eset.begin(); + it != eset.end(); ++it) + { + if ((*it).numClasses() == l) + { + vector ts + = fetchTensors(out.getSym(), *it); + KronProdAllOptim kp(l); + for (int i = 0; i < l; i++) + kp.setMat(i, *(ts[i])); + kp.optimizeOrder(); + FPSTensor fps(out.getDims(), *it, t, kp); + fps.addTo(out); + } + } +} + +/* This fills a given vector with integer sequences corresponding to + first |num| indices from interval |start| (including) to |end| + (excluding). If there are not |num| of such indices, the shorter vector + is returned. */ +Tensor::index +FGSContainer::getIndices(int num, vector &out, + const Tensor::index &start, + const Tensor::index &end) +{ + out.clear(); + int i = 0; + Tensor::index run = start; + while (i < num && run != end) + { + out.push_back(run.getCoor()); + i++; + ++run; + } + return run; +} diff --git a/dynare++/tl/cc/t_container.cweb b/dynare++/tl/cc/t_container.cweb deleted file mode 100644 index e4a1b3ee6..000000000 --- a/dynare++/tl/cc/t_container.cweb +++ /dev/null @@ -1,138 +0,0 @@ -@q $Id: t_container.cweb 148 2005-04-19 15:12:26Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@ Start of {\tt t\_container.cpp} file. -@s USubTensor int -@c -#include "t_container.h" -#include "kron_prod.h" -#include "ps_tensor.h" -#include "pyramid_prod.h" - -const int FGSContainer::num_one_time = 10; -@<|UGSContainer| conversion from |FGSContainer|@>; -@<|UGSContainer::multAndAdd| code@>; -@<|FGSContainer| conversion from |UGSContainer|@>; -@<|FGSContainer::multAndAdd| folded code@>; -@<|FGSContainer::multAndAdd| unfolded code@>; -@<|FGSContainer::getIndices| code@>; - -@ -@<|UGSContainer| conversion from |FGSContainer|@>= -UGSContainer::UGSContainer(const FGSContainer& c) - : TensorContainer(c.num()) -{ - for (FGSContainer::const_iterator it = c.begin(); - it != c.end(); ++it) { - UGSTensor* unfolded = new UGSTensor(*((*it).second)); - insert(unfolded); - } -} - -@ We set |l| to dimension of |t|, this is a tensor which multiplies -tensors from the container from the left. Also we set |k| to a -dimension of the resulting tensor. We go through all equivalences on -|k| element set and pickup only those which have $l$ classes. - -In each loop, we fetch all necessary tensors for the product to the -vector |ts|. Then we form Kronecker product |KronProdAll| and feed it -with tensors from |ts|. Then we form unfolded permuted symmetry tensor -|UPSTensor| as matrix product of |t| and Kronecker product |kp|. Then -we add the permuted data to |out|. This is done by |UPSTensor| method -|addTo|. - -@<|UGSContainer::multAndAdd| code@>= -void UGSContainer::multAndAdd(const UGSTensor& t, UGSTensor& out) const -{ - int l = t.dimen(); - int k = out.dimen(); - const EquivalenceSet& eset = ebundle.get(k); - - for (EquivalenceSet::const_iterator it = eset.begin(); - it != eset.end(); ++it) { - if ((*it).numClasses() == l) { - vector ts = - fetchTensors(out.getSym(), *it); - KronProdAllOptim kp(l); - for (int i = 0; i < l; i++) - kp.setMat(i, *(ts[i])); - kp.optimizeOrder(); - UPSTensor ups(out.getDims(), *it, t, kp); - ups.addTo(out); - } - } -} - -@ -@<|FGSContainer| conversion from |UGSContainer|@>= -FGSContainer::FGSContainer(const UGSContainer& c) - : TensorContainer(c.num()) -{ - for (UGSContainer::const_iterator it = c.begin(); - it != c.end(); ++it) { - FGSTensor* folded = new FGSTensor(*((*it).second)); - insert(folded); - } -} - - -@ Here we perform one step of the Faa Di Bruno operation. We call the -|multAndAdd| for unfolded tensor. -@<|FGSContainer::multAndAdd| folded code@>= -void FGSContainer::multAndAdd(const FGSTensor& t, FGSTensor& out) const -{ - UGSTensor ut(t); - multAndAdd(ut, out); -} - -@ This is the same as |@<|UGSContainer::multAndAdd| code@>| -but we do not construct |UPSTensor| from the Kronecker -product, but |FPSTensor|. - -@<|FGSContainer::multAndAdd| unfolded code@>= -void FGSContainer::multAndAdd(const UGSTensor& t, FGSTensor& out) const -{ - int l = t.dimen(); - int k = out.dimen(); - const EquivalenceSet& eset = ebundle.get(k); - - for (EquivalenceSet::const_iterator it = eset.begin(); - it != eset.end(); ++it) { - if ((*it).numClasses() == l) { - vector ts = - fetchTensors(out.getSym(), *it); - KronProdAllOptim kp(l); - for (int i = 0; i < l; i++) - kp.setMat(i, *(ts[i])); - kp.optimizeOrder(); - FPSTensor fps(out.getDims(), *it, t, kp); - fps.addTo(out); - } - } -} - - -@ This fills a given vector with integer sequences corresponding to -first |num| indices from interval |start| (including) to |end| -(excluding). If there are not |num| of such indices, the shorter vector -is returned. - -@<|FGSContainer::getIndices| code@>= -Tensor::index -FGSContainer::getIndices(int num, vector& out, - const Tensor::index& start, - const Tensor::index& end) -{ - out.clear(); - int i = 0; - Tensor::index run = start; - while (i < num && run != end) { - out.push_back(run.getCoor()); - i++; - ++run; - } - return run; -} - - -@ End of {\tt t\_container.cpp} file. diff --git a/dynare++/tl/cc/t_container.hh b/dynare++/tl/cc/t_container.hh new file mode 100644 index 000000000..07c6e7b98 --- /dev/null +++ b/dynare++/tl/cc/t_container.hh @@ -0,0 +1,387 @@ +// Copyright 2004, Ondra Kamenik + +// Tensor containers. + +/* One of primary purposes of the tensor library is to perform one step + of the Faa Di Bruno formula: + $$\left[B_{s^k}\right]_{\alpha_1\ldots\alpha_k}= + [h_{y^l}]_{\gamma_1\ldots\gamma_l}\sum_{c\in M_{l,k}} + \prod_{m=1}^l\left[g_{s^{\vert c_m\vert}}\right]^{\gamma_m}_{c_m(\alpha)} + $$ + where $h_{y^l}$ and $g_{s^i}$ are tensors, $M_{l,k}$ is a set of all + equivalences with $l$ classes of $k$ element set, $c_m$ is $m$-the + class of equivalence $c$, and $\vert c_m\vert$ is its + cardinality. Further, $c_m(\alpha)$ is a sequence of $\alpha$s picked + by equivalence class $c_m$. + + In order to accomplish this operation, we basically need some storage + of all tensors of the form $\left[g_{s^i}\right]$. Note that $s$ can + be compound, for instance $s=[y,u]$. Then we need storage for + $\left[g_{y^3}\right]$, $\left[g_{y^2u}\right]$, + $\left[g_{yu^5}\right]$, etc. + + We need an object holding all tensors of the same type. Here type + means an information, that coordinates of the tensors can be of type + $y$, or $u$. We will group only tensors, whose symmetry is described + by |Symmetry| class. These are only $y^2u^3$, not $yuyu^2$. So, we are + going to define a class which will hold tensors whose symmetries are + of type |Symmetry| and have the same symmetry length (number of + different coordinate types). Also, for each symmetry there will be at + most one tensor. + + The class has two purposes: The first is to provide storage (insert + and retrieve). The second is to perform the above step of Faa Di Bruno. This is + going through all equivalences with $l$ classes, perform the tensor + product and add to the result. + + We define a template class |TensorContainer|. From different + instantiations of the template class we will inherit to create concrete + classes, for example container of unfolded general symmetric + tensors. The one step of the Faa Di Bruno (we call it |multAndAdd|) is + implemented in the concrete subclasses, because the implementation + depends on storage. Note even, that |multAndAdd| has not a template + common declaration. This is because sparse tensor $h$ is multiplied by + folded tensors $g$ yielding folded tensor $B$, but unfolded tensor $h$ + is multiplied by unfolded tensors $g$ yielding unfolded tensor $B$. */ + +#ifndef T_CONTAINER_H +#define T_CONTAINER_H + +#include "symmetry.hh" +#include "gs_tensor.hh" +#include "tl_exception.hh" +#include "tl_static.hh" +#include "sparse_tensor.hh" +#include "equivalence.hh" +#include "rfs_tensor.hh" +#include "Vector.h" + +#include +#include +#include + +#include + +// |ltsym| predicate +/* We need a predicate on strict weak ordering of + symmetries. */ +struct ltsym +{ + bool + operator()(const Symmetry &s1, const Symmetry &s2) const + { + return s1 < s2; + } +}; + +/* Here we define the template class for tensor container. We implement + it as |stl::map|. It is a unique container, no two tensors with same + symmetries can coexist. Keys of the map are symmetries, values are + pointers to tensor. The class is responsible for deallocating all + tensors. Creation of the tensors is done outside. + + The class has integer |n| as its member. It is a number of different + coordinate types of all contained tensors. Besides intuitive insert + and retrieve interface, we define a method |fetchTensors|, which for a + given symmetry and given equivalence calculates symmetries implied by + the symmetry and all equivalence classes, and fetches corresponding + tensors in a vector. + + Also, each instance of the container has a reference to + |EquivalenceBundle| which allows an access to equivalences. */ + +template +class TensorContainer +{ +protected: + typedef const _Ttype *_const_ptr; + typedef _Ttype *_ptr; + typedef map _Map; + typedef typename _Map::value_type _mvtype; +public: + typedef typename _Map::iterator iterator; + typedef typename _Map::const_iterator const_iterator; +private: + int n; + _Map m; +protected: + const EquivalenceBundle &ebundle; +public: + TensorContainer(int nn) + : n(nn), ebundle(*(tls.ebundle)) + { + } + /* This is just a copy constructor. This makes a hard copy of all tensors. */ + TensorContainer(const TensorContainer<_Ttype> &c) + : n(c.n), m(), ebundle(c.ebundle) + { + for (const_iterator it = c.m.begin(); it != c.m.end(); ++it) + { + _Ttype *ten = new _Ttype(*((*it).second)); + insert(ten); + } + } + + // |TensorContainer| subtensor constructor + /* This constructor constructs a new tensor container, whose tensors + are in-place subtensors of the given container. */ + TensorContainer(int first_row, int num, TensorContainer<_Ttype> &c) + : n(c.n), ebundle(*(tls.ebundle)) + { + for (iterator it = c.m.begin(); it != c.m.end(); ++it) + { + _Ttype *t = new _Ttype(first_row, num, *((*it).second)); + insert(t); + } + } + + _const_ptr + get(const Symmetry &s) const + { + TL_RAISE_IF(s.num() != num(), + "Incompatible symmetry lookup in TensorContainer::get"); + const_iterator it = m.find(s); + if (it == m.end()) + { + TL_RAISE("Symmetry not found in TensorContainer::get"); + return NULL; + } + else + { + return (*it).second; + } + } + + _ptr + get(const Symmetry &s) + { + TL_RAISE_IF(s.num() != num(), + "Incompatible symmetry lookup in TensorContainer::get"); + iterator it = m.find(s); + if (it == m.end()) + { + TL_RAISE("Symmetry not found in TensorContainer::get"); + return NULL; + } + else + { + return (*it).second; + } + } + + bool + check(const Symmetry &s) const + { + TL_RAISE_IF(s.num() != num(), + "Incompatible symmetry lookup in TensorContainer::check"); + const_iterator it = m.find(s); + return it != m.end(); + } + + void + insert(_ptr t) + { + TL_RAISE_IF(t->getSym().num() != num(), + "Incompatible symmetry insertion in TensorContainer::insert"); + TL_RAISE_IF(check(t->getSym()), + "Tensor already in container in TensorContainer::insert"); + m.insert(_mvtype(t->getSym(), t)); + if (!t->isFinite()) + { + throw TLException(__FILE__, __LINE__, "NaN or Inf asserted in TensorContainer::insert"); + } + } + + void + remove(const Symmetry &s) + { + iterator it = m.find(s); + if (it != m.end()) + { + _ptr t = (*it).second; + m.erase(it); + delete t; + } + } + + void + clear() + { + while (!m.empty()) + { + delete (*(m.begin())).second; + m.erase(m.begin()); + } + } + + int + getMaxDim() const + { + int res = -1; + for (const_iterator run = m.begin(); run != m.end(); ++run) + { + int dim = (*run).first.dimen(); + if (dim > res) + res = dim; + } + return res; + } + + /* Debug print. */ + void + print() const + { + printf("Tensor container: nvars=%d, tensors=%D\n", n, m.size()); + for (const_iterator it = m.begin(); it != m.end(); ++it) + { + printf("Symmetry: "); + (*it).first.print(); + ((*it).second)->print(); + } + } + + /* Output to the MAT file. */ + void + writeMat(mat_t *fd, const char *prefix) const + { + for (const_iterator it = begin(); it != end(); ++it) + { + char lname[100]; + sprintf(lname, "%s_g", prefix); + const Symmetry &sym = (*it).first; + for (int i = 0; i < sym.num(); i++) + { + char tmp[10]; + sprintf(tmp, "_%d", sym[i]); + strcat(lname, tmp); + } + ConstTwoDMatrix m(*((*it).second)); + m.writeMat(fd, lname); + } + } + + /* Output to the Memory Map. */ + void + writeMMap(map &mm, const string &prefix) const + { + ostringstream lname; + for (const_iterator it = begin(); it != end(); ++it) + { + lname.str(prefix); + lname << "_g"; + const Symmetry &sym = (*it).first; + for (int i = 0; i < sym.num(); i++) + lname << "_" << sym[i]; + mm.insert(make_pair(lname.str(), ConstTwoDMatrix(*((*it).second)))); + } + } + + /* Here we fetch all tensors given by symmetry and equivalence. We go + through all equivalence classes, calculate implied symmetry, and + fetch its tensor storing it in the same order to the vector. */ + + vector<_const_ptr> + fetchTensors(const Symmetry &rsym, const Equivalence &e) const + { + vector<_const_ptr> res(e.numClasses()); + int i = 0; + for (Equivalence::const_seqit it = e.begin(); + it != e.end(); ++it, i++) + { + Symmetry s(rsym, *it); + res[i] = get(s); + } + return res; + } + + virtual ~TensorContainer() + { + clear(); + } + + int + num() const + { + return n; + } + const EquivalenceBundle & + getEqBundle() const + { + return ebundle; + } + + const_iterator + begin() const + { + return m.begin(); + } + const_iterator + end() const + { + return m.end(); + } + iterator + begin() + { + return m.begin(); + } + iterator + end() + { + return m.end(); + } +}; + +/* Here is a container storing |UGSTensor|s. We declare |multAndAdd| method. */ + +class FGSContainer; +class UGSContainer : public TensorContainer +{ +public: + UGSContainer(int nn) + : TensorContainer(nn) + { + } + UGSContainer(const UGSContainer &uc) + : TensorContainer(uc) + { + } + UGSContainer(const FGSContainer &c); + void multAndAdd(const UGSTensor &t, UGSTensor &out) const; +}; + +/* Here is a container storing |FGSTensor|s. We declare two versions of + |multAndAdd| method. The first works for folded $B$ and folded $h$ + tensors, the second works for folded $B$ and unfolded $h$. There is no + point to do it for unfolded $B$ since the algorithm go through all the + indices of $B$ and calculates corresponding columns. So, if $B$ is + needed unfolded, it is more effective to calculate its folded version + and then unfold by conversion. + + The static member |num_one_time| is a number of columns formed from + product of $g$ tensors at one time. This is subject to change, probably + we will have to do some tuning and decide about this number based on + symmetries, and dimensions in the runtime. */ + +class FGSContainer : public TensorContainer +{ + static const int num_one_time; +public: + FGSContainer(int nn) + : TensorContainer(nn) + { + } + FGSContainer(const FGSContainer &fc) + : TensorContainer(fc) + { + } + FGSContainer(const UGSContainer &c); + void multAndAdd(const FGSTensor &t, FGSTensor &out) const; + void multAndAdd(const UGSTensor &t, FGSTensor &out) const; +private: + static Tensor::index getIndices(int num, vector &out, + const Tensor::index &start, + const Tensor::index &end); +}; + +#endif diff --git a/dynare++/tl/cc/t_container.hweb b/dynare++/tl/cc/t_container.hweb deleted file mode 100644 index 5aa46b461..000000000 --- a/dynare++/tl/cc/t_container.hweb +++ /dev/null @@ -1,380 +0,0 @@ -@q $Id: t_container.hweb 2353 2009-09-03 19:22:36Z michel $ @> -@q Copyright 2004, Ondra Kamenik @> - -@*2 Tensor containers. Start of {\tt t\_container.h} file. - -One of primary purposes of the tensor library is to perform one step -of the Faa Di Bruno formula: -$$\left[B_{s^k}\right]_{\alpha_1\ldots\alpha_k}= -[h_{y^l}]_{\gamma_1\ldots\gamma_l}\sum_{c\in M_{l,k}} -\prod_{m=1}^l\left[g_{s^{\vert c_m\vert}}\right]^{\gamma_m}_{c_m(\alpha)} -$$ -where $h_{y^l}$ and $g_{s^i}$ are tensors, $M_{l,k}$ is a set of all -equivalences with $l$ classes of $k$ element set, $c_m$ is $m$-the -class of equivalence $c$, and $\vert c_m\vert$ is its -cardinality. Further, $c_m(\alpha)$ is a sequence of $\alpha$s picked -by equivalence class $c_m$. - -In order to accomplish this operation, we basically need some storage -of all tensors of the form $\left[g_{s^i}\right]$. Note that $s$ can -be compound, for instance $s=[y,u]$. Then we need storage for -$\left[g_{y^3}\right]$, $\left[g_{y^2u}\right]$, -$\left[g_{yu^5}\right]$, etc. - -We need an object holding all tensors of the same type. Here type -means an information, that coordinates of the tensors can be of type -$y$, or $u$. We will group only tensors, whose symmetry is described -by |Symmetry| class. These are only $y^2u^3$, not $yuyu^2$. So, we are -going to define a class which will hold tensors whose symmetries are -of type |Symmetry| and have the same symmetry length (number of -different coordinate types). Also, for each symmetry there will be at -most one tensor. - -The class has two purposes: The first is to provide storage (insert -and retrieve). The second is to perform the above step of Faa Di Bruno. This is -going through all equivalences with $l$ classes, perform the tensor -product and add to the result. - -We define a template class |TensorContainer|. From different -instantiations of the template class we will inherit to create concrete -classes, for example container of unfolded general symmetric -tensors. The one step of the Faa Di Bruno (we call it |multAndAdd|) is -implemented in the concrete subclasses, because the implementation -depends on storage. Note even, that |multAndAdd| has not a template -common declaration. This is because sparse tensor $h$ is multiplied by -folded tensors $g$ yielding folded tensor $B$, but unfolded tensor $h$ -is multiplied by unfolded tensors $g$ yielding unfolded tensor $B$. - -@c -#ifndef T_CONTAINER_H -#define T_CONTAINER_H - -#include "symmetry.h" -#include "gs_tensor.h" -#include "tl_exception.h" -#include "tl_static.h" -#include "sparse_tensor.h" -#include "equivalence.h" -#include "rfs_tensor.h" -#include "Vector.h" - -#include -#include -#include - -#include - -@<|ltsym| predicate@>; -@<|TensorContainer| class definition@>; -@<|UGSContainer| class declaration@>; -@<|FGSContainer| class declaration@>; - -#endif - -@ We need a predicate on strict weak ordering of symmetries. -@<|ltsym| predicate@>= -struct ltsym { - bool operator()(const Symmetry& s1, const Symmetry& s2) const - {@+ return s1 < s2;@+} -}; - -@ Here we define the template class for tensor container. We implement -it as |stl::map|. It is a unique container, no two tensors with same -symmetries can coexist. Keys of the map are symmetries, values are -pointers to tensor. The class is responsible for deallocating all -tensors. Creation of the tensors is done outside. - -The class has integer |n| as its member. It is a number of different -coordinate types of all contained tensors. Besides intuitive insert -and retrieve interface, we define a method |fetchTensors|, which for a -given symmetry and given equivalence calculates symmetries implied by -the symmetry and all equivalence classes, and fetches corresponding -tensors in a vector. - -Also, each instance of the container has a reference to -|EquivalenceBundle| which allows an access to equivalences. - -@s _const_ptr int; -@s _ptr int; -@s _Map int; - -@<|TensorContainer| class definition@>= -template class TensorContainer { -protected:@; - typedef const _Ttype* _const_ptr; - typedef _Ttype* _ptr; - typedef map _Map;@/ - typedef typename _Map::value_type _mvtype;@/ -public:@; - typedef typename _Map::iterator iterator;@/ - typedef typename _Map::const_iterator const_iterator;@/ -private:@; - int n; - _Map m; -protected:@; - const EquivalenceBundle& ebundle; -public:@; - TensorContainer(int nn) - : n(nn), ebundle(*(tls.ebundle)) @+ {} - @<|TensorContainer| copy constructor@>; - @<|TensorContainer| subtensor constructor@>; - @<|TensorContainer:get| code@>; - @<|TensorContainer::check| code@>; - @<|TensorContainer::insert| code@>; - @<|TensorContainer::remove| code@>; - @<|TensorContainer::clear| code@>; - @<|TensorContainer::fetchTensors| code@>; - @<|TensorContainer::getMaxDim| code@>; - @<|TensorContainer::print| code@>; - @<|TensorContainer::writeMat| code@>; - @<|TensorContainer::writeMMap| code@>; - - virtual ~TensorContainer() - {@+ clear();@+} - - @<|TensorContainer| inline methods@>; -}; - -@ -@<|TensorContainer| inline methods@>= - int num() const - {@+ return n;@+} - const EquivalenceBundle& getEqBundle() const - {@+ return ebundle;@+} - - const_iterator begin() const - {@+ return m.begin();@+} - const_iterator end() const - {@+ return m.end();@+} - iterator begin() - {@+ return m.begin();@+} - iterator end() - {@+ return m.end();@+} - -@ This is just a copy constructor. This makes a hard copy of all tensors. -@<|TensorContainer| copy constructor@>= -TensorContainer(const TensorContainer<_Ttype>& c) - : n(c.n), m(), ebundle(c.ebundle) -{ - for (const_iterator it = c.m.begin(); it != c.m.end(); ++it) { - _Ttype* ten = new _Ttype(*((*it).second)); - insert(ten); - } -} - -@ This constructor constructs a new tensor container, whose tensors -are in-place subtensors of the given container. - -@<|TensorContainer| subtensor constructor@>= -TensorContainer(int first_row, int num, TensorContainer<_Ttype>& c) - : n(c.n), ebundle(*(tls.ebundle)) -{ - for (iterator it = c.m.begin(); it != c.m.end(); ++it) { - _Ttype* t = new _Ttype(first_row, num, *((*it).second)); - insert(t); - } -} - - -@ -@<|TensorContainer:get| code@>= -_const_ptr get(const Symmetry& s) const -{ - TL_RAISE_IF(s.num() != num(), - "Incompatible symmetry lookup in TensorContainer::get"); - const_iterator it = m.find(s); - if (it == m.end()) { - TL_RAISE("Symmetry not found in TensorContainer::get"); - return NULL; - } else { - return (*it).second; - } -} -@# - -_ptr get(const Symmetry& s) -{ - TL_RAISE_IF(s.num() != num(), - "Incompatible symmetry lookup in TensorContainer::get"); - iterator it = m.find(s); - if (it == m.end()) { - TL_RAISE("Symmetry not found in TensorContainer::get"); - return NULL; - } else { - return (*it).second; - } -} - -@ -@<|TensorContainer::check| code@>= -bool check(const Symmetry& s) const -{ - TL_RAISE_IF(s.num() != num(), - "Incompatible symmetry lookup in TensorContainer::check"); - const_iterator it = m.find(s); - return it != m.end(); -} - -@ -@<|TensorContainer::insert| code@>= -void insert(_ptr t) -{ - TL_RAISE_IF(t->getSym().num() != num(), - "Incompatible symmetry insertion in TensorContainer::insert"); - TL_RAISE_IF(check(t->getSym()), - "Tensor already in container in TensorContainer::insert"); - m.insert(_mvtype(t->getSym(),t)); - if (! t->isFinite()) { - throw TLException(__FILE__, __LINE__, "NaN or Inf asserted in TensorContainer::insert"); - } -} - -@ -@<|TensorContainer::remove| code@>= -void remove(const Symmetry& s) -{ - iterator it = m.find(s); - if (it != m.end()) { - _ptr t = (*it).second; - m.erase(it); - delete t; - } -} - - -@ -@<|TensorContainer::clear| code@>= -void clear() -{ - while (! m.empty()) { - delete (*(m.begin())).second; - m.erase(m.begin()); - } -} - -@ -@<|TensorContainer::getMaxDim| code@>= -int getMaxDim() const -{ - int res = -1; - for (const_iterator run = m.begin(); run != m.end(); ++run) { - int dim = (*run).first.dimen(); - if (dim > res) - res = dim; - } - return res; -} - - -@ Debug print. -@<|TensorContainer::print| code@>= -void print() const -{ - printf("Tensor container: nvars=%d, tensors=%D\n", n, m.size()); - for (const_iterator it = m.begin(); it != m.end(); ++it) { - printf("Symmetry: "); - (*it).first.print(); - ((*it).second)->print(); - } -} - -@ Output to the MAT file. -@<|TensorContainer::writeMat| code@>= -void writeMat(mat_t* fd, const char* prefix) const -{ - for (const_iterator it = begin(); it != end(); ++it) { - char lname[100]; - sprintf(lname, "%s_g", prefix); - const Symmetry& sym = (*it).first; - for (int i = 0; i < sym.num(); i++) { - char tmp[10]; - sprintf(tmp, "_%d", sym[i]); - strcat(lname, tmp); - } - ConstTwoDMatrix m(*((*it).second)); - m.writeMat(fd, lname); - } -} - -@ Output to the Memory Map. -@<|TensorContainer::writeMMap| code@>= -void writeMMap(map &mm, const string &prefix) const -{ - ostringstream lname; - for (const_iterator it = begin(); it != end(); ++it) { - lname.str(prefix); - lname << "_g"; - const Symmetry& sym = (*it).first; - for (int i = 0; i < sym.num(); i++) - lname << "_" << sym[i]; - mm.insert(make_pair(lname.str(), ConstTwoDMatrix(*((*it).second)))); - } -} - -@ Here we fetch all tensors given by symmetry and equivalence. We go -through all equivalence classes, calculate implied symmetry, and -fetch its tensor storing it in the same order to the vector. - -@<|TensorContainer::fetchTensors| code@>= -vector<_const_ptr> -fetchTensors(const Symmetry& rsym, const Equivalence& e) const -{ - vector<_const_ptr> res(e.numClasses()); - int i = 0; - for (Equivalence::const_seqit it = e.begin(); - it != e.end(); ++it, i++) { - Symmetry s(rsym, *it); - res[i] = get(s); - } - return res; -} - -@ Here is a container storing |UGSTensor|s. We declare |multAndAdd| method. - -@<|UGSContainer| class declaration@>= -class FGSContainer; -class UGSContainer : public TensorContainer { -public:@; - UGSContainer(int nn) - : TensorContainer(nn)@+ {} - UGSContainer(const UGSContainer& uc) - : TensorContainer(uc)@+ {} - UGSContainer(const FGSContainer& c); - void multAndAdd(const UGSTensor& t, UGSTensor& out) const; -}; - - -@ Here is a container storing |FGSTensor|s. We declare two versions of -|multAndAdd| method. The first works for folded $B$ and folded $h$ -tensors, the second works for folded $B$ and unfolded $h$. There is no -point to do it for unfolded $B$ since the algorithm go through all the -indices of $B$ and calculates corresponding columns. So, if $B$ is -needed unfolded, it is more effective to calculate its folded version -and then unfold by conversion. - -The static member |num_one_time| is a number of columns formed from -product of $g$ tensors at one time. This is subject to change, probably -we will have to do some tuning and decide about this number based on -symmetries, and dimensions in the runtime. - -@s FGSContainer int -@<|FGSContainer| class declaration@>= -class FGSContainer : public TensorContainer { - static const int num_one_time; -public:@; - FGSContainer(int nn) - : TensorContainer(nn)@+ {} - FGSContainer(const FGSContainer& fc) - : TensorContainer(fc)@+ {} - FGSContainer(const UGSContainer& c); - void multAndAdd(const FGSTensor& t, FGSTensor& out) const; - void multAndAdd(const UGSTensor& t, FGSTensor& out) const; -private:@; - static Tensor::index - getIndices(int num, vector& out, - const Tensor::index& start, - const Tensor::index& end); -}; - - -@ End of {\tt t\_container.h} file. diff --git a/dynare++/tl/cc/t_polynomial.cc b/dynare++/tl/cc/t_polynomial.cc new file mode 100644 index 000000000..2ab0e7534 --- /dev/null +++ b/dynare++/tl/cc/t_polynomial.cc @@ -0,0 +1,68 @@ +// Copyright 2004, Ondra Kamenik + +#include "t_polynomial.hh" +#include "kron_prod.hh" + +// |PowerProvider::getNext| unfolded code +/* This method constructs unfolded |ut| of higher dimension, deleting + the previous. */ + +const URSingleTensor & +PowerProvider::getNext(const URSingleTensor *dummy) +{ + if (ut) + { + URSingleTensor *ut_new = new URSingleTensor(nv, ut->dimen()+1); + KronProd::kronMult(ConstVector(origv), ConstVector(ut->getData()), ut_new->getData()); + delete ut; + ut = ut_new; + } + else + { + ut = new URSingleTensor(nv, 1); + ut->getData() = origv; + } + return *ut; +} + +// |PowerProvider::getNext| folded code +/* This method just constructs next unfolded |ut| and creates folded + |ft|. */ + +const FRSingleTensor & +PowerProvider::getNext(const FRSingleTensor *dummy) +{ + getNext(ut); + if (ft) + delete ft; + ft = new FRSingleTensor(*ut); + return *ft; +} + +PowerProvider::~PowerProvider() +{ + if (ut) + delete ut; + if (ft) + delete ft; +} + +UTensorPolynomial::UTensorPolynomial(const FTensorPolynomial &fp) + : TensorPolynomial(fp.nrows(), fp.nvars()) +{ + for (FTensorPolynomial::const_iterator it = fp.begin(); + it != fp.end(); ++it) + { + insert(new UFSTensor(*((*it).second))); + } +} + +FTensorPolynomial::FTensorPolynomial(const UTensorPolynomial &up) + : TensorPolynomial(up.nrows(), up.nvars()) +{ + for (UTensorPolynomial::const_iterator it = up.begin(); + it != up.end(); ++it) + { + insert(new FFSTensor(*((*it).second))); + } +} diff --git a/dynare++/tl/cc/t_polynomial.cweb b/dynare++/tl/cc/t_polynomial.cweb deleted file mode 100644 index 8ad9d0070..000000000 --- a/dynare++/tl/cc/t_polynomial.cweb +++ /dev/null @@ -1,80 +0,0 @@ -@q $Id: t_polynomial.cweb 1210 2007-03-19 21:38:49Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@ Start of {\tt t\_polynomial.cpp} file. - -@c -#include "t_polynomial.h" -#include "kron_prod.h" - -@<|PowerProvider::getNext| unfolded code@>; -@<|PowerProvider::getNext| folded code@>; -@<|PowerProvider| destructor code@>; -@<|UTensorPolynomial| constructor conversion code@>; -@<|FTensorPolynomial| constructor conversion code@>; - - -@ This method constructs unfolded |ut| of higher dimension, deleting -the previous. - -@<|PowerProvider::getNext| unfolded code@>= -const URSingleTensor& PowerProvider::getNext(const URSingleTensor* dummy) -{ - if (ut) { - URSingleTensor* ut_new = new URSingleTensor(nv, ut->dimen()+1); - KronProd::kronMult(ConstVector(origv), ConstVector(ut->getData()), ut_new->getData()); - delete ut; - ut = ut_new; - } else { - ut = new URSingleTensor(nv, 1); - ut->getData() = origv; - } - return *ut; -} - -@ This method just constructs next unfolded |ut| and creates folded -|ft|. - -@<|PowerProvider::getNext| folded code@>= -const FRSingleTensor& PowerProvider::getNext(const FRSingleTensor* dummy) -{ - getNext(ut); - if (ft) - delete ft; - ft = new FRSingleTensor(*ut); - return *ft; -} - -@ -@<|PowerProvider| destructor code@>= -PowerProvider::~PowerProvider() -{ - if (ut) - delete ut; - if (ft) - delete ft; -} - -@ Clear. -@<|UTensorPolynomial| constructor conversion code@>= -UTensorPolynomial::UTensorPolynomial(const FTensorPolynomial& fp) - : TensorPolynomial(fp.nrows(), fp.nvars()) -{ - for (FTensorPolynomial::const_iterator it = fp.begin(); - it != fp.end(); ++it) { - insert(new UFSTensor(*((*it).second))); - } -} - -@ Clear. -@<|FTensorPolynomial| constructor conversion code@>= -FTensorPolynomial::FTensorPolynomial(const UTensorPolynomial& up) - : TensorPolynomial(up.nrows(), up.nvars()) -{ - for (UTensorPolynomial::const_iterator it = up.begin(); - it != up.end(); ++it) { - insert(new FFSTensor(*((*it).second))); - } -} - -@ End of {\tt t\_polynomial.cpp} file. diff --git a/dynare++/tl/cc/t_polynomial.hh b/dynare++/tl/cc/t_polynomial.hh new file mode 100644 index 000000000..9d0e27161 --- /dev/null +++ b/dynare++/tl/cc/t_polynomial.hh @@ -0,0 +1,536 @@ +// Copyright 2004, Ondra Kamenik + +// Tensor polynomial evaluation. + +/* 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. */ + +#include "t_container.hh" +#include "fs_tensor.hh" +#include "rfs_tensor.hh" +#include "tl_static.hh" + +/* 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. */ + +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. */ + +template +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@ + /* 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(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$ + /* 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 + |@|. 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. */ + 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| + /* The pointer |ten| is either a new tensor or got from |this| container. */ + _Ttype *ten; + if (_Tparent::check(Symmetry(j))) + { + ten = _Tparent::get(Symmetry(j)); + } + else + { + ten = new _Ttype(nrows(), nvars(), j); + ten->zeros(); + insert(ten); + } + + 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); + } + } + } + + // do contraction for $i=0$ + /* This is easy. The code is equivalent to code |@0$@>| as for $i=0$. The contraction here takes a form of a + simple addition. */ + for (int j = 0; j <= tp.maxdim; j++) + { + if (tp.check(Symmetry(j))) + { + + // initialize |ten| of dimension |j| + /* Same code as above */ + _Ttype *ten; + if (_Tparent::check(Symmetry(j))) + { + ten = _Tparent::get(Symmetry(j)); + } + else + { + ten = new _Ttype(nrows(), nvars(), j); + ten->zeros(); + insert(ten); + } + + Symmetry sym(0, j); + IntSequence coor(sym, pp); + _TGStype slice(*(tp.get(Symmetry(j))), ss, coor, TensorDimens(sym, ss)); + ten->add(1.0, slice); + } + } + } + + 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; + } + + /* 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|. */ + + 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|. */ + + 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|. */ + + 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. */ + + 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|. */ + + _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 <mp = *(_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. */ + +class FTensorPolynomial; +class UTensorPolynomial : public TensorPolynomial +{ +public: + UTensorPolynomial(int rows, int vars) + : TensorPolynomial(rows, vars) + { + } + UTensorPolynomial(const UTensorPolynomial &up, int k) + : TensorPolynomial(up, k) + { + } + UTensorPolynomial(const FTensorPolynomial &fp); + UTensorPolynomial(const UTensorPolynomial &tp, const Vector &xval) + : TensorPolynomial(tp, xval) + { + } + UTensorPolynomial(int first_row, int num, UTensorPolynomial &tp) + : TensorPolynomial(first_row, num, tp) + { + } +}; + +/* This just gives a name to folded tensor polynomial. */ + +class FTensorPolynomial : public TensorPolynomial +{ +public: + FTensorPolynomial(int rows, int vars) + : TensorPolynomial(rows, vars) + { + } + FTensorPolynomial(const FTensorPolynomial &fp, int k) + : TensorPolynomial(fp, k) + { + } + FTensorPolynomial(const UTensorPolynomial &up); + FTensorPolynomial(const FTensorPolynomial &tp, const Vector &xval) + : TensorPolynomial(tp, xval) + { + } + FTensorPolynomial(int first_row, int num, FTensorPolynomial &tp) + : TensorPolynomial(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$. */ + +template +class CompactPolynomial : public _Ttype +{ +public: + /* 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(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. */ + + 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. */ +class UCompactPolynomial : public CompactPolynomial +{ +public: + UCompactPolynomial(const UTensorPolynomial &upol) + : CompactPolynomial(upol) + { + } +}; + +/* Specialization of the |CompactPolynomial| for folded tensor. */ +class FCompactPolynomial : public CompactPolynomial +{ +public: + FCompactPolynomial(const FTensorPolynomial &fpol) + : CompactPolynomial(fpol) + { + } +}; diff --git a/dynare++/tl/cc/t_polynomial.hweb b/dynare++/tl/cc/t_polynomial.hweb deleted file mode 100644 index e0a9c5d06..000000000 --- a/dynare++/tl/cc/t_polynomial.hweb +++ /dev/null @@ -1,507 +0,0 @@ -@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 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; - - @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 -|@|. 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. - -@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))) { - @; - 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 |@0$@>| as for $i=0$. The contraction here takes a form of a -simple addition. - -@= - for (int j = 0; j <= tp.maxdim; j++) { - if (tp.check(Symmetry(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. -@= - _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 { -public:@; - UTensorPolynomial(int rows, int vars) - : TensorPolynomial(rows, vars)@+ {} - UTensorPolynomial(const UTensorPolynomial& up, int k) - : TensorPolynomial(up, k)@+ {} - UTensorPolynomial(const FTensorPolynomial& fp); - UTensorPolynomial(const UTensorPolynomial& tp, const Vector& xval) - : TensorPolynomial(tp, xval)@+ {} - UTensorPolynomial(int first_row, int num, UTensorPolynomial& tp) - : TensorPolynomial(first_row, num, tp)@+ {} -}; - -@ This just gives a name to folded tensor polynomial. -@<|FTensorPolynomial| class declaration@>= -class FTensorPolynomial : public TensorPolynomial { -public:@; - FTensorPolynomial(int rows, int vars) - : TensorPolynomial(rows, vars)@+ {} - FTensorPolynomial(const FTensorPolynomial& fp, int k) - : TensorPolynomial(fp, k)@+ {} - FTensorPolynomial(const UTensorPolynomial& up); - FTensorPolynomial(const FTensorPolynomial& tp, const Vector& xval) - : TensorPolynomial(tp, xval)@+ {} - FTensorPolynomial(int first_row, int num, FTensorPolynomial& tp) - : TensorPolynomial(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 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 { -public:@; - UCompactPolynomial(const UTensorPolynomial& upol) - : CompactPolynomial(upol)@+ {} -}; - -@ Specialization of the |CompactPolynomial| for folded tensor. -@<|FCompactPolynomial| class declaration@>= -class FCompactPolynomial : public CompactPolynomial { -public:@; - FCompactPolynomial(const FTensorPolynomial& fpol) - : CompactPolynomial(fpol)@+ {} -}; - - - -@ End of {\tt t\_polynomial.h} file. diff --git a/dynare++/tl/cc/tensor.cc b/dynare++/tl/cc/tensor.cc new file mode 100644 index 000000000..14163a118 --- /dev/null +++ b/dynare++/tl/cc/tensor.cc @@ -0,0 +1,222 @@ +// Copyright 2004, Ondra Kamenik + +#include "tensor.hh" +#include "tl_exception.hh" +#include "tl_static.hh" + +// |Tensor| static methods +/* Here we implement calculation of $\pmatrix{n\cr k}$ where $n-k$ is + usually bigger than $k$. + + Also we implement $a^b$. */ + +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; +} + +// |Tensor::noverseq_ip| static method +/* 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. */ + +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. */ + +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)|. */ + +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|. */ + +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)|. */ + +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. */ + +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. */ + +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 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. */ + +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; +} diff --git a/dynare++/tl/cc/tensor.cweb b/dynare++/tl/cc/tensor.cweb deleted file mode 100644 index a6f5438fc..000000000 --- a/dynare++/tl/cc/tensor.cweb +++ /dev/null @@ -1,229 +0,0 @@ -@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= -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. diff --git a/dynare++/tl/cc/tensor.hh b/dynare++/tl/cc/tensor.hh new file mode 100644 index 000000000..6828caaaa --- /dev/null +++ b/dynare++/tl/cc/tensor.hh @@ -0,0 +1,309 @@ +// Copyright 2004, Ondra Kamenik + +// Tensor concept. + +/* Here we define a tensor class. Tensor is a mathematical object + corresponding to a $(n+1)$-dimensional array. An element of such array + is denoted $[B]_{\alpha_1\ldots\alpha_n}^\beta$, where $\beta$ is a + special index and $\alpha_1\ldots\alpha_n$ are other indices. The + class |Tensor| and its subclasses view such array as a 2D matrix, + where $\beta$ corresponds to one dimension, and + $\alpha_1\ldots\alpha_2$ unfold to the other dimension. Whether + $\beta$ correspond to rows or columns is decided by tensor subclasses, + however, most of our tensors will have rows indexed by $\beta$, and + $\alpha_1\ldots\alpha_n$ will unfold column-wise. + + There might be some symmetries in the tensor data. For instance, if + $\alpha_1$ is interchanged with $\alpha_3$ and the both elements equal + for all possible $\alpha_i$, and $\beta$, then there is a symmetry + of $\alpha_1$ and $\alpha_3$. + + For any symmetry, there are basically two possible storages of the + data. The first is unfolded storage, which stores all elements + regardless the symmetry. The other storage type is folded, which + stores only elements which do not repeat. We declare abstract classes + for unfolded tensor, and folded tensor. + + Also, here we also define a concept of tensor index which is the + $n$-tuple $\alpha_1\ldots\alpha_n$. It is an iterator, which iterates + in dependence of symmetry and storage of the underlying tensor. + + Although we do not decide about possible symmetries at this point, it + is worth noting that we implement two kinds of symmetries. The first + one is a full symmetry where all indices are interchangeable. The + second one is a generalization of the first. We define tensor of a + symmetry, where there are a few groups of indices interchangeable + within a group and not across. Moreover, the groups are required to be + consequent partitions of the index $n$-tuple. This is, we do not allow + $\alpha_1$ be interchangeable with $\alpha_3$ and not with $\alpha_2$ + at the same time. + + However, some intermediate results are, in fact, tensors of a symmetry + not fitting to our concept. We develop the tensor abstraction for it, + but these objects are not used very often. They have limited usage + due to their specialized constructor. */ + +#ifndef TENSOR_H +#define TENSOR_H + +#include "int_sequence.hh" +#include "twod_matrix.hh" + +/* The index represents $n$-tuple $\alpha_1\ldots\alpha_n$. Since its + movement is dependent on the underlying tensor (with storage and + symmetry), we maintain a pointer to that tensor, we maintain the + $n$-tuple (or coordinates) as |IntSequence| and also we maintain the + offset number (column, or row) of the index in the tensor. The pointer + is const, since we do not need to change data through the index. + + Here we require the |tensor| to implement |increment| and |decrement| + methods, which calculate following and preceding $n$-tuple. Also, we + need to calculate offset number from the given coordinates, so the + tensor must implement method |getOffset|. This method is used only in + construction of the index from the given coordinates. As the index is + created, the offset is automatically incremented, and decremented + together with index. The|getOffset| method can be relatively + computationally complex. This must be kept in mind. Also we generally + suppose that n-tuple of all zeros is the first offset (first columns + or row). + + What follows is a definition of index class, the only + interesting point is |operator==| which decides only according to + offset, not according to the coordinates. This is useful since there + can be more than one of coordinate representations of past-the-end + index. */ + +template +class _index +{ + typedef _index<_Tptr> _Self; + _Tptr tensor; + int offset; + IntSequence coor; +public: + _index(_Tptr t, int n) + : tensor(t), offset(0), coor(n, 0) + { + } + _index(_Tptr t, const IntSequence &cr, int c) + : tensor(t), offset(c), coor(cr) + { + } + _index(_Tptr t, const IntSequence &cr) + : tensor(t), offset(tensor->getOffset(cr)), coor(cr) + { + } + _index(const _index &ind) + : tensor(ind.tensor), offset(ind.offset), coor(ind.coor) + { + } + const _Self & + operator=(const _Self &in) + { + tensor = in.tensor; offset = in.offset; coor = in.coor; + return *this; + } + _Self & + operator++() + { + tensor->increment(coor); offset++; return *this; + } + _Self & + operator--() + { + tensor->decrement(coor); offset--; return *this; + } + int + operator*() const + { + return offset; + } + bool + operator==(const _index &n) const + { + return offset == n.offset; + } + bool + operator!=(const _index &n) const + { + return offset != n.offset; + } + const IntSequence & + getCoor() const + { + return coor; + } + void + print() const + { + printf("%4d: ", offset); coor.print(); + } +}; + +/* Here is the |Tensor| class, which is nothing else than a simple subclass + of |TwoDMatrix|. The unique semantically new member is |dim| which is tensor + dimension (length of $\alpha_1\ldots\alpha_n$). We also declare + |increment|, |decrement| and |getOffset| methods as pure virtual. + + We also add members for index begin and index end. This is useful, + since |begin| and |end| methods do not return instance but only + references, which prevent making additional copy of index (for example + in for cycles as |in != end()| which would do a copy of index for each + cycle). The index begin |in_beg| is constructed as a sequence of all + zeros, and |in_end| is constructed from the sequence |last| passed to + the constructor, since it depends on subclasses. Also we have to say, + along what coordinate is the multidimensional index. This is used only + for initialization of |in_end|. + + Also, we declare static auxiliary functions for $\pmatrix{n\cr k}$ + which is |noverk| and $a^b$, which is |power|. */ + +class Tensor : public TwoDMatrix +{ +public: + enum indor {along_row, along_col}; + typedef _index index; +protected: + const index in_beg; + const index in_end; + int dim; +public: + Tensor(indor io, const IntSequence &last, int r, int c, int d) + : TwoDMatrix(r, c), + in_beg(this, d), + in_end(this, last, (io == along_row) ? r : c), + dim(d) + { + } + Tensor(indor io, const IntSequence &first, const IntSequence &last, + int r, int c, int d) + : TwoDMatrix(r, c), + in_beg(this, first, 0), + in_end(this, last, (io == along_row) ? r : c), + dim(d) + { + } + Tensor(int first_row, int num, Tensor &t) + : TwoDMatrix(first_row, num, t), + in_beg(t.in_beg), + in_end(t.in_end), + dim(t.dim) + { + } + Tensor(const Tensor &t) + : TwoDMatrix(t), + in_beg(this, t.in_beg.getCoor(), *(t.in_beg)), + in_end(this, t.in_end.getCoor(), *(t.in_end)), + dim(t.dim) + { + } + virtual ~Tensor() + { + } + virtual void increment(IntSequence &v) const = 0; + virtual void decrement(IntSequence &v) const = 0; + virtual int getOffset(const IntSequence &v) const = 0; + int + dimen() const + { + return dim; + } + + const index & + begin() const + { + return in_beg; + } + const index & + end() const + { + return in_end; + } + + static int noverk(int n, int k); + static int power(int a, int b); + static int + noverseq(const IntSequence &s) + { + IntSequence seq(s); + return noverseq_ip((IntSequence &) s); + } +private: + static int noverseq_ip(IntSequence &s); +}; + +/* Here is an abstraction for unfolded tensor. We provide a pure + virtual method |fold| which returns a new instance of folded tensor of + the same symmetry. Also we provide static methods for incrementing and + decrementing an index with full symmetry and general symmetry as + defined above. */ + +class FTensor; +class UTensor : public Tensor +{ +public: + UTensor(indor io, const IntSequence &last, int r, int c, int d) + : Tensor(io, last, r, c, d) + { + } + UTensor(const UTensor &ut) + : Tensor(ut) + { + } + UTensor(int first_row, int num, UTensor &t) + : Tensor(first_row, num, t) + { + } + virtual ~UTensor() + { + } + virtual FTensor&fold() const = 0; + + static void increment(IntSequence &v, int nv); + static void decrement(IntSequence &v, int nv); + static void increment(IntSequence &v, const IntSequence &nvmx); + static void decrement(IntSequence &v, const IntSequence &nvmx); + static int getOffset(const IntSequence &v, int nv); + static int getOffset(const IntSequence &v, const IntSequence &nvmx); +}; + +/* This is an abstraction for folded tensor. It only provides a method + |unfold|, which returns the unfolded version of the same symmetry, and + static methods for decrementing indices. + + We also provide static methods for decrementing the |IntSequence| in + folded fashion and also calculating an offset for a given + |IntSequence|. However, this is relatively complex calculation, so + this should be avoided if possible. */ + +class FTensor : public Tensor +{ +public: + FTensor(indor io, const IntSequence &last, int r, int c, int d) + : Tensor(io, last, r, c, d) + { + } + FTensor(const FTensor &ft) + : Tensor(ft) + { + } + FTensor(int first_row, int num, FTensor &t) + : Tensor(first_row, num, t) + { + } + virtual ~FTensor() + { + } + virtual UTensor&unfold() const = 0; + + static void decrement(IntSequence &v, int nv); + static int + getOffset(const IntSequence &v, int nv) + { + IntSequence vtmp(v); return getOffsetRecurse(vtmp, nv); + } +private: + static int getOffsetRecurse(IntSequence &v, int nv); +}; + +#endif diff --git a/dynare++/tl/cc/tensor.hweb b/dynare++/tl/cc/tensor.hweb deleted file mode 100644 index 0594a23b7..000000000 --- a/dynare++/tl/cc/tensor.hweb +++ /dev/null @@ -1,252 +0,0 @@ -@q $Id: tensor.hweb 741 2006-05-09 11:12:46Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@*2 Tensor concept. Start of {\tt tensor.h} file. - -Here we define a tensor class. Tensor is a mathematical object -corresponding to a $(n+1)$-dimensional array. An element of such array -is denoted $[B]_{\alpha_1\ldots\alpha_n}^\beta$, where $\beta$ is a -special index and $\alpha_1\ldots\alpha_n$ are other indices. The -class |Tensor| and its subclasses view such array as a 2D matrix, -where $\beta$ corresponds to one dimension, and -$\alpha_1\ldots\alpha_2$ unfold to the other dimension. Whether -$\beta$ correspond to rows or columns is decided by tensor subclasses, -however, most of our tensors will have rows indexed by $\beta$, and -$\alpha_1\ldots\alpha_n$ will unfold column-wise. - -There might be some symmetries in the tensor data. For instance, if -$\alpha_1$ is interchanged with $\alpha_3$ and the both elements equal -for all possible $\alpha_i$, and $\beta$, then there is a symmetry -of $\alpha_1$ and $\alpha_3$. - -For any symmetry, there are basically two possible storages of the -data. The first is unfolded storage, which stores all elements -regardless the symmetry. The other storage type is folded, which -stores only elements which do not repeat. We declare abstract classes -for unfolded tensor, and folded tensor. - -Also, here we also define a concept of tensor index which is the -$n$-tuple $\alpha_1\ldots\alpha_n$. It is an iterator, which iterates -in dependence of symmetry and storage of the underlying tensor. - -Although we do not decide about possible symmetries at this point, it -is worth noting that we implement two kinds of symmetries. The first -one is a full symmetry where all indices are interchangeable. The -second one is a generalization of the first. We define tensor of a -symmetry, where there are a few groups of indices interchangeable -within a group and not across. Moreover, the groups are required to be -consequent partitions of the index $n$-tuple. This is, we do not allow -$\alpha_1$ be interchangeable with $\alpha_3$ and not with $\alpha_2$ -at the same time. - -However, some intermediate results are, in fact, tensors of a symmetry -not fitting to our concept. We develop the tensor abstraction for it, -but these objects are not used very often. They have limited usage -due to their specialized constructor. - -@c - -#ifndef TENSOR_H -#define TENSOR_H - -#include "int_sequence.h" -#include "twod_matrix.h" - -@; -@<|Tensor| class declaration@>; -@<|UTensor| class declaration@>; -@<|FTensor| class declaration@>; - -#endif - -@ The index represents $n$-tuple $\alpha_1\ldots\alpha_n$. Since its -movement is dependent on the underlying tensor (with storage and -symmetry), we maintain a pointer to that tensor, we maintain the -$n$-tuple (or coordinates) as |IntSequence| and also we maintain the -offset number (column, or row) of the index in the tensor. The pointer -is const, since we do not need to change data through the index. - -Here we require the |tensor| to implement |increment| and |decrement| -methods, which calculate following and preceding $n$-tuple. Also, we -need to calculate offset number from the given coordinates, so the -tensor must implement method |getOffset|. This method is used only in -construction of the index from the given coordinates. As the index is -created, the offset is automatically incremented, and decremented -together with index. The|getOffset| method can be relatively -computationally complex. This must be kept in mind. Also we generally -suppose that n-tuple of all zeros is the first offset (first columns -or row). - -What follows is a definition of index class, the only -interesting point is |operator==| which decides only according to -offset, not according to the coordinates. This is useful since there -can be more than one of coordinate representations of past-the-end -index. - -@s _Tptr int -@s _Self int - -@= -template class _index { - typedef _index<_Tptr> _Self; - _Tptr tensor; - int offset; - IntSequence coor; -public:@; - _index(_Tptr t, int n) - : tensor(t), offset(0), coor(n, 0)@+ {} - _index(_Tptr t, const IntSequence& cr, int c) - : tensor(t), offset(c), coor(cr)@+ {} - _index(_Tptr t, const IntSequence& cr) - : tensor(t), offset(tensor->getOffset(cr)), coor(cr)@+ {} - _index(const _index& ind) - : tensor(ind.tensor), offset(ind.offset), coor(ind.coor)@+ {} - const _Self& operator=(const _Self& in) - {@+ tensor = in.tensor;@+ offset = in.offset;@+ coor = in.coor; - return *this;@+} - _Self& operator++() - {@+ tensor->increment(coor);@+ offset++;@+ return *this;@+} - _Self& operator--() - {@+ tensor->decrement(coor);@+ offset--;@+ return *this;@+} - int operator*() const - {@+ return offset;@+} - bool operator==(const _index& n) const - {@+ return offset == n.offset;@+} - bool operator!=(const _index& n) const - {@+ return offset != n.offset;@+} - const IntSequence& getCoor() const - {@+ return coor;@+} - void print() const - {@+ printf("%4d: ", offset);@+ coor.print();@+} -}; - -@ Here is the |Tensor| class, which is nothing else than a simple subclass -of |TwoDMatrix|. The unique semantically new member is |dim| which is tensor -dimension (length of $\alpha_1\ldots\alpha_n$). We also declare -|increment|, |decrement| and |getOffset| methods as pure virtual. - -We also add members for index begin and index end. This is useful, -since |begin| and |end| methods do not return instance but only -references, which prevent making additional copy of index (for example -in for cycles as |in != end()| which would do a copy of index for each -cycle). The index begin |in_beg| is constructed as a sequence of all -zeros, and |in_end| is constructed from the sequence |last| passed to -the constructor, since it depends on subclasses. Also we have to say, -along what coordinate is the multidimensional index. This is used only -for initialization of |in_end|. - -Also, we declare static auxiliary functions for $\pmatrix{n\cr k}$ -which is |noverk| and $a^b$, which is |power|. - -@s indor int - -@<|Tensor| class declaration@>= -class Tensor : public TwoDMatrix { -public:@; - enum indor {along_row, along_col}; - typedef _index index; -protected:@; - const index in_beg; - const index in_end; - int dim; -public:@; - Tensor(indor io, const IntSequence& last, int r, int c, int d) - : TwoDMatrix(r, c), - in_beg(this, d), - in_end(this, last, (io == along_row)? r:c), - dim(d)@+ {} - Tensor(indor io, const IntSequence& first, const IntSequence& last, - int r, int c, int d) - : TwoDMatrix(r, c), - in_beg(this, first, 0), - in_end(this, last, (io == along_row)? r:c), - dim(d)@+ {} - Tensor(int first_row, int num, Tensor& t) - : TwoDMatrix(first_row, num, t), - in_beg(t.in_beg), - in_end(t.in_end), - dim(t.dim)@+ {} - Tensor(const Tensor& t) - : TwoDMatrix(t), - in_beg(this, t.in_beg.getCoor(), *(t.in_beg)), - in_end(this, t.in_end.getCoor(), *(t.in_end)), - dim(t.dim)@+ {} - virtual ~Tensor()@+ {} - virtual void increment(IntSequence& v) const =0; - virtual void decrement(IntSequence& v) const =0; - virtual int getOffset(const IntSequence& v) const =0; - int dimen() const - {@+ return dim;@+} - - const index& begin() const - {@+ return in_beg;@+} - const index& end() const - {@+ return in_end;@+} - - static int noverk(int n, int k); - static int power(int a, int b); - static int noverseq(const IntSequence& s) - { - IntSequence seq(s); - return noverseq_ip((IntSequence&)s); - } -private:@; - static int noverseq_ip(IntSequence& s); -}; - -@ Here is an abstraction for unfolded tensor. We provide a pure -virtual method |fold| which returns a new instance of folded tensor of -the same symmetry. Also we provide static methods for incrementing and -decrementing an index with full symmetry and general symmetry as -defined above. - -@<|UTensor| class declaration@>= -class FTensor; -class UTensor : public Tensor { -public:@; - UTensor(indor io, const IntSequence& last, int r, int c, int d) - : Tensor(io, last, r, c, d)@+ {} - UTensor(const UTensor& ut) - : Tensor(ut)@+ {} - UTensor(int first_row, int num, UTensor& t) - : Tensor(first_row, num, t)@+ {} - virtual ~UTensor()@+ {} - virtual FTensor& fold() const =0; - - static void increment(IntSequence& v, int nv); - static void decrement(IntSequence& v, int nv); - static void increment(IntSequence& v, const IntSequence& nvmx); - static void decrement(IntSequence& v, const IntSequence& nvmx); - static int getOffset(const IntSequence& v, int nv); - static int getOffset(const IntSequence& v, const IntSequence& nvmx); -}; - -@ This is an abstraction for folded tensor. It only provides a method -|unfold|, which returns the unfolded version of the same symmetry, and -static methods for decrementing indices. - -We also provide static methods for decrementing the |IntSequence| in -folded fashion and also calculating an offset for a given -|IntSequence|. However, this is relatively complex calculation, so -this should be avoided if possible. - -@<|FTensor| class declaration@>= -class FTensor : public Tensor { -public:@; - FTensor(indor io, const IntSequence& last, int r, int c, int d) - : Tensor(io, last, r, c, d)@+ {} - FTensor(const FTensor& ft) - : Tensor(ft)@+ {} - FTensor(int first_row, int num, FTensor& t) - : Tensor(first_row, num, t)@+ {} - virtual ~FTensor()@+ {} - virtual UTensor& unfold() const =0; - - static void decrement(IntSequence& v, int nv); - static int getOffset(const IntSequence& v, int nv) - {@+IntSequence vtmp(v);@+ return getOffsetRecurse(vtmp, nv);@+} -private:@; - static int getOffsetRecurse(IntSequence& v, int nv); -}; - -@ End of {\tt tensor.h} file. diff --git a/dynare++/tl/cc/tl_exception.hh b/dynare++/tl/cc/tl_exception.hh new file mode 100644 index 000000000..7b01b140e --- /dev/null +++ b/dynare++/tl/cc/tl_exception.hh @@ -0,0 +1,74 @@ +// Copyright 2004, Ondra Kamenik + +// Exception. + +/* Within the code we often check some state of variables, typically + preconditions or postconditions. If the state is not as required, it + is worthless to continue, since this means some fatal error in + algorithms. In this case we raise an exception which can be caught at + some higher level. This header file defines a simple infrastructure + for this. */ + +#ifndef TL_EXCEPTION_H +#define TL_EXCEPTION_H + +#include +#include + +/* The basic idea of raising an exception if some condition fails is + that the conditions is checked only if required. We define global + |TL_DEBUG| macro which is integer and says, how many debug messages + the programm has to emit. We also define |TL_DEBUG_EXCEPTION| which + says, for what values of |TL_DEBUG| we will check for conditions of + the exceptions. If the |TL_DEBUG| is equal or higher than + |TL_DEBUG_EXCEPTION|, the exception conditions are checked. + + We define |TL_RAISE|, and |TL_RAISE_IF| macros which throw an instance + of |TLException| if |TL_DEBUG >= TL_DEBUG_EXCEPTION|. The first is + unconditional throw, the second is conditioned by a given + expression. Note that if |TL_DEBUG < TL_DEBUG_EXCEPTION| then the code + is compiled but evaluation of the condition is passed. If code is + optimized, the optimizer also passes evaluation of |TL_DEBUG| and + |TL_DEBUG_EXCEPTION| comparison (I hope). + + We provide default values for |TL_DEBUG| and |TL_DEBUG_EXCEPTION|. */ + +#ifndef TL_DEBUG_EXCEPTION +# define TL_DEBUG_EXCEPTION 1 +#endif + +#ifndef TL_DEBUG +# define TL_DEBUG 0 +#endif + +#define TL_RAISE(mes) \ + if (TL_DEBUG >= TL_DEBUG_EXCEPTION) throw TLException(__FILE__, __LINE__, mes); + +#define TL_RAISE_IF(expr, mes) \ + if (TL_DEBUG >= TL_DEBUG_EXCEPTION && (expr)) throw TLException(__FILE__, __LINE__, mes); + +/* Primitive exception class containing file name, line number and message. */ + +class TLException +{ + char fname[50]; + int lnum; + char message[500]; +public: + TLException(const char *f, int l, const char *mes) + { + strncpy(fname, f, 50); fname[49] = '\0'; + strncpy(message, mes, 500); message[499] = '\0'; + lnum = l; + } + virtual ~TLException() + { + } + virtual void + print() const + { + printf("At %s:%d:%s\n", fname, lnum, message); + } +}; + +#endif diff --git a/dynare++/tl/cc/tl_exception.hweb b/dynare++/tl/cc/tl_exception.hweb deleted file mode 100644 index 684efc49f..000000000 --- a/dynare++/tl/cc/tl_exception.hweb +++ /dev/null @@ -1,79 +0,0 @@ -@q $Id: tl_exception.hweb 332 2005-07-15 13:41:48Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@*2 Exception. Start of {\tt tl\_exception.h} file. - -Within the code we often check some state of variables, typically -preconditions or postconditions. If the state is not as required, it -is worthless to continue, since this means some fatal error in -algorithms. In this case we raise an exception which can be caught at -some higher level. This header file defines a simple infrastructure -for this. - -@s TLException int -@c -#ifndef TL_EXCEPTION_H -#define TL_EXCEPTION_H - -#include -#include - -@; - -#endif - -@ The basic idea of raising an exception if some condition fails is -that the conditions is checked only if required. We define global -|TL_DEBUG| macro which is integer and says, how many debug messages -the programm has to emit. We also define |TL_DEBUG_EXCEPTION| which -says, for what values of |TL_DEBUG| we will check for conditions of -the exceptions. If the |TL_DEBUG| is equal or higher than -|TL_DEBUG_EXCEPTION|, the exception conditions are checked. - -We define |TL_RAISE|, and |TL_RAISE_IF| macros which throw an instance -of |TLException| if |TL_DEBUG >= TL_DEBUG_EXCEPTION|. The first is -unconditional throw, the second is conditioned by a given -expression. Note that if |TL_DEBUG < TL_DEBUG_EXCEPTION| then the code -is compiled but evaluation of the condition is passed. If code is -optimized, the optimizer also passes evaluation of |TL_DEBUG| and -|TL_DEBUG_EXCEPTION| comparison (I hope). - -We provide default values for |TL_DEBUG| and |TL_DEBUG_EXCEPTION|. - -@= -#ifndef TL_DEBUG_EXCEPTION -#define TL_DEBUG_EXCEPTION 1 -#endif - -#ifndef TL_DEBUG -#define TL_DEBUG 0 -#endif - -#define TL_RAISE(mes) \ -if (TL_DEBUG >= TL_DEBUG_EXCEPTION) throw TLException(__FILE__, __LINE__, mes); - -#define TL_RAISE_IF(expr, mes) \ -if (TL_DEBUG >= TL_DEBUG_EXCEPTION && (expr)) throw TLException(__FILE__, __LINE__, mes); - -@<|TLException| class definition@>; - -@ Primitive exception class containing file name, line number and message. -@<|TLException| class definition@>= -class TLException { - char fname[50]; - int lnum; - char message[500]; -public:@; - TLException(const char* f, int l, const char* mes) - { - strncpy(fname, f, 50);@+ fname[49] = '\0'; - strncpy(message, mes, 500);@+ message[499] = '\0'; - lnum = l; - } - virtual ~TLException()@+ {} - virtual void print() const - {@+ printf("At %s:%d:%s\n", fname, lnum, message);@+} -}; - - -@ End of {\tt tl\_exception.h} file. diff --git a/dynare++/tl/cc/tl_static.cc b/dynare++/tl/cc/tl_static.cc new file mode 100644 index 000000000..c15937b6f --- /dev/null +++ b/dynare++/tl/cc/tl_static.cc @@ -0,0 +1,82 @@ +// Copyright 2004, Ondra Kamenik + +#include "tl_static.hh" +#include "tl_exception.hh" + +TLStatic tls; + +/* Note that we allow for repeated calls of |init|. This is not normal + and the only purpose of allowing this is the test suite. */ + +TLStatic::TLStatic() +{ + ebundle = NULL; + pbundle = NULL; + ptriang = NULL; +} + +TLStatic::~TLStatic() +{ + if (ebundle) + delete ebundle; + if (pbundle) + delete pbundle; + if (ptriang) + delete ptriang; +} + +void +TLStatic::init(int dim, int nvar) +{ + if (ebundle) + ebundle->generateUpTo(dim); + else + ebundle = new EquivalenceBundle(dim); + + if (pbundle) + pbundle->generateUpTo(dim); + else + pbundle = new PermutationBundle(dim); + + if (ptriang) + delete ptriang; + ptriang = new PascalTriangle(nvar, dim); +} + +/* The coefficients are stored in |data| row by row where a row are + coeffs with the same $k$. + + We first initialize the first row with ones. Then for each other row + we initialize the first item to one, and other items are a sum of + coefficients of $n-1$ which is in code |i+j-1|. */ + +PascalTriangle::PascalTriangle(int n, int k) + : data(new int[(n+1)*(k+1)]), kmax(k), nmax(n) +{ + for (int i = 0; i <= n; i++) + data[i] = 1; + for (int j = 1; j <= k; j++) + { + data[j*(nmax+1)] = 1; + for (int i = 1; i <= n; i++) + data[j*(nmax+1)+i] = noverk(i+j-1, j) + noverk(i+j-1, j-1); + } +} + +/* Clear. Recall, that there are |nmax+1| items in a row. */ + +int +PascalTriangle::noverk(int n, int k) const +{ + TL_RAISE_IF(k > n || n < 0, + "Wrong arguments for PascalTriangle::noverk"); + + if (k <= kmax && n-k <= nmax) + return data[k*(nmax+1)+n-k]; + + if (n-k <= kmax && k <= nmax) + return data[(n-k)*(nmax+1)+k]; + + TL_RAISE("n or k out of range in PascalTriangle::noverk"); + return 0; +} diff --git a/dynare++/tl/cc/tl_static.cweb b/dynare++/tl/cc/tl_static.cweb deleted file mode 100644 index 778715465..000000000 --- a/dynare++/tl/cc/tl_static.cweb +++ /dev/null @@ -1,89 +0,0 @@ -@q $Id: tl_static.cweb 200 2005-05-12 12:28:19Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@ Start of {\tt tl\_static.cpp} file. -@c -#include "tl_static.h" -#include "tl_exception.h" - -TLStatic tls; -@<|TLStatic| methods@>; -@<|PascalTriangle| constructor code@>; -@<|PascalTriangle::noverk| code@>; - -@ Note that we allow for repeated calls of |init|. This is not normal -and the only purpose of allowing this is the test suite. - -@<|TLStatic| methods@>= -TLStatic::TLStatic() -{ - ebundle = NULL; - pbundle = NULL; - ptriang = NULL; -} - -TLStatic::~TLStatic() -{ - if (ebundle) - delete ebundle; - if (pbundle) - delete pbundle; - if (ptriang) - delete ptriang; -} - -void TLStatic::init(int dim, int nvar) -{ - if (ebundle) - ebundle->generateUpTo(dim); - else - ebundle = new EquivalenceBundle(dim); - - if (pbundle) - pbundle->generateUpTo(dim); - else - pbundle = new PermutationBundle(dim); - - if (ptriang) - delete ptriang; - ptriang = new PascalTriangle(nvar, dim); -} - -@ The coefficients are stored in |data| row by row where a row are -coeffs with the same $k$. - -We first initialize the first row with ones. Then for each other row -we initialize the first item to one, and other items are a sum of -coefficients of $n-1$ which is in code |i+j-1|. - -@<|PascalTriangle| constructor code@>= -PascalTriangle::PascalTriangle(int n, int k) - : data(new int[(n+1)*(k+1)]), kmax(k), nmax(n) -{ - for (int i = 0; i <= n; i++) - data[i] = 1; - for (int j = 1; j <= k; j++) { - data[j*(nmax+1)] = 1; - for (int i = 1; i <= n; i++) - data[j*(nmax+1)+i] = noverk(i+j-1,j) + noverk(i+j-1,j-1); - } -} - -@ Clear. Recall, that there are |nmax+1| items in a row. -@<|PascalTriangle::noverk| code@>= -int PascalTriangle::noverk(int n, int k) const -{ - TL_RAISE_IF(k > n || n < 0, - "Wrong arguments for PascalTriangle::noverk"); - - if (k <= kmax && n-k <= nmax) - return data[k*(nmax+1)+n-k]; - - if (n-k <= kmax && k <= nmax) - return data[(n-k)*(nmax+1)+k]; - - TL_RAISE("n or k out of range in PascalTriangle::noverk"); - return 0; -} - -@ End of {\tt tl\_static.cpp} file. diff --git a/dynare++/tl/cc/tl_static.hh b/dynare++/tl/cc/tl_static.hh new file mode 100644 index 000000000..3d66fb53e --- /dev/null +++ b/dynare++/tl/cc/tl_static.hh @@ -0,0 +1,55 @@ +// Copyright 2004, Ondra Kamenik + +// Tensor library static data. + +/* The purpose of this file is to make a unique static variable which + would contain all other static variables and be responsible for their + correct initialization and destruction. The variables include an + equivalence bundle and a Pascal triangle for binomial + coefficients. Both depend on dimension of the problem, and maximum + number of variables. + + So we declare static |tls| variable of type |TLStatic| encapsulating + the variables. The |tls| must be initialized at the beginning of + the program, as dimension and number of variables is known. + + Also we define a class for Pascal triangle. */ + +#ifndef TL_STATIC_H +#define TL_STATIC_H + +#include "equivalence.hh" +#include "permutation.hh" + +/* Pascal triangle is a storage for binomial coefficients. We store in + |data| array the coefficients of rectangle starting at $\pmatrix{0\cr + 0}$, and ending $\pmatrix{nmax+kmax\cr kmax}$. */ + +class PascalTriangle +{ + int *data; + int kmax; + int nmax; +public: + PascalTriangle(int n, int k); + ~PascalTriangle() + { + delete [] data; + } + int noverk(int n, int k) const; +}; + +struct TLStatic +{ + EquivalenceBundle *ebundle; + PermutationBundle *pbundle; + PascalTriangle *ptriang; + + TLStatic(); + ~TLStatic(); + void init(int dim, int nvar); +}; + +extern TLStatic tls; + +#endif diff --git a/dynare++/tl/cc/tl_static.hweb b/dynare++/tl/cc/tl_static.hweb deleted file mode 100644 index 83d37004d..000000000 --- a/dynare++/tl/cc/tl_static.hweb +++ /dev/null @@ -1,62 +0,0 @@ -@q $Id: tl_static.hweb 148 2005-04-19 15:12:26Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@*2 Tensor library static data. Start of {\tt tl\_static.h} file. - -The purpose of this file is to make a unique static variable which -would contain all other static variables and be responsible for their -correct initialization and destruction. The variables include an -equivalence bundle and a Pascal triangle for binomial -coefficients. Both depend on dimension of the problem, and maximum -number of variables. - -So we declare static |tls| variable of type |TLStatic| encapsulating -the variables. The |tls| must be initialized at the beginning of -the program, as dimension and number of variables is known. - -Also we define a class for Pascal triangle. - -@c -#ifndef TL_STATIC_H -#define TL_STATIC_H - -#include "equivalence.h" -#include "permutation.h" - -@<|PascalTriangle| class declaration@>; -@<|TLStatic| class declaration@>; -extern TLStatic tls; - -#endif - -@ Pascal triangle is a storage for binomial coefficients. We store in -|data| array the coefficients of rectangle starting at $\pmatrix{0\cr -0}$, and ending $\pmatrix{nmax+kmax\cr kmax}$. - -@<|PascalTriangle| class declaration@>= -class PascalTriangle { - int* data; - int kmax; - int nmax; -public:@; - PascalTriangle(int n, int k); - ~PascalTriangle() - {@+ delete [] data;@+} - int noverk(int n, int k) const; -}; - - -@ -@<|TLStatic| class declaration@>= -struct TLStatic { - EquivalenceBundle* ebundle; - PermutationBundle* pbundle; - PascalTriangle* ptriang; - - TLStatic(); - ~TLStatic(); - void init(int dim, int nvar); -}; - - -@ End of {\tt tl\_static.h} file. diff --git a/dynare++/tl/cc/twod_matrix.cc b/dynare++/tl/cc/twod_matrix.cc new file mode 100644 index 000000000..04a32107b --- /dev/null +++ b/dynare++/tl/cc/twod_matrix.cc @@ -0,0 +1,118 @@ +// Copyright 2004, Ondra Kamenik + +#include "twod_matrix.hh" +#include "tl_exception.hh" + +ConstTwoDMatrix::ConstTwoDMatrix(const TwoDMatrix &m) + : ConstGeneralMatrix(m) +{ +} + +ConstTwoDMatrix::ConstTwoDMatrix(const TwoDMatrix &m, int first_col, int num) + : ConstGeneralMatrix(m, 0, first_col, m.nrows(), num) +{ +} + +ConstTwoDMatrix::ConstTwoDMatrix(const ConstTwoDMatrix &m, int first_col, int num) + : ConstGeneralMatrix(m, 0, first_col, m.nrows(), num) +{ +} + +ConstTwoDMatrix::ConstTwoDMatrix(int first_row, int num, const TwoDMatrix &m) + : ConstGeneralMatrix(m, first_row, 0, num, m.ncols()) +{ +} + +ConstTwoDMatrix::ConstTwoDMatrix(int first_row, int num, const ConstTwoDMatrix &m) + : ConstGeneralMatrix(m, first_row, 0, num, m.ncols()) +{ +} + +void +ConstTwoDMatrix::writeMat(mat_t *fd, const char *vname) const +{ +#if MATIO_MAJOR_VERSION > 1 || (MATIO_MAJOR_VERSION == 1 && MATIO_MINOR_VERSION >= 5) + size_t dims[2]; + const matio_compression compression = MAT_COMPRESSION_NONE; +#else + int dims[2]; + const int compression = COMPRESSION_NONE; +#endif + dims[0] = nrows(); + dims[1] = ncols(); + double *data = new double[nrows()*ncols()]; + + for (int j = 0; j < ncols(); j++) + for (int i = 0; i < nrows(); i++) + data[j*nrows()+i] = get(i, j); + + matvar_t *v = Mat_VarCreate(vname, MAT_C_DOUBLE, MAT_T_DOUBLE, 2, dims, data, 0); + + Mat_VarWrite(fd, v, compression); + + Mat_VarFree(v); + delete[] data; +} + +void +TwoDMatrix::copyRow(int from, int to) +{ + if (from != to) + copyRow(ConstTwoDMatrix(*this), from, to); +} + +void +TwoDMatrix::copyRow(const ConstTwoDMatrix &m, int from, int to) +{ + ConstVector fr_row(from, m); + Vector to_row(to, *this); + to_row = fr_row; +} + +void +TwoDMatrix::addRow(double d, const ConstTwoDMatrix &m, int from, int to) +{ + ConstVector fr_row(from, m); + Vector to_row(to, *this); + to_row.add(d, fr_row); +} + +void +TwoDMatrix::copyColumn(int from, int to) +{ + if (from != to) + copyColumn(ConstTwoDMatrix(*this), from, to); +} + +void +TwoDMatrix::copyColumn(const ConstTwoDMatrix &m, int from, int to) +{ + ConstVector fr_col(m, from); + Vector to_col(*this, to); + to_col = fr_col; +} + +void +TwoDMatrix::addColumn(double d, const ConstTwoDMatrix &m, int from, int to) +{ + ConstVector fr_col(m, from); + Vector to_col(*this, to); + to_col.add(d, fr_col); +} + +void +TwoDMatrix::save(const char *fname) const +{ + FILE *fd; + if (NULL == (fd = fopen(fname, "w"))) + { + TL_RAISE("Cannot open file for writing in TwoDMatrix::save"); + } + for (int row = 0; row < nrows(); row++) + { + for (int col = 0; col < ncols(); col++) + fprintf(fd, " %20.10g", get(row, col)); + fprintf(fd, "\n"); + } + fclose(fd); +} diff --git a/dynare++/tl/cc/twod_matrix.cweb b/dynare++/tl/cc/twod_matrix.cweb deleted file mode 100644 index 1cbf6b2bb..000000000 --- a/dynare++/tl/cc/twod_matrix.cweb +++ /dev/null @@ -1,121 +0,0 @@ -@q $Id: twod_matrix.cweb 148 2005-04-19 15:12:26Z kamenik $ @> -@q Copyright 2004, Ondra Kamenik @> - -@ Start of {\tt twod\_matrix.cpp} file. -@c -#include "twod_matrix.h" -#include "tl_exception.h" - - -@<|ConstTwoDMatrix| constructors@>; -@<|ConstTwoDMatrix::writeMat| code@>; -@<|TwoDMatrix| row methods code@>; -@<|TwoDMatrix| column methods code@>; -@<|TwoDMatrix::save| code@>; - -@ -@<|ConstTwoDMatrix| constructors@>= -ConstTwoDMatrix::ConstTwoDMatrix(const TwoDMatrix& m) - : ConstGeneralMatrix(m)@+ {} - -ConstTwoDMatrix::ConstTwoDMatrix(const TwoDMatrix& m, int first_col, int num) - : ConstGeneralMatrix(m, 0, first_col, m.nrows(), num)@+ {} - -ConstTwoDMatrix::ConstTwoDMatrix(const ConstTwoDMatrix& m, int first_col, int num) - : ConstGeneralMatrix(m, 0, first_col, m.nrows(), num)@+ {} - -ConstTwoDMatrix::ConstTwoDMatrix(int first_row, int num, const TwoDMatrix& m) - : ConstGeneralMatrix(m, first_row, 0, num, m.ncols())@+ {} - -ConstTwoDMatrix::ConstTwoDMatrix(int first_row, int num, const ConstTwoDMatrix& m) - : ConstGeneralMatrix(m, first_row, 0, num, m.ncols())@+ {} - -@ -@<|ConstTwoDMatrix::writeMat| code@>= -void ConstTwoDMatrix::writeMat(mat_t* fd, const char* vname) const -{ -#if MATIO_MAJOR_VERSION > 1 || (MATIO_MAJOR_VERSION == 1 && MATIO_MINOR_VERSION >= 5) - size_t dims[2]; - const matio_compression compression = MAT_COMPRESSION_NONE; -#else - int dims[2]; - const int compression = COMPRESSION_NONE; -#endif - dims[0] = nrows(); - dims[1] = ncols(); - double *data = new double[nrows()*ncols()]; - - for (int j = 0; j < ncols(); j++) - for (int i = 0; i < nrows(); i++) - data[j*nrows()+i] = get(i,j); - - matvar_t *v = Mat_VarCreate(vname, MAT_C_DOUBLE, MAT_T_DOUBLE, 2, dims, data, 0); - - Mat_VarWrite(fd, v, compression); - - Mat_VarFree(v); - delete[] data; -} - -@ -@<|TwoDMatrix| row methods code@>= -void TwoDMatrix::copyRow(int from, int to) -{ - if (from != to) - copyRow(ConstTwoDMatrix(*this), from, to); -} - -void TwoDMatrix::copyRow(const ConstTwoDMatrix& m, int from, int to) -{ - ConstVector fr_row(from, m); - Vector to_row(to, *this); - to_row = fr_row; -} - -void TwoDMatrix::addRow(double d, const ConstTwoDMatrix& m, int from, int to) -{ - ConstVector fr_row(from, m); - Vector to_row(to, *this); - to_row.add(d, fr_row); -} - - -@ -@<|TwoDMatrix| column methods code@>= -void TwoDMatrix::copyColumn(int from, int to) -{ - if (from != to) - copyColumn(ConstTwoDMatrix(*this), from, to); -} - -void TwoDMatrix::copyColumn(const ConstTwoDMatrix& m, int from, int to) -{ - ConstVector fr_col(m, from); - Vector to_col(*this, to); - to_col = fr_col; -} - -void TwoDMatrix::addColumn(double d, const ConstTwoDMatrix& m, int from, int to) -{ - ConstVector fr_col(m, from); - Vector to_col(*this, to); - to_col.add(d, fr_col); -} - -@ -@<|TwoDMatrix::save| code@>= -void TwoDMatrix::save(const char* fname) const -{ - FILE* fd; - if (NULL==(fd = fopen(fname,"w"))) { - TL_RAISE("Cannot open file for writing in TwoDMatrix::save"); - } - for (int row = 0; row < nrows(); row++) { - for (int col = 0; col < ncols(); col++) - fprintf(fd, " %20.10g", get(row, col)); - fprintf(fd, "\n"); - } - fclose(fd); -} - -@ End of {\tt twod\_matrix.cpp} file. diff --git a/dynare++/tl/cc/twod_matrix.hh b/dynare++/tl/cc/twod_matrix.hh new file mode 100644 index 000000000..90d9591d7 --- /dev/null +++ b/dynare++/tl/cc/twod_matrix.hh @@ -0,0 +1,192 @@ +// Copyright (C) 2004-2011, Ondra Kamenik + +// Matrix interface. + +/* Here we make an interface to 2-dimensional matrix defined in the + Sylvester module. That abstraction provides an interface to BLAS. The + main purpose of this file is to only make its subclass in order to + keep the tensor library and Sylvester module independent. So here is + mainly renaming of methods. + + Similarly as in the Sylvester module we declare two classes + |TwoDMatrix| and |ConstTwoDMatrix|. The only purpose of the latter is + to allow submatrix construction from const reference arguments. */ + +#ifndef TWOD_MATRIX_H +#define TWOD_MATRIX_H + +#include "GeneralMatrix.h" + +#include +#include + +class TwoDMatrix; + +/* We make two obvious constructors, and then a constructor making + submatrix of subsequent columns. We also rename + |GeneralMatrix::numRows()| and |GeneralMatrix::numCols()|. */ + +class ConstTwoDMatrix : public ConstGeneralMatrix +{ +public: + ConstTwoDMatrix(int m, int n, const double *d) + : ConstGeneralMatrix(d, m, n) + { + } + ConstTwoDMatrix(const TwoDMatrix &m); + ConstTwoDMatrix(const TwoDMatrix &m, int first_col, int num); + ConstTwoDMatrix(const ConstTwoDMatrix &m, int first_col, int num); + ConstTwoDMatrix(int first_row, int num, const TwoDMatrix &m); + ConstTwoDMatrix(int first_row, int num, const ConstTwoDMatrix &m); + ConstTwoDMatrix(const ConstTwoDMatrix &m, int first_row, int first_col, int rows, int cols) + : ConstGeneralMatrix(m, first_row, first_col, rows, cols) + { + } + virtual ~ConstTwoDMatrix() + { + } + + int + nrows() const + { + return numRows(); + } + int + ncols() const + { + return numCols(); + } + void writeMat(mat_t *fd, const char *vname) const; +}; + +/* Here we do the same as for |ConstTwoDMatrix| plus define + methods for copying and adding rows and columns. + + Also we have |save| method which dumps the matrix to a file with a + given name. The file can be read by Scilab {\tt fscanfMat} function. */ + +class TwoDMatrix : public GeneralMatrix +{ +public: + TwoDMatrix(int r, int c) + : GeneralMatrix(r, c) + { + } + TwoDMatrix(int r, int c, double *d) + : GeneralMatrix(d, r, c) + { + } + TwoDMatrix(int r, int c, const double *d) + : GeneralMatrix(d, r, c) + { + } + TwoDMatrix(const GeneralMatrix &m) + : GeneralMatrix(m) + { + } + TwoDMatrix(const GeneralMatrix &m, const char *dummy) + : GeneralMatrix(m, dummy) + { + } + TwoDMatrix(const TwoDMatrix &m, int first_col, int num) + : GeneralMatrix(m, 0, first_col, m.numRows(), num) + { + } + TwoDMatrix(TwoDMatrix &m, int first_col, int num) + : GeneralMatrix(m, 0, first_col, m.numRows(), num) + { + } + TwoDMatrix(int first_row, int num, const TwoDMatrix &m) + : GeneralMatrix(m, first_row, 0, num, m.ncols()) + { + } + TwoDMatrix(int first_row, int num, TwoDMatrix &m) + : GeneralMatrix(m, first_row, 0, num, m.ncols()) + { + } + TwoDMatrix(TwoDMatrix &m, int first_row, int first_col, int rows, int cols) + : GeneralMatrix(m, first_row, first_col, rows, cols) + { + } + TwoDMatrix(const TwoDMatrix &m, int first_row, int first_col, int rows, int cols) + : GeneralMatrix(m, first_row, first_col, rows, cols) + { + } + TwoDMatrix(const ConstTwoDMatrix &a, const ConstTwoDMatrix &b) + : GeneralMatrix(a, b) + { + } + virtual ~TwoDMatrix() + { + } + + int + nrows() const + { + return numRows(); + } + int + ncols() const + { + return numCols(); + } + + // |TwoDMatrix| row methods declarations + void copyRow(int from, int to); + void copyRow(const ConstTwoDMatrix &m, int from, int to); + void + copyRow(const TwoDMatrix &m, int from, int to) + { + copyRow(ConstTwoDMatrix(m), from, to); + } + void + addRow(const ConstTwoDMatrix &m, int from, int to) + { + addRow(1.0, m, from, to); + } + void + addRow(const TwoDMatrix &m, int from, int to) + { + addRow(1.0, ConstTwoDMatrix(m), from, to); + } + void addRow(double d, const ConstTwoDMatrix &m, int from, int to); + void + addRow(double d, const TwoDMatrix &m, int from, int to) + { + addRow(d, ConstTwoDMatrix(m), from, to); + } + + // |TwoDMatrix| column methods declarations + void copyColumn(int from, int to); + void copyColumn(const ConstTwoDMatrix &m, int from, int to); + void + copyColumn(const TwoDMatrix &m, int from, int to) + { + copyColumn(ConstTwoDMatrix(m), from, to); + } + void + addColumn(const ConstTwoDMatrix &m, int from, int to) + { + addColumn(1.0, ConstTwoDMatrix(m), from, to); + } + void + addColumn(const TwoDMatrix &m, int from, int to) + { + addColumn(1.0, ConstTwoDMatrix(m), from, to); + } + void addColumn(double d, const ConstTwoDMatrix &m, int from, int to); + void + addColumn(double d, const TwoDMatrix &m, int from, int to) + { + addColumn(d, ConstTwoDMatrix(m), from, to); + } + + void save(const char *fname) const; + void + writeMat(mat_t *fd, const char *vname) const + { + ConstTwoDMatrix(*this).writeMat(fd, vname); + } +}; + +#endif diff --git a/dynare++/tl/cc/twod_matrix.hweb b/dynare++/tl/cc/twod_matrix.hweb deleted file mode 100644 index 9e622189d..000000000 --- a/dynare++/tl/cc/twod_matrix.hweb +++ /dev/null @@ -1,139 +0,0 @@ -@q Copyright (C) 2004-2011, Ondra Kamenik @> - -@*2 Matrix interface. Start of {\tt twod\_matrix.h} file. - -Here we make an interface to 2-dimensional matrix defined in the -Sylvester module. That abstraction provides an interface to BLAS. The -main purpose of this file is to only make its subclass in order to -keep the tensor library and Sylvester module independent. So here is -mainly renaming of methods. - -Similarly as in the Sylvester module we declare two classes -|TwoDMatrix| and |ConstTwoDMatrix|. The only purpose of the latter is -to allow submatrix construction from const reference arguments. - -@s GeneralMatrix int -@s ConstGeneralMatrix int -@s Vector int -@s ConstVector int -@s TwoDMatrix int -@s ConstTwoDMatrix int - -@c -#ifndef TWOD_MATRIX_H -#define TWOD_MATRIX_H - -#include "GeneralMatrix.h" - -#include -#include - -class TwoDMatrix; -@<|ConstTwoDMatrix| class declaration@>; -@<|TwoDMatrix| class declaration@>; - -#endif - - -@ We make two obvious constructors, and then a constructor making -submatrix of subsequent columns. We also rename -|GeneralMatrix::numRows()| and |GeneralMatrix::numCols()|. - -@<|ConstTwoDMatrix| class declaration@>= -class ConstTwoDMatrix : public ConstGeneralMatrix { -public:@/ - ConstTwoDMatrix(int m, int n, const double* d) - : ConstGeneralMatrix(d, m, n)@+ {}@; - ConstTwoDMatrix(const TwoDMatrix& m); - ConstTwoDMatrix(const TwoDMatrix& m, int first_col, int num); - ConstTwoDMatrix(const ConstTwoDMatrix& m, int first_col, int num); - ConstTwoDMatrix(int first_row, int num, const TwoDMatrix& m); - ConstTwoDMatrix(int first_row, int num, const ConstTwoDMatrix& m); - ConstTwoDMatrix(const ConstTwoDMatrix& m, int first_row, int first_col, int rows, int cols) - : ConstGeneralMatrix(m, first_row, first_col, rows, cols)@+ {} - virtual ~ConstTwoDMatrix()@+ {} -@# - int nrows() const - {@+ return numRows();@+} - int ncols() const - {@+ return numCols();@+} - void writeMat(mat_t* fd, const char* vname) const; -}; - -@ Here we do the same as for |ConstTwoDMatrix| plus define -methods for copying and adding rows and columns. - -Also we have |save| method which dumps the matrix to a file with a -given name. The file can be read by Scilab {\tt fscanfMat} function. - -@<|TwoDMatrix| class declaration@>= -class TwoDMatrix : public GeneralMatrix { -public:@/ - TwoDMatrix(int r, int c) - : GeneralMatrix(r, c)@+ {}@; - TwoDMatrix(int r, int c, double* d) - : GeneralMatrix(d, r, c)@+ {}@; - TwoDMatrix(int r, int c, const double* d) - : GeneralMatrix(d, r, c)@+ {}@; - TwoDMatrix(const GeneralMatrix& m) - : GeneralMatrix(m)@+ {}@; - TwoDMatrix(const GeneralMatrix& m, const char* dummy) - : GeneralMatrix(m, dummy)@+ {}@; - TwoDMatrix(const TwoDMatrix& m, int first_col, int num) - : GeneralMatrix(m, 0, first_col, m.numRows(), num)@+ {}@; - TwoDMatrix(TwoDMatrix& m, int first_col, int num) - : GeneralMatrix(m, 0, first_col, m.numRows(), num)@+ {}@; - TwoDMatrix(int first_row, int num, const TwoDMatrix& m) - : GeneralMatrix(m, first_row, 0, num, m.ncols())@+ {} - TwoDMatrix(int first_row, int num, TwoDMatrix& m) - : GeneralMatrix(m, first_row, 0, num, m.ncols())@+ {} - TwoDMatrix(TwoDMatrix& m, int first_row, int first_col, int rows, int cols) - : GeneralMatrix(m, first_row, first_col, rows, cols)@+ {} - TwoDMatrix(const TwoDMatrix& m, int first_row, int first_col, int rows, int cols) - : GeneralMatrix(m, first_row, first_col, rows, cols)@+ {} - TwoDMatrix(const ConstTwoDMatrix& a, const ConstTwoDMatrix& b) - : GeneralMatrix(a, b)@+ {} - virtual ~TwoDMatrix()@+ {} -@# - int nrows() const - {@+ return numRows();@+} - int ncols() const - {@+ return numCols();@+} -@# - @<|TwoDMatrix| row methods declarations@>; - @<|TwoDMatrix| column methods declarations@>; - void save(const char* fname) const; - void writeMat(mat_t* fd, const char* vname) const - {@+ ConstTwoDMatrix(*this).writeMat(fd, vname);@+} -}; - -@ -@<|TwoDMatrix| row methods declarations@>= - void copyRow(int from, int to); - void copyRow(const ConstTwoDMatrix& m, int from, int to); - void copyRow(const TwoDMatrix& m, int from, int to) - {@+ copyRow(ConstTwoDMatrix(m), from, to);@+} - void addRow(const ConstTwoDMatrix& m, int from, int to) - {@+ addRow(1.0, m, from, to);@+} - void addRow(const TwoDMatrix& m, int from, int to) - {@+ addRow(1.0, ConstTwoDMatrix(m), from, to);@+} - void addRow(double d, const ConstTwoDMatrix& m, int from, int to); - void addRow(double d, const TwoDMatrix& m, int from, int to) - {@+ addRow(d, ConstTwoDMatrix(m), from, to);@+} - - -@ -@<|TwoDMatrix| column methods declarations@>= - void copyColumn(int from, int to); - void copyColumn(const ConstTwoDMatrix& m, int from, int to); - void copyColumn(const TwoDMatrix& m, int from, int to) - {@+ copyColumn(ConstTwoDMatrix(m), from, to);@+} - void addColumn(const ConstTwoDMatrix& m, int from, int to) - {@+ addColumn(1.0, ConstTwoDMatrix(m), from, to);@+} - void addColumn(const TwoDMatrix& m, int from, int to) - {@+ addColumn(1.0, ConstTwoDMatrix(m), from, to);@+} - void addColumn(double d, const ConstTwoDMatrix& m, int from, int to); - void addColumn(double d, const TwoDMatrix& m, int from, int to) - {@+ addColumn(d, ConstTwoDMatrix(m), from, to);@+} - -@ End of {\tt twod\_matrix.h} file. diff --git a/dynare++/tl/testing/Makefile.am b/dynare++/tl/testing/Makefile.am index 9c8b78753..a30233def 100644 --- a/dynare++/tl/testing/Makefile.am +++ b/dynare++/tl/testing/Makefile.am @@ -1,6 +1,6 @@ check_PROGRAMS = tests -tests_SOURCES = factory.cpp factory.h monoms.cpp monoms.h tests.cpp +tests_SOURCES = factory.cc factory.hh monoms.cc monoms.hh tests.cc tests_CPPFLAGS = -I../cc -I../../sylv/cc tests_CXXFLAGS = $(AM_CXXFLAGS) $(PTHREAD_CFLAGS) tests_LDFLAGS = $(AM_LDFLAGS) $(LDFLAGS_MATIO) diff --git a/dynare++/tl/testing/factory.cc b/dynare++/tl/testing/factory.cc new file mode 100644 index 000000000..e2bf9fbcd --- /dev/null +++ b/dynare++/tl/testing/factory.cc @@ -0,0 +1,54 @@ +/* $Id: factory.cpp 148 2005-04-19 15:12:26Z kamenik $ */ +/* Copyright 2004, Ondra Kamenik */ + +#include "factory.hh" + +#include +#include + +void +Factory::init(const Symmetry &s, const IntSequence &nvs) +{ + IntSequence sym(s); + long int seed = sym[0]; + seed = 256*seed + nvs[0]; + if (sym.size() > 1) + seed = 256*seed + sym[1]; + if (nvs.size() > 1) + seed = 256*seed + nvs[0]; + srand48(seed); +} + +void +Factory::init(int dim, int nv) +{ + long int seed = dim; + seed = 256*seed + nv; + srand48(seed); +} + +double +Factory::get() const +{ + return 1.0*(drand48()-0.5); +} + +void +Factory::fillMatrix(TwoDMatrix &m) const +{ + Vector &d = m.getData(); + for (int i = 0; i < d.length(); i++) + d[i] = get(); +} + +Vector * +Factory::makeVector(int n) +{ + init(n, n*n); + + Vector *v = new Vector(n); + for (int i = 0; i < n; i++) + (*v)[i] = get(); + + return v; +} diff --git a/dynare++/tl/testing/factory.cpp b/dynare++/tl/testing/factory.cpp deleted file mode 100644 index fa6116607..000000000 --- a/dynare++/tl/testing/factory.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* $Id: factory.cpp 148 2005-04-19 15:12:26Z kamenik $ */ -/* Copyright 2004, Ondra Kamenik */ - -#include "factory.h" - -#include -#include - -void Factory::init(const Symmetry& s, const IntSequence& nvs) -{ - IntSequence sym(s); - long int seed = sym[0]; - seed = 256*seed + nvs[0]; - if (sym.size() > 1) - seed = 256*seed + sym[1]; - if (nvs.size() > 1) - seed = 256*seed + nvs[0]; - srand48(seed); -} - -void Factory::init(int dim, int nv) -{ - long int seed = dim; - seed = 256*seed + nv; - srand48(seed); -} - -double Factory::get() const -{ - return 1.0*(drand48()-0.5); -} - -void Factory::fillMatrix(TwoDMatrix& m) const -{ - Vector& d = m.getData(); - for (int i = 0; i < d.length(); i++) - d[i] = get(); -} - -Vector* Factory::makeVector(int n) -{ - init(n, n*n); - - Vector* v = new Vector(n); - for (int i = 0; i < n; i++) - (*v)[i] = get(); - - return v; -} diff --git a/dynare++/tl/testing/factory.h b/dynare++/tl/testing/factory.hh similarity index 92% rename from dynare++/tl/testing/factory.h rename to dynare++/tl/testing/factory.hh index 89f0b6be8..7550d3f11 100644 --- a/dynare++/tl/testing/factory.h +++ b/dynare++/tl/testing/factory.hh @@ -4,12 +4,12 @@ #ifndef FACTORY_H #define FACTORY_H -#include "symmetry.h" -#include "int_sequence.h" -#include "twod_matrix.h" -#include "equivalence.h" -#include "rfs_tensor.h" -#include "t_container.h" +#include "symmetry.hh" +#include "int_sequence.hh" +#include "twod_matrix.hh" +#include "equivalence.hh" +#include "rfs_tensor.hh" +#include "t_container.hh" class Factory { diff --git a/dynare++/tl/testing/monoms.cc b/dynare++/tl/testing/monoms.cc new file mode 100644 index 000000000..3ead0d510 --- /dev/null +++ b/dynare++/tl/testing/monoms.cc @@ -0,0 +1,542 @@ +/* $Id: monoms.cpp 148 2005-04-19 15:12:26Z kamenik $ */ +/* Copyright 2004, Ondra Kamenik */ + +#include "monoms.hh" +#include "tl_exception.hh" +#include "fs_tensor.hh" + +#include +#include +#include + +IntGenerator intgen; + +void +IntGenerator::init(int nf, int ny, int nv, int nw, int nu, + int mx, double prob) +{ + maxim = mx; + probab = prob; + long int seed = nf; + seed = 256*seed + ny; + seed = 256*seed + nv; + seed = 256*seed + nw; + seed = 256*seed + nu; + srand48(seed); +} + +int +IntGenerator::get() const +{ + double d = drand48(); + int num_inter = (int) (((double) 2*maxim)/(1.0-probab)); + int num_zero_inter = num_inter - 2*maxim; + if (d < ((double) num_zero_inter)/num_inter) + return 0; + return (int) (d*num_inter)-num_zero_inter-maxim; +} + +Monom::Monom(int len) + : IntSequence(len) +{ + for (int i = 0; i < len; i++) + operator[](i) = intgen.get(); +} + +Monom::Monom(int len, int item) + : IntSequence(len, item) +{ +} + +double +Monom::deriv(const IntSequence &vars) const +{ + double res = 1.0; + int first_same_i = 0; + for (int i = 0; i < vars.size(); i++) + { + TL_RAISE_IF(vars[i] < 0 || vars[i] >= size(), + "Wrong variable index in Monom::deriv"); + if (vars[i] != vars[first_same_i]) + first_same_i = i; + int mult = operator[](vars[i]) - (i-first_same_i); + if (mult == 0) + return 0; + res *= mult; + } + return res; +} + +void +Monom::multiplyWith(int ex, const Monom &m) +{ + TL_RAISE_IF(size() != m.size(), + "Wrong sizes of monoms in Monom::multiplyWith"); + if (ex == 0) + return; + for (int i = 0; i < size(); i++) + operator[](i) += m[i]*ex; +} + +void +Monom::print() const +{ + printf("["); + for (int i = 0; i < size(); i++) + printf("%3d", operator[](i)); + printf("]"); +} + +Monom1Vector::Monom1Vector(int nxx, int l) + : nx(nxx), len(l), x(new Monom *[len]) +{ + for (int i = 0; i < len; i++) + { + x[i] = new Monom(nx); + } +} + +Monom1Vector::~Monom1Vector() +{ + for (int i = 0; i < len; i++) + { + delete x[i]; + } + delete [] x; +} + +void +Monom1Vector::deriv(const IntSequence &c, Vector &out) const +{ + TL_RAISE_IF(out.length() != len, + "Wrong length of output vector in Monom1Vector::deriv"); + + for (int i = 0; i < len; i++) + { + out[i] = x[i]->deriv(c); + } +} + +FGSTensor * +Monom1Vector::deriv(int dim) const +{ + FGSTensor *res + = new FGSTensor(len, TensorDimens(Symmetry(dim), IntSequence(1, nx))); + for (Tensor::index it = res->begin(); it != res->end(); ++it) + { + Vector outcol(*res, *it); + deriv(it.getCoor(), outcol); + } + return res; +} + +void +Monom1Vector::print() const +{ + printf("Variables: x(%d)\n", nx); + printf("Rows: %d\n", len); + for (int i = 0; i < len; i++) + { + printf("%2d: ", i); + x[i]->print(); + printf("\n"); + } +} + +Monom2Vector::Monom2Vector(int nyy, int nuu, int l) + : ny(nyy), nu(nuu), len(l), y(new Monom *[len]), u(new Monom *[len]) +{ + for (int i = 0; i < len; i++) + { + y[i] = new Monom(ny); + u[i] = new Monom(nu); + } +} + +Monom2Vector::Monom2Vector(const Monom1Vector &g, const Monom2Vector &xmon) + : ny(xmon.ny), nu(xmon.nu), len(g.len), + y(new Monom *[len]), u(new Monom *[len]) +{ + TL_RAISE_IF(xmon.len != g.nx, + "Wrong number of x's in Monom2Vector constructor"); + + for (int i = 0; i < len; i++) + { + y[i] = new Monom(ny, 0); + u[i] = new Monom(nu, 0); + } + + for (int i = 0; i < len; i++) + { + // multiply from xmon + for (int j = 0; j < g.nx; j++) + { + int ex = g.x[i]->operator[](j); + y[i]->multiplyWith(ex, *(xmon.y[j])); + u[i]->multiplyWith(ex, *(xmon.u[j])); + } + } +} + +Monom2Vector::~Monom2Vector() +{ + for (int i = 0; i < len; i++) + { + delete y[i]; + delete u[i]; + } + delete [] y; + delete [] u; +} + +void +Monom2Vector::deriv(const Symmetry &s, const IntSequence &c, + Vector &out) const +{ + TL_RAISE_IF(out.length() != len, + "Wrong length of output vector in Monom2Vector::deriv"); + TL_RAISE_IF(s.num() != 2, + "Wrong symmetry for Monom2Vector::deriv"); + TL_RAISE_IF(s.dimen() != c.size(), + "Incompatible symmetry and coordinates in Monom2Vector::deriv"); + IntSequence cy(c, 0, s[0]); + IntSequence cu(c, s[0], s.dimen()); + for (int i = 0; i < len; i++) + { + out[i] = y[i]->deriv(cy) * u[i]->deriv(cu); + } +} + +FGSTensor * +Monom2Vector::deriv(const Symmetry &s) const +{ + IntSequence nvs(2); nvs[0] = ny; nvs[1] = nu; + FGSTensor *t = new FGSTensor(len, TensorDimens(s, nvs)); + for (Tensor::index it = t->begin(); it != t->end(); ++it) + { + Vector col(*t, *it); + deriv(s, it.getCoor(), col); + } + return t; +} + +FGSContainer * +Monom2Vector::deriv(int maxdim) const +{ + FGSContainer *res = new FGSContainer(2); + for (int dim = 1; dim <= maxdim; dim++) + { + for (int ydim = 0; ydim <= dim; ydim++) + { + int udim = dim - ydim; + Symmetry s(ydim, udim); + res->insert(deriv(s)); + } + } + return res; +} + +void +Monom2Vector::print() const +{ + printf("Variables: y(%d) u(%d)\n", ny, nu); + printf("Rows: %d\n", len); + for (int i = 0; i < len; i++) + { + printf("%2d: ", i); + y[i]->print(); + printf(" "); + u[i]->print(); + printf("\n"); + } +} + +Monom4Vector::~Monom4Vector() +{ + for (int i = 0; i < len; i++) + { + delete x1[i]; + delete x2[i]; + delete x3[i]; + delete x4[i]; + } + delete [] x1; + delete [] x2; + delete [] x3; + delete [] x4; +} + +void +Monom4Vector::init_random() +{ + for (int i = 0; i < len; i++) + { + x1[i] = new Monom(nx1); + x2[i] = new Monom(nx2); + x3[i] = new Monom(nx3); + x4[i] = new Monom(nx4); + } +} + +Monom4Vector::Monom4Vector(int l, int ny, int nu) + : len(l), nx1(ny), nx2(nu), nx3(0), nx4(1), + x1(new Monom *[len]), + x2(new Monom *[len]), + x3(new Monom *[len]), + x4(new Monom *[len]) +{ + init_random(); +} + +Monom4Vector::Monom4Vector(int l, int ny, int nu, int nup) + : len(l), nx1(ny), nx2(nu), nx3(nup), nx4(1), + x1(new Monom *[len]), + x2(new Monom *[len]), + x3(new Monom *[len]), + x4(new Monom *[len]) +{ + init_random(); +} + +Monom4Vector::Monom4Vector(int l, int nbigg, int ng, int ny, int nu) + : len(l), nx1(nbigg), nx2(ng), nx3(ny), nx4(nu), + x1(new Monom *[len]), + x2(new Monom *[len]), + x3(new Monom *[len]), + x4(new Monom *[len]) +{ + init_random(); +} + +Monom4Vector::Monom4Vector(const Monom4Vector &f, const Monom4Vector &bigg, + const Monom4Vector &g) + : len(f.len), nx1(bigg.nx1), nx2(bigg.nx2), nx3(bigg.nx3), nx4(1), + x1(new Monom *[len]), + x2(new Monom *[len]), + x3(new Monom *[len]), + x4(new Monom *[len]) +{ + TL_RAISE_IF(!(bigg.nx1 == g.nx1 && bigg.nx2 == g.nx2 && g.nx3 == 0 + && bigg.nx4 == 1 && g.nx4 == 1), + "Incompatible g with G"); + TL_RAISE_IF(!(bigg.len == f.nx1 && g.len == f.nx2 + && bigg.nx1 == f.nx3 && bigg.nx2 == f.nx4), + "Incompatible g or G with f"); + + for (int i = 0; i < len; i++) + { + x1[i] = new Monom(nx1, 0); + x2[i] = new Monom(nx2, 0); + x3[i] = new Monom(nx3, 0); + x4[i] = new Monom(nx4, 0); + } + + for (int i = 0; i < len; i++) + { + // multiply from G (first argument) + for (int j = 0; j < f.nx1; j++) + { + int ex = f.x1[i]->operator[](j); + x1[i]->multiplyWith(ex, *(bigg.x1[j])); + x2[i]->multiplyWith(ex, *(bigg.x2[j])); + x3[i]->multiplyWith(ex, *(bigg.x3[j])); + x4[i]->multiplyWith(ex, *(bigg.x4[j])); + } + // multiply from g (second argument) + for (int j = 0; j < f.nx2; j++) + { + int ex = f.x2[i]->operator[](j); + x1[i]->multiplyWith(ex, *(g.x1[j])); + x2[i]->multiplyWith(ex, *(g.x2[j])); + x4[i]->multiplyWith(ex, *(g.x4[j])); + } + // add y as third argument of f + x1[i]->add(1, *(f.x3[i])); + // add u as fourth argument of f + x2[i]->add(1, *(f.x4[i])); + } +} + +void +Monom4Vector::deriv(const Symmetry &s, const IntSequence &coor, + Vector &out) const +{ + TL_RAISE_IF(out.length() != len, + "Wrong length of output vector in Monom4Vector::deriv"); + TL_RAISE_IF(s.num() != 4, + "Wrong symmetry for Monom4Vector::deriv"); + TL_RAISE_IF(s.dimen() != coor.size(), + "Incompatible symmetry and coordinates in Monom4Vector::deriv"); + + for (int i = 0; i < len; i++) + { + out[i] = 1; + int off = 0; + out[i] *= x1[i]->deriv(IntSequence(coor, off, off+s[0])); + off += s[0]; + out[i] *= x2[i]->deriv(IntSequence(coor, off, off+s[1])); + off += s[1]; + out[i] *= x3[i]->deriv(IntSequence(coor, off, off+s[2])); + off += s[2]; + out[i] *= x4[i]->deriv(IntSequence(coor, off, off+s[3])); + } +} + +FGSTensor * +Monom4Vector::deriv(const Symmetry &s) const +{ + IntSequence nvs(4); + nvs[0] = nx1; nvs[1] = nx2; + nvs[2] = nx3; nvs[3] = nx4; + + FGSTensor *res = new FGSTensor(len, TensorDimens(s, nvs)); + for (Tensor::index run = res->begin(); run != res->end(); ++run) + { + Vector col(*res, *run); + deriv(s, run.getCoor(), col); + } + return res; +} + +FSSparseTensor * +Monom4Vector::deriv(int dim) const +{ + IntSequence cum(4); + cum[0] = 0; cum[1] = nx1; cum[2] = nx1+nx2; cum[3] = nx1+nx2+nx3; + + FSSparseTensor *res = new FSSparseTensor(dim, nx1+nx2+nx3+nx4, len); + + FFSTensor dummy(0, nx1+nx2+nx3+nx4, dim); + for (Tensor::index run = dummy.begin(); run != dummy.end(); ++run) + { + Symmetry ind_sym(0, 0, 0, 0); + IntSequence ind(run.getCoor()); + for (int i = 0; i < ind.size(); i++) + { + int j = 3; + while (j >= 0 && ind[i] < cum[j]) + j--; + ind_sym[j]++; + ind[i] -= cum[j]; + } + + Vector col(len); + deriv(ind_sym, ind, col); + for (int i = 0; i < len; i++) + { + if (col[i] != 0.0) + { + res->insert(run.getCoor(), i, col[i]); + } + } + } + + return res; +} + +void +Monom4Vector::print() const +{ + printf("Variables: x1(%d) x2(%d) x3(%d) x4(%d)\n", + nx1, nx2, nx3, nx4); + printf("Rows: %d\n", len); + for (int i = 0; i < len; i++) + { + printf("%2d: ", i); + x1[i]->print(); + printf(" "); + x2[i]->print(); + printf(" "); + x3[i]->print(); + printf(" "); + x4[i]->print(); + printf("\n"); + } +} + +SparseDerivGenerator::SparseDerivGenerator( + int nf, int ny, int nu, int nup, int nbigg, int ng, + int mx, double prob, int maxdim) + : maxdimen(maxdim), ts(new FSSparseTensor *[maxdimen]) +{ + intgen.init(nf, ny, nu, nup, nbigg, mx, prob); + + Monom4Vector bigg_m(nbigg, ny, nu, nup); + Monom4Vector g_m(ng, ny, nu); + Monom4Vector f(nf, nbigg, ng, ny, nu); + Monom4Vector r(f, bigg_m, g_m); + bigg = new FGSContainer(4); + g = new FGSContainer(4); + rcont = new FGSContainer(4); + + for (int dim = 1; dim <= maxdimen; dim++) + { + SymmetrySet ss(dim, 4); + for (symiterator si(ss); !si.isEnd(); ++si) + { + bigg->insert(bigg_m.deriv(*si)); + rcont->insert(r.deriv(*si)); + if ((*si)[2] == 0) + g->insert(g_m.deriv(*si)); + } + + ts[dim-1] = f.deriv(dim); + } +} + +SparseDerivGenerator::~SparseDerivGenerator() +{ + delete bigg; + delete g; + delete rcont; + for (int i = 0; i < maxdimen; i++) + delete ts[i]; + delete [] ts; +} + +DenseDerivGenerator::DenseDerivGenerator(int ng, int nx, int ny, int nu, + int mx, double prob, int maxdim) + : maxdimen(maxdim), ts(new FGSTensor *[maxdimen]), + uts(new UGSTensor *[maxdimen]) +{ + intgen.init(ng, nx, ny, nu, nu, mx, prob); + Monom1Vector g(nx, ng); + Monom2Vector x(ny, nu, nx); + Monom2Vector r(g, x); + xcont = x.deriv(maxdimen); + rcont = r.deriv(maxdimen); + uxcont = NULL; + for (int d = 1; d <= maxdimen; d++) + { + ts[d-1] = g.deriv(d); + uts[d-1] = NULL; + } +} + +void +DenseDerivGenerator::unfold() +{ + uxcont = new UGSContainer(*xcont); + for (int i = 0; i < maxdimen; i++) + { + uts[i] = new UGSTensor(*(ts[i])); + } +} + +DenseDerivGenerator::~DenseDerivGenerator() +{ + delete xcont; + delete rcont; + for (int i = 0; i < maxdimen; i++) + { + delete ts[i]; + if (uts[i]) + delete uts[i]; + } + delete [] ts; + delete [] uts; +} + +// Local Variables: +// mode:C++ +// End: diff --git a/dynare++/tl/testing/monoms.cpp b/dynare++/tl/testing/monoms.cpp deleted file mode 100644 index a37c3a245..000000000 --- a/dynare++/tl/testing/monoms.cpp +++ /dev/null @@ -1,491 +0,0 @@ -/* $Id: monoms.cpp 148 2005-04-19 15:12:26Z kamenik $ */ -/* Copyright 2004, Ondra Kamenik */ - -#include "monoms.h" -#include "tl_exception.h" -#include "fs_tensor.h" - -#include -#include -#include - -IntGenerator intgen; - -void IntGenerator::init(int nf, int ny, int nv, int nw, int nu, - int mx, double prob) -{ - maxim = mx; - probab = prob; - long int seed = nf; - seed = 256*seed + ny; - seed = 256*seed + nv; - seed = 256*seed + nw; - seed = 256*seed + nu; - srand48(seed); -} - -int IntGenerator::get() const -{ - double d = drand48(); - int num_inter = (int)( ((double)2*maxim)/(1.0-probab)); - int num_zero_inter = num_inter - 2*maxim; - if (d < ((double)num_zero_inter)/num_inter) - return 0; - return (int)(d*num_inter)-num_zero_inter-maxim; -} - -Monom::Monom(int len) - : IntSequence(len) -{ - for (int i = 0; i < len; i++) - operator[](i) = intgen.get(); -} - -Monom::Monom(int len, int item) - : IntSequence(len, item) -{ -} - -double Monom::deriv(const IntSequence& vars) const -{ - double res = 1.0; - int first_same_i = 0; - for (int i = 0; i < vars.size(); i++) { - TL_RAISE_IF(vars[i] < 0 || vars[i] >= size(), - "Wrong variable index in Monom::deriv"); - if (vars[i] != vars[first_same_i]) - first_same_i = i; - int mult = operator[](vars[i]) - (i-first_same_i); - if (mult == 0) - return 0; - res *= mult; - } - return res; -} - -void Monom::multiplyWith(int ex, const Monom& m) -{ - TL_RAISE_IF(size() != m.size(), - "Wrong sizes of monoms in Monom::multiplyWith"); - if (ex == 0) - return; - for (int i = 0; i < size(); i++) - operator[](i) += m[i]*ex; -} - -void Monom::print() const -{ - printf("["); - for (int i = 0; i < size(); i++) - printf("%3d", operator[](i)); - printf("]"); -} - -Monom1Vector::Monom1Vector(int nxx, int l) - : nx(nxx), len(l), x(new Monom*[len]) -{ - for (int i = 0; i < len; i++) { - x[i] = new Monom(nx); - } -} - -Monom1Vector::~Monom1Vector() -{ - for (int i = 0; i < len; i++) { - delete x[i]; - } - delete [] x; -} - -void Monom1Vector::deriv(const IntSequence& c, Vector& out) const -{ - TL_RAISE_IF(out.length() != len, - "Wrong length of output vector in Monom1Vector::deriv"); - - for (int i = 0; i < len; i++) { - out[i] = x[i]->deriv(c); - } -} - - -FGSTensor* Monom1Vector::deriv(int dim) const -{ - FGSTensor* res = - new FGSTensor(len, TensorDimens(Symmetry(dim), IntSequence(1, nx))); - for (Tensor::index it = res->begin(); it != res->end(); ++it) { - Vector outcol(*res, *it); - deriv(it.getCoor(), outcol); - } - return res; -} - -void Monom1Vector::print() const -{ - printf("Variables: x(%d)\n", nx); - printf("Rows: %d\n", len); - for (int i = 0; i < len; i++) { - printf("%2d: ", i); - x[i]->print(); - printf("\n"); - } -} - -Monom2Vector::Monom2Vector(int nyy, int nuu, int l) - : ny(nyy), nu(nuu), len(l), y(new Monom*[len]), u(new Monom*[len]) -{ - for (int i = 0; i < len; i++) { - y[i] = new Monom(ny); - u[i] = new Monom(nu); - } -} - -Monom2Vector::Monom2Vector(const Monom1Vector& g, const Monom2Vector& xmon) - : ny(xmon.ny), nu(xmon.nu), len(g.len), - y(new Monom*[len]), u(new Monom*[len]) -{ - TL_RAISE_IF(xmon.len != g.nx, - "Wrong number of x's in Monom2Vector constructor"); - - for (int i = 0; i < len; i++) { - y[i] = new Monom(ny, 0); - u[i] = new Monom(nu, 0); - } - - for (int i = 0; i < len; i++) { - // multiply from xmon - for (int j = 0; j < g.nx; j++) { - int ex = g.x[i]->operator[](j); - y[i]->multiplyWith(ex, *(xmon.y[j])); - u[i]->multiplyWith(ex, *(xmon.u[j])); - } - } -} - -Monom2Vector::~Monom2Vector() -{ - for (int i = 0; i < len; i++) { - delete y[i]; - delete u[i]; - } - delete [] y; - delete [] u; -} - -void Monom2Vector::deriv(const Symmetry& s, const IntSequence& c, - Vector& out) const -{ - TL_RAISE_IF(out.length() != len, - "Wrong length of output vector in Monom2Vector::deriv"); - TL_RAISE_IF(s.num() != 2, - "Wrong symmetry for Monom2Vector::deriv"); - TL_RAISE_IF(s.dimen() != c.size(), - "Incompatible symmetry and coordinates in Monom2Vector::deriv"); - IntSequence cy(c, 0, s[0]); - IntSequence cu(c, s[0], s.dimen()); - for (int i = 0; i < len; i++) { - out[i] = y[i]->deriv(cy) * u[i]->deriv(cu); - } -} - -FGSTensor* Monom2Vector::deriv(const Symmetry& s) const -{ - IntSequence nvs(2); nvs[0] = ny; nvs[1] = nu; - FGSTensor* t = new FGSTensor(len, TensorDimens(s, nvs)); - for (Tensor::index it = t->begin(); it != t->end(); ++it) { - Vector col(*t, *it); - deriv(s, it.getCoor(), col); - } - return t; -} - -FGSContainer* Monom2Vector::deriv(int maxdim) const -{ - FGSContainer* res = new FGSContainer(2); - for (int dim = 1; dim <= maxdim; dim++) { - for (int ydim = 0; ydim <= dim; ydim++) { - int udim = dim - ydim; - Symmetry s(ydim, udim); - res->insert(deriv(s)); - } - } - return res; -} - -void Monom2Vector::print() const -{ - printf("Variables: y(%d) u(%d)\n", ny, nu); - printf("Rows: %d\n", len); - for (int i = 0; i < len; i++) { - printf("%2d: ", i); - y[i]->print(); - printf(" "); - u[i]->print(); - printf("\n"); - } -} - -Monom4Vector::~Monom4Vector() -{ - for (int i = 0; i < len; i++) { - delete x1[i]; - delete x2[i]; - delete x3[i]; - delete x4[i]; - } - delete [] x1; - delete [] x2; - delete [] x3; - delete [] x4; -} - -void Monom4Vector::init_random() -{ - for (int i = 0; i < len; i++) { - x1[i] = new Monom(nx1); - x2[i] = new Monom(nx2); - x3[i] = new Monom(nx3); - x4[i] = new Monom(nx4); - } -} - -Monom4Vector::Monom4Vector(int l, int ny, int nu) - : len(l), nx1(ny), nx2(nu), nx3(0), nx4(1), - x1(new Monom*[len]), - x2(new Monom*[len]), - x3(new Monom*[len]), - x4(new Monom*[len]) -{ - init_random(); -} - -Monom4Vector::Monom4Vector(int l, int ny, int nu, int nup) - : len(l), nx1(ny), nx2(nu), nx3(nup), nx4(1), - x1(new Monom*[len]), - x2(new Monom*[len]), - x3(new Monom*[len]), - x4(new Monom*[len]) -{ - init_random(); -} - -Monom4Vector::Monom4Vector(int l, int nbigg, int ng, int ny, int nu) - : len(l), nx1(nbigg), nx2(ng), nx3(ny), nx4(nu), - x1(new Monom*[len]), - x2(new Monom*[len]), - x3(new Monom*[len]), - x4(new Monom*[len]) -{ - init_random(); -} - -Monom4Vector::Monom4Vector(const Monom4Vector& f, const Monom4Vector& bigg, - const Monom4Vector& g) - : len(f.len), nx1(bigg.nx1), nx2(bigg.nx2), nx3(bigg.nx3), nx4(1), - x1(new Monom*[len]), - x2(new Monom*[len]), - x3(new Monom*[len]), - x4(new Monom*[len]) -{ - TL_RAISE_IF(!(bigg.nx1 == g.nx1 && bigg.nx2 == g.nx2 && g.nx3 == 0 && - bigg.nx4 == 1 && g.nx4 == 1), - "Incompatible g with G"); - TL_RAISE_IF(!(bigg.len == f.nx1 && g.len == f.nx2 && - bigg.nx1 == f.nx3 && bigg.nx2 == f.nx4), - "Incompatible g or G with f"); - - for (int i = 0; i < len; i++) { - x1[i] = new Monom(nx1, 0); - x2[i] = new Monom(nx2, 0); - x3[i] = new Monom(nx3, 0); - x4[i] = new Monom(nx4, 0); - } - - for (int i = 0; i < len; i++) { - // multiply from G (first argument) - for (int j = 0; j < f.nx1; j++) { - int ex = f.x1[i]->operator[](j); - x1[i]->multiplyWith(ex, *(bigg.x1[j])); - x2[i]->multiplyWith(ex, *(bigg.x2[j])); - x3[i]->multiplyWith(ex, *(bigg.x3[j])); - x4[i]->multiplyWith(ex, *(bigg.x4[j])); - } - // multiply from g (second argument) - for (int j = 0; j < f.nx2; j++) { - int ex = f.x2[i]->operator[](j); - x1[i]->multiplyWith(ex, *(g.x1[j])); - x2[i]->multiplyWith(ex, *(g.x2[j])); - x4[i]->multiplyWith(ex, *(g.x4[j])); - } - // add y as third argument of f - x1[i]->add(1, *(f.x3[i])); - // add u as fourth argument of f - x2[i]->add(1, *(f.x4[i])); - } -} - -void Monom4Vector::deriv(const Symmetry& s, const IntSequence& coor, - Vector& out) const -{ - TL_RAISE_IF(out.length() != len, - "Wrong length of output vector in Monom4Vector::deriv"); - TL_RAISE_IF(s.num() != 4, - "Wrong symmetry for Monom4Vector::deriv"); - TL_RAISE_IF(s.dimen() != coor.size(), - "Incompatible symmetry and coordinates in Monom4Vector::deriv"); - - for (int i = 0; i < len; i++) { - out[i] = 1; - int off = 0; - out[i] *= x1[i]->deriv(IntSequence(coor, off, off+s[0])); - off += s[0]; - out[i] *= x2[i]->deriv(IntSequence(coor, off, off+s[1])); - off += s[1]; - out[i] *= x3[i]->deriv(IntSequence(coor, off, off+s[2])); - off += s[2]; - out[i] *= x4[i]->deriv(IntSequence(coor, off, off+s[3])); - } -} - -FGSTensor* Monom4Vector::deriv(const Symmetry& s) const -{ - IntSequence nvs(4); - nvs[0] = nx1; nvs[1] = nx2; - nvs[2] = nx3; nvs[3] = nx4; - - FGSTensor* res = new FGSTensor(len, TensorDimens(s, nvs)); - for (Tensor::index run = res->begin(); run != res->end(); ++run) { - Vector col(*res, *run); - deriv(s, run.getCoor(), col); - } - return res; -} - -FSSparseTensor* Monom4Vector::deriv(int dim) const -{ - IntSequence cum(4); - cum[0] = 0; cum[1] = nx1; cum[2] = nx1+nx2; cum[3] = nx1+nx2+nx3; - - FSSparseTensor* res = new FSSparseTensor(dim, nx1+nx2+nx3+nx4, len); - - FFSTensor dummy(0, nx1+nx2+nx3+nx4, dim); - for (Tensor::index run = dummy.begin(); run != dummy.end(); ++run) { - Symmetry ind_sym(0,0,0,0); - IntSequence ind(run.getCoor()); - for (int i = 0; i < ind.size(); i++) { - int j = 3; - while (j >= 0 && ind[i] < cum[j]) j--; - ind_sym[j]++; - ind[i] -= cum[j]; - } - - Vector col(len); - deriv(ind_sym, ind, col); - for (int i = 0; i < len; i++) { - if (col[i] != 0.0) { - res->insert(run.getCoor(), i, col[i]); - } - } - } - - return res; -} - -void Monom4Vector::print() const -{ - printf("Variables: x1(%d) x2(%d) x3(%d) x4(%d)\n", - nx1, nx2, nx3, nx4); - printf("Rows: %d\n", len); - for (int i = 0; i < len; i++) { - printf("%2d: ", i); - x1[i]->print(); - printf(" "); - x2[i]->print(); - printf(" "); - x3[i]->print(); - printf(" "); - x4[i]->print(); - printf("\n"); - } -} - -SparseDerivGenerator::SparseDerivGenerator( - int nf, int ny, int nu, int nup, int nbigg, int ng, - int mx, double prob, int maxdim) - : maxdimen(maxdim), ts(new FSSparseTensor*[maxdimen]) -{ - intgen.init(nf, ny, nu, nup, nbigg, mx, prob); - - Monom4Vector bigg_m(nbigg, ny, nu, nup); - Monom4Vector g_m(ng, ny, nu); - Monom4Vector f(nf, nbigg, ng, ny, nu); - Monom4Vector r(f, bigg_m, g_m); - bigg = new FGSContainer(4); - g = new FGSContainer(4); - rcont = new FGSContainer(4); - - for (int dim = 1; dim <= maxdimen; dim++) { - SymmetrySet ss(dim, 4); - for (symiterator si(ss); !si.isEnd(); ++si) { - bigg->insert(bigg_m.deriv(*si)); - rcont->insert(r.deriv(*si)); - if ((*si)[2] == 0) - g->insert(g_m.deriv(*si)); - } - - ts[dim-1] = f.deriv(dim); - } -} - -SparseDerivGenerator::~SparseDerivGenerator() -{ - delete bigg; - delete g; - delete rcont; - for(int i = 0; i < maxdimen; i++) - delete ts[i]; - delete [] ts; -} - - -DenseDerivGenerator::DenseDerivGenerator(int ng, int nx, int ny, int nu, - int mx, double prob, int maxdim) - : maxdimen(maxdim), ts(new FGSTensor*[maxdimen]), - uts(new UGSTensor*[maxdimen]) -{ - intgen.init(ng, nx, ny, nu, nu, mx, prob); - Monom1Vector g(nx, ng); - Monom2Vector x(ny, nu, nx); - Monom2Vector r(g, x); - xcont = x.deriv(maxdimen); - rcont = r.deriv(maxdimen); - uxcont = NULL; - for (int d = 1; d <= maxdimen; d++) { - ts[d-1] = g.deriv(d); - uts[d-1] = NULL; - } -} - -void DenseDerivGenerator::unfold() -{ - uxcont = new UGSContainer(*xcont); - for (int i = 0; i < maxdimen; i++) { - uts[i] = new UGSTensor(*(ts[i])); - } -} - -DenseDerivGenerator::~DenseDerivGenerator() -{ - delete xcont; - delete rcont; - for (int i = 0; i < maxdimen; i++) { - delete ts[i]; - if (uts[i]) - delete uts[i]; - } - delete [] ts; - delete [] uts; -} - -// Local Variables: -// mode:C++ -// End: diff --git a/dynare++/tl/testing/monoms.h b/dynare++/tl/testing/monoms.hh similarity index 96% rename from dynare++/tl/testing/monoms.h rename to dynare++/tl/testing/monoms.hh index 76c56e2a7..86707e3bd 100644 --- a/dynare++/tl/testing/monoms.h +++ b/dynare++/tl/testing/monoms.hh @@ -4,10 +4,10 @@ #ifndef MONOMS_H #define MONOMS_H -#include "int_sequence.h" -#include "gs_tensor.h" -#include "t_container.h" -#include "sparse_tensor.h" +#include "int_sequence.hh" +#include "gs_tensor.hh" +#include "t_container.hh" +#include "sparse_tensor.hh" #include "Vector.h" class IntGenerator diff --git a/dynare++/tl/testing/tests.cc b/dynare++/tl/testing/tests.cc new file mode 100644 index 000000000..d989481cb --- /dev/null +++ b/dynare++/tl/testing/tests.cc @@ -0,0 +1,1205 @@ +/* $Id: tests.cpp 148 2005-04-19 15:12:26Z kamenik $ */ +/* Copyright 2004, Ondra Kamenik */ + +#include "SylvException.h" +#include "tl_exception.hh" +#include "gs_tensor.hh" +#include "factory.hh" +#include "monoms.hh" +#include "t_container.hh" +#include "stack_container.hh" +#include "t_polynomial.hh" +#include "rfs_tensor.hh" +#include "ps_tensor.hh" +#include "tl_static.hh" + +#include +#include +#include + +class TestRunnable +{ + char name[100]; +public: + int dim; // dimension of the solved problem + int nvar; // number of variable of the solved problem + TestRunnable(const char *n, int d, int nv) + : dim(d), nvar(nv) + { + strncpy(name, n, 100); + } + bool test() const; + virtual bool run() const = 0; + const char * + getName() const + { + return name; + } +protected: + template + static bool index_forward(const Symmetry &s, const IntSequence &nvs); + + template + static bool index_backward(const Symmetry &s, const IntSequence &nvs); + + template + static bool index_offset(const Symmetry &s, const IntSequence &nvs); + + static bool fold_unfold(const FTensor *folded); + static bool + fs_fold_unfold(int r, int nv, int dim) + { + Factory f; + FTensor *folded = f.make(r, nv, dim); + return fold_unfold(folded); // folded deallocated in fold_unfold + } + static bool + r_fold_unfold(int r, int nv, int dim) + { + Factory f; + FTensor *folded = f.make(r, nv, dim); + return fold_unfold(folded); // folded deallocated in fold_unfold + } + static bool + gs_fold_unfold(int r, const Symmetry &s, const IntSequence &nvs) + { + Factory f; + FTensor *folded = f.make(r, s, nvs); + return fold_unfold(folded); // folded deallocated in fold_unfold + } + + static bool dense_prod(const Symmetry &bsym, const IntSequence &bnvs, + int hdim, int hnv, int rows); + + static bool folded_monomial(int ng, int nx, int ny, int nu, int dim); + + static bool unfolded_monomial(int ng, int nx, int ny, int nu, int dim); + + static bool fold_zcont(int nf, int ny, int nu, int nup, int nbigg, + int ng, int dim); + + static bool unfold_zcont(int nf, int ny, int nu, int nup, int nbigg, + int ng, int dim); + + static bool folded_contraction(int r, int nv, int dim); + + static bool unfolded_contraction(int r, int nv, int dim); + + static bool poly_eval(int r, int nv, int maxdim); + +}; + +bool +TestRunnable::test() const +{ + printf("Running test <%s>\n", name); + clock_t start = clock(); + bool passed = run(); + clock_t end = clock(); + printf("CPU time %8.4g (CPU seconds)..................", + ((double) (end-start))/CLOCKS_PER_SEC); + if (passed) + { + printf("passed\n\n"); + return passed; + } + else + { + printf("FAILED\n\n"); + return passed; + } +} + +/****************************************************/ +/* definition of TestRunnable static methods */ +/****************************************************/ +template +bool +TestRunnable::index_forward(const Symmetry &s, const IntSequence &nvs) +{ + int fails = 0; + int ndecr = 0; + int nincr = 0; + _Ttype dummy(0, TensorDimens(s, nvs)); + typename _Ttype::index run = dummy.end(); + do + { + --run; + ndecr++; + typename _Ttype::index run2 = dummy.begin(); + for (int i = 0; i < *run; i++) + { + ++run2; + nincr++; + } + if (!(run == run2)) + fails++; + } + while (run != dummy.begin()); + + printf("\tnumber of columns = %d\n", dummy.ncols()); + printf("\tnumber of increments = %d\n", nincr); + printf("\tnumber of decrements = %d\n", ndecr); + printf("\tnumber of failures = %d\n", fails); + + return fails == 0; +} + +template +bool +TestRunnable::index_backward(const Symmetry &s, const IntSequence &nvs) +{ + int fails = 0; + int ndecr = 0; + int nincr = 0; + _Ttype dummy(0, TensorDimens(s, nvs)); + typename _Ttype::index run = dummy.begin(); + while (run != dummy.end()) + { + typename _Ttype::index run2 = dummy.end(); + for (int i = 0; i < dummy.ncols() - *run; i++) + { + --run2; + ndecr++; + } + if (!(run == run2)) + fails++; + ++run; + nincr++; + } + + printf("\tnumber of columns = %d\n", dummy.ncols()); + printf("\tnumber of increments = %d\n", nincr); + printf("\tnumber of decrements = %d\n", ndecr); + printf("\tnumber of failures = %d\n", fails); + + return fails == 0; +} + +template +bool +TestRunnable::index_offset(const Symmetry &s, const IntSequence &nvs) +{ + int fails = 0; + int nincr = 0; + _Ttype dummy(0, TensorDimens(s, nvs)); + for (typename _Ttype::index run = dummy.begin(); + run != dummy.end(); ++run, nincr++) + { + typename _Ttype::index run2(&dummy, run.getCoor()); + if (!(run == run2)) + fails++; + } + + printf("\tnumber of columns = %d\n", dummy.ncols()); + printf("\tnumber of increments = %d\n", nincr); + printf("\tnumber of failures = %d\n", fails); + + return fails == 0; +} + +bool +TestRunnable::fold_unfold(const FTensor *folded) +{ + UTensor *unfolded = &(folded->unfold()); + FTensor *folded2 = &(unfolded->fold()); + folded2->add(-1.0, *folded); + double normInf = folded2->getNormInf(); + double norm1 = folded2->getNorm1(); + printf("\tfolded size: (%d, %d)\n", folded->nrows(), folded->ncols()); + printf("\tunfolded size: (%d, %d)\n", unfolded->nrows(), unfolded->ncols()); + printf("\tdifference normInf: %8.4g\n", normInf); + printf("\tdifference norm1: %8.4g\n", norm1); + + delete folded; + delete unfolded; + delete folded2; + + return normInf < 1.0e-15; +} + +bool +TestRunnable::dense_prod(const Symmetry &bsym, const IntSequence &bnvs, + int hdim, int hnv, int rows) +{ + Factory f; + FGSContainer *cont + = f.makeCont(hnv, bnvs, bsym.dimen()-hdim+1); + FGSTensor *fh + = f.make(rows, Symmetry(hdim), IntSequence(1, hnv)); + UGSTensor uh(*fh); + FGSTensor fb(rows, TensorDimens(bsym, bnvs)); + fb.getData().zeros(); + clock_t s1 = clock(); + cont->multAndAdd(uh, fb); + clock_t s2 = clock(); + UGSContainer ucont(*cont); + clock_t s3 = clock(); + UGSTensor ub(rows, fb.getDims()); + ub.getData().zeros(); + clock_t s4 = clock(); + ucont.multAndAdd(uh, ub); + clock_t s5 = clock(); + + UGSTensor btmp(fb); + btmp.add(-1, ub); + double norm = btmp.getData().getMax(); + double norm1 = btmp.getNorm1(); + double normInf = btmp.getNormInf(); + + printf("\ttime for folded product: %8.4g\n", + ((double) (s2-s1))/CLOCKS_PER_SEC); + printf("\ttime for unfolded product: %8.4g\n", + ((double) (s5-s4))/CLOCKS_PER_SEC); + printf("\ttime for container convert: %8.4g\n", + ((double) (s3-s2))/CLOCKS_PER_SEC); + printf("\tunfolded difference normMax: %10.6g\n", norm); + printf("\tunfolded difference norm1: %10.6g\n", norm1); + printf("\tunfolded difference normInf: %10.6g\n", normInf); + + delete cont; + delete fh; + + return norm < 1.e-13; +} + +bool +TestRunnable::folded_monomial(int ng, int nx, int ny, int nu, int dim) +{ + clock_t gen_time = clock(); + DenseDerivGenerator gen(ng, nx, ny, nu, 5, 0.3, dim); + gen_time = clock()-gen_time; + printf("\ttime for monom generation: %8.4g\n", + ((double) gen_time)/CLOCKS_PER_SEC); + IntSequence nvs(2); nvs[0] = ny; nvs[1] = nu; + double maxnorm = 0; + for (int ydim = 0; ydim <= dim; ydim++) + { + Symmetry s(ydim, dim-ydim); + printf("\tSymmetry: "); s.print(); + FGSTensor res(ng, TensorDimens(s, nvs)); + res.getData().zeros(); + clock_t stime = clock(); + for (int d = 1; d <= dim; d++) + { + gen.xcont->multAndAdd(*(gen.ts[d-1]), res); + } + stime = clock() - stime; + printf("\t\ttime for symmetry: %8.4g\n", + ((double) stime)/CLOCKS_PER_SEC); + const FGSTensor *mres = gen.rcont->get(s); + res.add(-1.0, *mres); + double normtmp = res.getData().getMax(); + printf("\t\terror normMax: %10.6g\n", normtmp); + if (normtmp > maxnorm) + maxnorm = normtmp; + } + return maxnorm < 1.0e-10; +} + +bool +TestRunnable::unfolded_monomial(int ng, int nx, int ny, int nu, int dim) +{ + clock_t gen_time = clock(); + DenseDerivGenerator gen(ng, nx, ny, nu, 5, 0.3, dim); + gen_time = clock()-gen_time; + printf("\ttime for monom generation: %8.4g\n", + ((double) gen_time)/CLOCKS_PER_SEC); + clock_t u_time = clock(); + gen.unfold(); + u_time = clock() - u_time; + printf("\ttime for monom unfolding: %8.4g\n", + ((double) u_time)/CLOCKS_PER_SEC); + IntSequence nvs(2); nvs[0] = ny; nvs[1] = nu; + double maxnorm = 0; + for (int ydim = 0; ydim <= dim; ydim++) + { + Symmetry s(ydim, dim-ydim); + printf("\tSymmetry: "); s.print(); + UGSTensor res(ng, TensorDimens(s, nvs)); + res.getData().zeros(); + clock_t stime = clock(); + for (int d = 1; d <= dim; d++) + { + gen.uxcont->multAndAdd(*(gen.uts[d-1]), res); + } + stime = clock() - stime; + printf("\t\ttime for symmetry: %8.4g\n", + ((double) stime)/CLOCKS_PER_SEC); + const FGSTensor *mres = gen.rcont->get(s); + FGSTensor foldres(res); + foldres.add(-1.0, *mres); + double normtmp = foldres.getData().getMax(); + printf("\t\terror normMax: %10.6g\n", normtmp); + if (normtmp > maxnorm) + maxnorm = normtmp; + } + return maxnorm < 1.0e-10; +} + +bool +TestRunnable::fold_zcont(int nf, int ny, int nu, int nup, int nbigg, + int ng, int dim) +{ + clock_t gen_time = clock(); + SparseDerivGenerator dg(nf, ny, nu, nup, nbigg, ng, + 5, 0.55, dim); + gen_time = clock()-gen_time; + for (int d = 1; d <= dim; d++) + { + printf("\tfill of dim=%d tensor: %3.2f %%\n", + d, 100*dg.ts[d-1]->getFillFactor()); + } + printf("\ttime for monom generation: %8.4g\n", + ((double) gen_time)/CLOCKS_PER_SEC); + + IntSequence nvs(4); + nvs[0] = ny; nvs[1] = nu; nvs[2] = nup; nvs[3] = 1; + double maxnorm = 0.0; + + // form ZContainer + FoldedZContainer zc(dg.bigg, nbigg, dg.g, ng, ny, nu); + + for (int d = 2; d <= dim; d++) + { + SymmetrySet ss(d, 4); + for (symiterator si(ss); !si.isEnd(); ++si) + { + printf("\tSymmetry: "); (*si).print(); + FGSTensor res(nf, TensorDimens(*si, nvs)); + res.getData().zeros(); + clock_t stime = clock(); + for (int l = 1; l <= (*si).dimen(); l++) + { + zc.multAndAdd(*(dg.ts[l-1]), res); + } + stime = clock() - stime; + printf("\t\ttime for symmetry: %8.4g\n", + ((double) stime)/CLOCKS_PER_SEC); + const FGSTensor *mres = dg.rcont->get(*si); + res.add(-1.0, *mres); + double normtmp = res.getData().getMax(); + printf("\t\terror normMax: %10.6g\n", normtmp); + if (normtmp > maxnorm) + maxnorm = normtmp; + } + } + return maxnorm < 1.0e-10; +} + +bool +TestRunnable::unfold_zcont(int nf, int ny, int nu, int nup, int nbigg, + int ng, int dim) +{ + clock_t gen_time = clock(); + SparseDerivGenerator dg(nf, ny, nu, nup, nbigg, ng, + 5, 0.55, dim); + gen_time = clock()-gen_time; + for (int d = 1; d <= dim; d++) + { + printf("\tfill of dim=%d tensor: %3.2f %%\n", + d, 100*dg.ts[d-1]->getFillFactor()); + } + printf("\ttime for monom generation: %8.4g\n", + ((double) gen_time)/CLOCKS_PER_SEC); + + clock_t con_time = clock(); + UGSContainer uG_cont(*(dg.bigg)); + UGSContainer ug_cont(*(dg.g)); + con_time = clock()-con_time; + printf("\ttime for container unfold: %8.4g\n", + ((double) con_time)/CLOCKS_PER_SEC); + + IntSequence nvs(4); + nvs[0] = ny; nvs[1] = nu; nvs[2] = nup; nvs[3] = 1; + double maxnorm = 0.0; + + // form ZContainer + UnfoldedZContainer zc(&uG_cont, nbigg, &ug_cont, ng, ny, nu); + + for (int d = 2; d <= dim; d++) + { + SymmetrySet ss(d, 4); + for (symiterator si(ss); !si.isEnd(); ++si) + { + printf("\tSymmetry: "); (*si).print(); + UGSTensor res(nf, TensorDimens(*si, nvs)); + res.getData().zeros(); + clock_t stime = clock(); + for (int l = 1; l <= (*si).dimen(); l++) + { + zc.multAndAdd(*(dg.ts[l-1]), res); + } + stime = clock() - stime; + printf("\t\ttime for symmetry: %8.4g\n", + ((double) stime)/CLOCKS_PER_SEC); + FGSTensor fold_res(res); + const FGSTensor *mres = dg.rcont->get(*si); + fold_res.add(-1.0, *mres); + double normtmp = fold_res.getData().getMax(); + printf("\t\terror normMax: %10.6g\n", normtmp); + if (normtmp > maxnorm) + maxnorm = normtmp; + } + } + return maxnorm < 1.0e-10; +} + +bool +TestRunnable::folded_contraction(int r, int nv, int dim) +{ + Factory fact; + Vector *x = fact.makeVector(nv); + + FFSTensor *forig = fact.make(r, nv, dim); + FFSTensor *f = new FFSTensor(*forig); + clock_t ctime = clock(); + for (int d = dim-1; d > 0; d--) + { + FFSTensor *fnew = new FFSTensor(*f, ConstVector(*x)); + delete f; + f = fnew; + } + ctime = clock() - ctime; + Vector res(forig->nrows()); + res.zeros(); + f->multaVec(res, *x); + + UFSTensor u(*forig); + clock_t utime = clock(); + URSingleTensor ux(*x, dim); + Vector v(u.nrows()); + v.zeros(); + u.multaVec(v, ux.getData()); + utime = clock() - utime; + + v.add(-1.0, res); + printf("\ttime for folded contraction: %8.4g\n", + ((double) ctime)/CLOCKS_PER_SEC); + printf("\ttime for unfolded power: %8.4g\n", + ((double) utime)/CLOCKS_PER_SEC); + printf("\terror normMax: %10.6g\n", v.getMax()); + printf("\terror norm1: %10.6g\n", v.getNorm1()); + + delete f; + delete x; + + return (v.getMax() < 1.e-10); +} + +bool +TestRunnable::unfolded_contraction(int r, int nv, int dim) +{ + Factory fact; + Vector *x = fact.makeVector(nv); + + FFSTensor *forig = fact.make(r, nv, dim); + UFSTensor uorig(*forig); + delete forig; + UFSTensor *u = new UFSTensor(uorig); + clock_t ctime = clock(); + for (int d = dim-1; d > 0; d--) + { + UFSTensor *unew = new UFSTensor(*u, ConstVector(*x)); + delete u; + u = unew; + } + ctime = clock() - ctime; + Vector res(uorig.nrows()); + res.zeros(); + u->multaVec(res, *x); + + clock_t utime = clock(); + URSingleTensor ux(*x, dim); + Vector v(uorig.nrows()); + v.zeros(); + uorig.multaVec(v, ux.getData()); + utime = clock() - utime; + + v.add(-1.0, res); + printf("\ttime for unfolded contraction: %8.4g\n", + ((double) ctime)/CLOCKS_PER_SEC); + printf("\ttime for unfolded power: %8.4g\n", + ((double) utime)/CLOCKS_PER_SEC); + printf("\terror normMax: %10.6g\n", v.getMax()); + printf("\terror norm1: %10.6g\n", v.getNorm1()); + + delete u; + delete x; + + return (v.getMax() < 1.e-10); +} + +bool +TestRunnable::poly_eval(int r, int nv, int maxdim) +{ + Factory fact; + Vector *x = fact.makeVector(nv); + + Vector out_ft(r); out_ft.zeros(); + Vector out_fh(r); out_fh.zeros(); + Vector out_ut(r); out_ut.zeros(); + Vector out_uh(r); out_uh.zeros(); + + UTensorPolynomial *up; + { + FTensorPolynomial *fp = fact.makePoly(r, nv, maxdim); + + clock_t ft_cl = clock(); + fp->evalTrad(out_ft, *x); + ft_cl = clock() - ft_cl; + printf("\ttime for folded power eval: %8.4g\n", + ((double) ft_cl)/CLOCKS_PER_SEC); + + clock_t fh_cl = clock(); + fp->evalHorner(out_fh, *x); + fh_cl = clock() - fh_cl; + printf("\ttime for folded horner eval: %8.4g\n", + ((double) fh_cl)/CLOCKS_PER_SEC); + + up = new UTensorPolynomial(*fp); + delete fp; + } + + clock_t ut_cl = clock(); + up->evalTrad(out_ut, *x); + ut_cl = clock() - ut_cl; + printf("\ttime for unfolded power eval: %8.4g\n", + ((double) ut_cl)/CLOCKS_PER_SEC); + + clock_t uh_cl = clock(); + up->evalHorner(out_uh, *x); + uh_cl = clock() - uh_cl; + printf("\ttime for unfolded horner eval: %8.4g\n", + ((double) uh_cl)/CLOCKS_PER_SEC); + + out_ft.add(-1.0, out_ut); + double max_ft = out_ft.getMax(); + out_fh.add(-1.0, out_ut); + double max_fh = out_fh.getMax(); + out_uh.add(-1.0, out_ut); + double max_uh = out_uh.getMax(); + + printf("\tfolded power error norm max: %10.6g\n", max_ft); + printf("\tfolded horner error norm max: %10.6g\n", max_fh); + printf("\tunfolded horner error norm max: %10.6g\n", max_uh); + + delete up; + delete x; + return (max_ft+max_fh+max_uh < 1.0e-10); +} + +/****************************************************/ +/* definition of TestRunnable subclasses */ +/****************************************************/ +class SmallIndexForwardFold : public TestRunnable +{ +public: + SmallIndexForwardFold() + : TestRunnable("small index forward for fold (44)(222)", 5, 4) + { + } + bool + run() const + { + Symmetry s(2, 3); + IntSequence nvs(2); nvs[0] = 4; nvs[1] = 2; + return index_forward(s, nvs); + } +}; + +class SmallIndexForwardUnfold : public TestRunnable +{ +public: + SmallIndexForwardUnfold() + : TestRunnable("small index forward for unfold (44)(222)", 5, 4) + { + } + bool + run() const + { + Symmetry s(2, 3); + IntSequence nvs(2); nvs[0] = 4; nvs[1] = 2; + return index_forward(s, nvs); + } +}; + +class IndexForwardFold : public TestRunnable +{ +public: + IndexForwardFold() + : TestRunnable("index forward for fold (55)(222)(22)", 7, 5) + { + } + bool + run() const + { + Symmetry s(2, 3, 2); + IntSequence nvs(3); nvs[0] = 5; nvs[1] = 2; nvs[2] = 2; + return index_forward(s, nvs); + } +}; + +class IndexForwardUnfold : public TestRunnable +{ +public: + IndexForwardUnfold() + : TestRunnable("index forward for unfold (55)(222)(22)", 7, 5) + { + } + bool + run() const + { + Symmetry s(2, 3, 2); + IntSequence nvs(3); nvs[0] = 5; nvs[1] = 2; nvs[2] = 2; + return index_forward(s, nvs); + } +}; + +class SmallIndexBackwardFold : public TestRunnable +{ +public: + SmallIndexBackwardFold() + : TestRunnable("small index backward for fold (3)(3)(222)", 5, 3) + { + } + bool + run() const + { + Symmetry s(1, 1, 3); + IntSequence nvs(3); nvs[0] = 3; nvs[1] = 3; nvs[2] = 2; + return index_backward(s, nvs); + } +}; + +class IndexBackwardFold : public TestRunnable +{ +public: + IndexBackwardFold() + : TestRunnable("index backward for fold (44)(222)(44)", 7, 4) + { + } + bool + run() const + { + Symmetry s(2, 3, 2); + IntSequence nvs(3); nvs[0] = 4; nvs[1] = 2; nvs[2] = 4; + return index_backward(s, nvs); + } +}; + +class SmallIndexBackwardUnfold : public TestRunnable +{ +public: + SmallIndexBackwardUnfold() + : TestRunnable("small index backward for unfold (3)(3)(222)", 5, 3) + { + } + bool + run() const + { + Symmetry s(1, 1, 3); + IntSequence nvs(3); nvs[0] = 3; nvs[1] = 3; nvs[2] = 2; + return index_backward(s, nvs); + } +}; + +class IndexBackwardUnfold : public TestRunnable +{ +public: + IndexBackwardUnfold() + : TestRunnable("index backward for unfold (44)(222)(44)", 7, 4) + { + } + bool + run() const + { + Symmetry s(2, 3, 2); + IntSequence nvs(3); nvs[0] = 4; nvs[1] = 2; nvs[2] = 4; + return index_backward(s, nvs); + } +}; + +class SmallIndexOffsetFold : public TestRunnable +{ +public: + SmallIndexOffsetFold() + : TestRunnable("small index offset for fold (44)(222)", 5, 4) + { + } + bool + run() const + { + Symmetry s(2, 3); + IntSequence nvs(2); nvs[0] = 4; nvs[1] = 2; + return index_offset(s, nvs); + } +}; + +class SmallIndexOffsetUnfold : public TestRunnable +{ +public: + SmallIndexOffsetUnfold() + : TestRunnable("small index offset for unfold (44)(222)", 5, 4) + { + } + bool + run() const + { + Symmetry s(2, 3); + IntSequence nvs(2); nvs[0] = 4; nvs[1] = 2; + return index_offset(s, nvs); + } +}; + +class IndexOffsetFold : public TestRunnable +{ +public: + IndexOffsetFold() + : TestRunnable("index offset for fold (55)(222)(22)", 5, 5) + { + } + bool + run() const + { + Symmetry s(2, 3, 2); + IntSequence nvs(3); nvs[0] = 5; nvs[1] = 2; nvs[2] = 2; + return index_offset(s, nvs); + } +}; + +class IndexOffsetUnfold : public TestRunnable +{ +public: + IndexOffsetUnfold() + : TestRunnable("index offset for unfold (55)(222)(22)", 7, 5) + { + } + bool + run() const + { + Symmetry s(2, 3, 2); + IntSequence nvs(3); nvs[0] = 5; nvs[1] = 2; nvs[2] = 2; + return index_offset(s, nvs); + } +}; + +class SmallFoldUnfoldFS : public TestRunnable +{ +public: + SmallFoldUnfoldFS() + : TestRunnable("small fold-unfold for full symmetry (444)", 3, 4) + { + } + bool + run() const + { + return fs_fold_unfold(5, 4, 3); + } +}; + +class SmallFoldUnfoldGS : public TestRunnable +{ +public: + SmallFoldUnfoldGS() + : TestRunnable("small fold-unfold for gen symmetry (3)(33)(22)", 5, 3) + { + } + bool + run() const + { + Symmetry s(1, 2, 2); + IntSequence nvs(3); nvs[0] = 3; nvs[1] = 3; nvs[2] = 2; + return gs_fold_unfold(5, s, nvs); + } +}; + +class FoldUnfoldFS : public TestRunnable +{ +public: + FoldUnfoldFS() + : TestRunnable("fold-unfold for full symmetry (9999)", 4, 9) + { + } + bool + run() const + { + return fs_fold_unfold(5, 9, 4); + } +}; + +class FoldUnfoldGS : public TestRunnable +{ +public: + FoldUnfoldGS() + : TestRunnable("fold-unfold for gen symmetry (66)(2)(66)", 5, 6) + { + } + bool + run() const + { + Symmetry s(2, 1, 2); + IntSequence nvs(3); nvs[0] = 6; nvs[1] = 2; nvs[2] = 6; + return gs_fold_unfold(5, s, nvs); + } +}; + +class SmallFoldUnfoldR : public TestRunnable +{ +public: + SmallFoldUnfoldR() + : TestRunnable("small fold-unfold for row full symmetry (333)", 3, 3) + { + } + bool + run() const + { + return r_fold_unfold(5, 3, 3); + } +}; + +class FoldUnfoldR : public TestRunnable +{ +public: + FoldUnfoldR() + : TestRunnable("fold-unfold for row full symmetry (66666)", 5, 6) + { + } + bool + run() const + { + return r_fold_unfold(5, 6, 5); + } +}; + +class SmallDenseProd : public TestRunnable +{ +public: + SmallDenseProd() + : TestRunnable("small dense prod bsym=1-2,nvs=3-2,h=2-3,r=2", 3, 3) + { + } + bool + run() const + { + IntSequence bnvs(2); bnvs[0] = 3; bnvs[1] = 2; + return dense_prod(Symmetry(1, 2), bnvs, 2, 3, 2); + } +}; + +class DenseProd : public TestRunnable +{ +public: + DenseProd() + : TestRunnable("dense prod bsym=2-3,nvs=10-7,h=3-15,r=10", 5, 15) + { + } + bool + run() const + { + IntSequence bnvs(2); bnvs[0] = 10; bnvs[1] = 7; + return dense_prod(Symmetry(2, 3), bnvs, 3, 15, 10); + } +}; + +class BigDenseProd : public TestRunnable +{ +public: + BigDenseProd() + : TestRunnable("dense prod bsym=3-2,nvs=13-11,h=3-20,r=20", 6, 20) + { + } + bool + run() const + { + IntSequence bnvs(2); bnvs[0] = 13; bnvs[1] = 11; + return dense_prod(Symmetry(3, 2), bnvs, 3, 20, 20); + } +}; + +class SmallFoldedMonomial : public TestRunnable +{ +public: + SmallFoldedMonomial() + : TestRunnable("folded vrs. monoms (g,x,y,u)=(10,4,5,3), dim=4", 4, 8) + { + } + bool + run() const + { + return folded_monomial(10, 4, 5, 3, 4); + } +}; + +class FoldedMonomial : public TestRunnable +{ +public: + FoldedMonomial() + : TestRunnable("folded vrs. monoms (g,x,y,u)=(20,12,10,5), dim=4", 4, 15) + { + } + bool + run() const + { + return folded_monomial(20, 12, 10, 5, 4); + } +}; + +class SmallUnfoldedMonomial : public TestRunnable +{ +public: + SmallUnfoldedMonomial() + : TestRunnable("unfolded vrs. monoms (g,x,y,u)=(10,4,5,3), dim=4", 4, 8) + { + } + bool + run() const + { + return unfolded_monomial(10, 4, 5, 3, 4); + } +}; + +class UnfoldedMonomial : public TestRunnable +{ +public: + UnfoldedMonomial() + : TestRunnable("unfolded vrs. monoms (g,x,y,u)=(20,12,10,5), dim=4", 4, 15) + { + } + bool + run() const + { + return unfolded_monomial(20, 12, 10, 5, 4); + } +}; + +class FoldedContractionSmall : public TestRunnable +{ +public: + FoldedContractionSmall() + : TestRunnable("folded contraction small (r=5, nv=4, dim=3)", 3, 4) + { + } + bool + run() const + { + return folded_contraction(5, 4, 3); + } +}; + +class FoldedContractionBig : public TestRunnable +{ +public: + FoldedContractionBig() + : TestRunnable("folded contraction big (r=20, nv=12, dim=5)", 5, 12) + { + } + bool + run() const + { + return folded_contraction(20, 12, 5); + } +}; + +class UnfoldedContractionSmall : public TestRunnable +{ +public: + UnfoldedContractionSmall() + : TestRunnable("unfolded contraction small (r=5, nv=4, dim=3)", 3, 4) + { + } + bool + run() const + { + return unfolded_contraction(5, 4, 3); + } +}; + +class UnfoldedContractionBig : public TestRunnable +{ +public: + UnfoldedContractionBig() + : TestRunnable("unfolded contraction big (r=20, nv=12, dim=5)", 5, 12) + { + } + bool + run() const + { + return unfolded_contraction(20, 12, 5); + } +}; + +class PolyEvalSmall : public TestRunnable +{ +public: + PolyEvalSmall() + : TestRunnable("polynomial evaluation small (r=4, nv=5, maxdim=4)", 4, 5) + { + } + bool + run() const + { + return poly_eval(4, 5, 4); + } +}; + +class PolyEvalBig : public TestRunnable +{ +public: + PolyEvalBig() + : TestRunnable("polynomial evaluation big (r=244, nv=97, maxdim=2)", 2, 97) + { + } + bool + run() const + { + return poly_eval(244, 97, 2); + } +}; + +class FoldZContSmall : public TestRunnable +{ +public: + FoldZContSmall() + : TestRunnable("folded Z container (r=3,ny=2,nu=2,nup=1,G=2,g=2,dim=3)", + 3, 8) + { + } + bool + run() const + { + return fold_zcont(3, 2, 2, 1, 2, 2, 3); + } +}; + +class FoldZCont : public TestRunnable +{ +public: + FoldZCont() + : TestRunnable("folded Z container (r=13,ny=5,nu=7,nup=4,G=6,g=7,dim=4)", + 4, 25) + { + } + bool + run() const + { + return fold_zcont(13, 5, 7, 4, 6, 7, 4); + } +}; + +class UnfoldZContSmall : public TestRunnable +{ +public: + UnfoldZContSmall() + : TestRunnable("unfolded Z container (r=3,ny=2,nu=2,nup=1,G=2,g=2,dim=3)", + 3, 8) + { + } + bool + run() const + { + return unfold_zcont(3, 2, 2, 1, 2, 2, 3); + } +}; + +class UnfoldZCont : public TestRunnable +{ +public: + UnfoldZCont() + : TestRunnable("unfolded Z container (r=13,ny=5,nu=7,nup=4,G=6,g=7,dim=4", + 4, 25) + { + } + bool + run() const + { + return unfold_zcont(13, 5, 7, 4, 6, 7, 4); + } +}; + +int +main() +{ + TestRunnable *all_tests[50]; + // fill in vector of all tests + int num_tests = 0; + all_tests[num_tests++] = new SmallIndexForwardFold(); + all_tests[num_tests++] = new SmallIndexForwardUnfold(); + all_tests[num_tests++] = new IndexForwardFold(); + all_tests[num_tests++] = new IndexForwardUnfold(); + all_tests[num_tests++] = new SmallIndexBackwardFold(); + all_tests[num_tests++] = new IndexBackwardFold(); + all_tests[num_tests++] = new SmallIndexBackwardUnfold(); + all_tests[num_tests++] = new IndexBackwardUnfold(); + all_tests[num_tests++] = new SmallIndexOffsetFold(); + all_tests[num_tests++] = new SmallIndexOffsetUnfold(); + all_tests[num_tests++] = new IndexOffsetFold(); + all_tests[num_tests++] = new IndexOffsetUnfold(); + all_tests[num_tests++] = new SmallFoldUnfoldFS(); + all_tests[num_tests++] = new SmallFoldUnfoldGS(); + all_tests[num_tests++] = new FoldUnfoldFS(); + all_tests[num_tests++] = new FoldUnfoldGS(); + all_tests[num_tests++] = new SmallFoldUnfoldR(); + all_tests[num_tests++] = new FoldUnfoldR(); + all_tests[num_tests++] = new SmallDenseProd(); + all_tests[num_tests++] = new DenseProd(); + all_tests[num_tests++] = new BigDenseProd(); + all_tests[num_tests++] = new SmallFoldedMonomial(); + all_tests[num_tests++] = new FoldedMonomial(); + all_tests[num_tests++] = new SmallUnfoldedMonomial(); + all_tests[num_tests++] = new UnfoldedMonomial(); + all_tests[num_tests++] = new FoldedContractionSmall(); + all_tests[num_tests++] = new FoldedContractionBig(); + all_tests[num_tests++] = new UnfoldedContractionSmall(); + all_tests[num_tests++] = new UnfoldedContractionBig(); + all_tests[num_tests++] = new PolyEvalSmall(); + all_tests[num_tests++] = new PolyEvalBig(); + all_tests[num_tests++] = new FoldZContSmall(); + all_tests[num_tests++] = new FoldZCont(); + all_tests[num_tests++] = new UnfoldZContSmall(); + all_tests[num_tests++] = new UnfoldZCont(); + + // find maximum dimension and maximum nvar + int dmax = 0; + int nvmax = 0; + for (int i = 0; i < num_tests; i++) + { + if (dmax < all_tests[i]->dim) + dmax = all_tests[i]->dim; + if (nvmax < all_tests[i]->nvar) + nvmax = all_tests[i]->nvar; + } + tls.init(dmax, nvmax); // initialize library + + // launch the tests + int success = 0; + for (int i = 0; i < num_tests; i++) + { + try + { + if (all_tests[i]->test()) + success++; + } + catch (const TLException &e) + { + printf("Caugth TL exception in <%s>:\n", all_tests[i]->getName()); + e.print(); + } + catch (SylvException &e) + { + printf("Caught Sylv exception in <%s>:\n", all_tests[i]->getName()); + e.printMessage(); + } + } + + printf("There were %d tests that failed out of %d tests run.\n", + num_tests - success, num_tests); + + // destroy + for (int i = 0; i < num_tests; i++) + { + delete all_tests[i]; + } + + return 0; +} diff --git a/dynare++/tl/testing/tests.cpp b/dynare++/tl/testing/tests.cpp deleted file mode 100644 index 5e86ebd04..000000000 --- a/dynare++/tl/testing/tests.cpp +++ /dev/null @@ -1,1019 +0,0 @@ -/* $Id: tests.cpp 148 2005-04-19 15:12:26Z kamenik $ */ -/* Copyright 2004, Ondra Kamenik */ - -#include "SylvException.h" -#include "tl_exception.h" -#include "gs_tensor.h" -#include "factory.h" -#include "monoms.h" -#include "t_container.h" -#include "stack_container.h" -#include "t_polynomial.h" -#include "rfs_tensor.h" -#include "ps_tensor.h" -#include "tl_static.h" - -#include -#include -#include - - -class TestRunnable { - char name[100]; -public: - int dim; // dimension of the solved problem - int nvar; // number of variable of the solved problem - TestRunnable(const char* n, int d, int nv) - : dim(d), nvar(nv) - {strncpy(name, n, 100);} - bool test() const; - virtual bool run() const =0; - const char* getName() const - {return name;} -protected: - template - static bool index_forward(const Symmetry& s, const IntSequence& nvs); - - template - static bool index_backward(const Symmetry& s, const IntSequence& nvs); - - template - static bool index_offset(const Symmetry& s, const IntSequence& nvs); - - static bool fold_unfold(const FTensor* folded); - static bool fs_fold_unfold(int r, int nv, int dim) - { - Factory f; - FTensor* folded = f.make(r, nv, dim); - return fold_unfold(folded); // folded deallocated in fold_unfold - } - static bool r_fold_unfold(int r, int nv, int dim) - { - Factory f; - FTensor* folded = f.make(r, nv, dim); - return fold_unfold(folded); // folded deallocated in fold_unfold - } - static bool gs_fold_unfold(int r, const Symmetry& s, const IntSequence& nvs) - { - Factory f; - FTensor* folded = f.make(r, s, nvs); - return fold_unfold(folded); // folded deallocated in fold_unfold - } - - static bool dense_prod(const Symmetry& bsym, const IntSequence& bnvs, - int hdim, int hnv, int rows); - - static bool folded_monomial(int ng, int nx, int ny, int nu, int dim); - - static bool unfolded_monomial(int ng, int nx, int ny, int nu, int dim); - - static bool fold_zcont(int nf, int ny, int nu, int nup, int nbigg, - int ng, int dim); - - static bool unfold_zcont(int nf, int ny, int nu, int nup, int nbigg, - int ng, int dim); - - static bool folded_contraction(int r, int nv, int dim); - - static bool unfolded_contraction(int r, int nv, int dim); - - static bool poly_eval(int r, int nv, int maxdim); - - -}; - -bool TestRunnable::test() const -{ - printf("Running test <%s>\n",name); - clock_t start = clock(); - bool passed = run(); - clock_t end = clock(); - printf("CPU time %8.4g (CPU seconds)..................", - ((double)(end-start))/CLOCKS_PER_SEC); - if (passed) { - printf("passed\n\n"); - return passed; - } else { - printf("FAILED\n\n"); - return passed; - } -} - - -/****************************************************/ -/* definition of TestRunnable static methods */ -/****************************************************/ -template -bool TestRunnable::index_forward(const Symmetry& s, const IntSequence& nvs) -{ - int fails = 0; - int ndecr = 0; - int nincr = 0; - _Ttype dummy(0, TensorDimens(s, nvs)); - typename _Ttype::index run = dummy.end(); - do { - --run; - ndecr++; - typename _Ttype::index run2 = dummy.begin(); - for (int i = 0; i < *run; i++) { - ++run2; - nincr++; - } - if (! (run == run2)) - fails++; - } while (run != dummy.begin()); - - printf("\tnumber of columns = %d\n",dummy.ncols()); - printf("\tnumber of increments = %d\n",nincr); - printf("\tnumber of decrements = %d\n",ndecr); - printf("\tnumber of failures = %d\n",fails); - - return fails == 0; -} - -template -bool TestRunnable::index_backward(const Symmetry& s, const IntSequence& nvs) -{ - int fails = 0; - int ndecr = 0; - int nincr = 0; - _Ttype dummy(0, TensorDimens(s, nvs)); - typename _Ttype::index run = dummy.begin(); - while (run != dummy.end()) { - typename _Ttype::index run2 = dummy.end(); - for (int i = 0; i < dummy.ncols() - *run; i++) { - --run2; - ndecr++; - } - if (! (run == run2)) - fails++; - ++run; - nincr++; - } - - printf("\tnumber of columns = %d\n",dummy.ncols()); - printf("\tnumber of increments = %d\n",nincr); - printf("\tnumber of decrements = %d\n",ndecr); - printf("\tnumber of failures = %d\n",fails); - - return fails == 0; -} - -template -bool TestRunnable::index_offset(const Symmetry& s, const IntSequence& nvs) -{ - int fails = 0; - int nincr = 0; - _Ttype dummy(0, TensorDimens(s, nvs)); - for (typename _Ttype::index run = dummy.begin(); - run != dummy.end(); ++run, nincr++) { - typename _Ttype::index run2(&dummy, run.getCoor()); - if (! (run == run2)) - fails++; - } - - printf("\tnumber of columns = %d\n",dummy.ncols()); - printf("\tnumber of increments = %d\n",nincr); - printf("\tnumber of failures = %d\n",fails); - - return fails == 0; -} - -bool TestRunnable::fold_unfold(const FTensor* folded) -{ - UTensor* unfolded = &(folded->unfold()); - FTensor* folded2 = &(unfolded->fold()); - folded2->add(-1.0, *folded); - double normInf = folded2->getNormInf(); - double norm1 = folded2->getNorm1(); - printf("\tfolded size: (%d, %d)\n",folded->nrows(), folded->ncols()); - printf("\tunfolded size: (%d, %d)\n",unfolded->nrows(), unfolded->ncols()); - printf("\tdifference normInf: %8.4g\n", normInf); - printf("\tdifference norm1: %8.4g\n", norm1); - - delete folded; - delete unfolded; - delete folded2; - - return normInf < 1.0e-15; -} - -bool TestRunnable::dense_prod(const Symmetry& bsym, const IntSequence& bnvs, - int hdim, int hnv, int rows) -{ - Factory f; - FGSContainer* cont = - f.makeCont(hnv, bnvs, bsym.dimen()-hdim+1); - FGSTensor* fh = - f.make(rows, Symmetry(hdim), IntSequence(1, hnv)); - UGSTensor uh(*fh); - FGSTensor fb(rows, TensorDimens(bsym, bnvs)); - fb.getData().zeros(); - clock_t s1 = clock(); - cont->multAndAdd(uh, fb); - clock_t s2 = clock(); - UGSContainer ucont(*cont); - clock_t s3 = clock(); - UGSTensor ub(rows, fb.getDims()); - ub.getData().zeros(); - clock_t s4 = clock(); - ucont.multAndAdd(uh, ub); - clock_t s5 = clock(); - - UGSTensor btmp(fb); - btmp.add(-1, ub); - double norm = btmp.getData().getMax(); - double norm1 = btmp.getNorm1(); - double normInf = btmp.getNormInf(); - - printf("\ttime for folded product: %8.4g\n", - ((double)(s2-s1))/CLOCKS_PER_SEC); - printf("\ttime for unfolded product: %8.4g\n", - ((double)(s5-s4))/CLOCKS_PER_SEC); - printf("\ttime for container convert: %8.4g\n", - ((double)(s3-s2))/CLOCKS_PER_SEC); - printf("\tunfolded difference normMax: %10.6g\n", norm); - printf("\tunfolded difference norm1: %10.6g\n", norm1); - printf("\tunfolded difference normInf: %10.6g\n", normInf); - - delete cont; - delete fh; - - return norm < 1.e-13; -} - -bool TestRunnable::folded_monomial(int ng, int nx, int ny, int nu, int dim) -{ - clock_t gen_time = clock(); - DenseDerivGenerator gen(ng, nx, ny, nu, 5, 0.3, dim); - gen_time = clock()-gen_time; - printf("\ttime for monom generation: %8.4g\n", - ((double)gen_time)/CLOCKS_PER_SEC); - IntSequence nvs(2); nvs[0] = ny; nvs[1] = nu; - double maxnorm = 0; - for (int ydim = 0; ydim <= dim; ydim++) { - Symmetry s(ydim, dim-ydim); - printf("\tSymmetry: ");s.print(); - FGSTensor res(ng, TensorDimens(s, nvs)); - res.getData().zeros(); - clock_t stime = clock(); - for (int d = 1; d <= dim; d++) { - gen.xcont->multAndAdd(*(gen.ts[d-1]), res); - } - stime = clock() - stime; - printf("\t\ttime for symmetry: %8.4g\n", - ((double)stime)/CLOCKS_PER_SEC); - const FGSTensor* mres = gen.rcont->get(s); - res.add(-1.0, *mres); - double normtmp = res.getData().getMax(); - printf("\t\terror normMax: %10.6g\n", normtmp); - if (normtmp > maxnorm) - maxnorm = normtmp; - } - return maxnorm < 1.0e-10; -} - -bool TestRunnable::unfolded_monomial(int ng, int nx, int ny, int nu, int dim) -{ - clock_t gen_time = clock(); - DenseDerivGenerator gen(ng, nx, ny, nu, 5, 0.3, dim); - gen_time = clock()-gen_time; - printf("\ttime for monom generation: %8.4g\n", - ((double)gen_time)/CLOCKS_PER_SEC); - clock_t u_time = clock(); - gen.unfold(); - u_time = clock() - u_time; - printf("\ttime for monom unfolding: %8.4g\n", - ((double)u_time)/CLOCKS_PER_SEC); - IntSequence nvs(2); nvs[0] = ny; nvs[1] = nu; - double maxnorm = 0; - for (int ydim = 0; ydim <= dim; ydim++) { - Symmetry s(ydim, dim-ydim); - printf("\tSymmetry: ");s.print(); - UGSTensor res(ng, TensorDimens(s, nvs)); - res.getData().zeros(); - clock_t stime = clock(); - for (int d = 1; d <= dim; d++) { - gen.uxcont->multAndAdd(*(gen.uts[d-1]), res); - } - stime = clock() - stime; - printf("\t\ttime for symmetry: %8.4g\n", - ((double)stime)/CLOCKS_PER_SEC); - const FGSTensor* mres = gen.rcont->get(s); - FGSTensor foldres(res); - foldres.add(-1.0, *mres); - double normtmp = foldres.getData().getMax(); - printf("\t\terror normMax: %10.6g\n", normtmp); - if (normtmp > maxnorm) - maxnorm = normtmp; - } - return maxnorm < 1.0e-10; -} - -bool TestRunnable::fold_zcont(int nf, int ny, int nu, int nup, int nbigg, - int ng, int dim) -{ - clock_t gen_time = clock(); - SparseDerivGenerator dg(nf, ny, nu, nup, nbigg, ng, - 5, 0.55, dim); - gen_time = clock()-gen_time; - for (int d = 1; d <= dim; d++) { - printf("\tfill of dim=%d tensor: %3.2f %%\n", - d, 100*dg.ts[d-1]->getFillFactor()); - } - printf("\ttime for monom generation: %8.4g\n", - ((double)gen_time)/CLOCKS_PER_SEC); - - IntSequence nvs(4); - nvs[0] = ny; nvs[1] = nu; nvs[2] = nup; nvs[3] = 1; - double maxnorm = 0.0; - - // form ZContainer - FoldedZContainer zc(dg.bigg, nbigg, dg.g, ng, ny, nu); - - for (int d = 2; d <= dim; d++) { - SymmetrySet ss(d, 4); - for (symiterator si(ss); !si.isEnd(); ++si) { - printf("\tSymmetry: ");(*si).print(); - FGSTensor res(nf, TensorDimens(*si, nvs)); - res.getData().zeros(); - clock_t stime = clock(); - for (int l = 1; l <= (*si).dimen(); l++) { - zc.multAndAdd(*(dg.ts[l-1]), res); - } - stime = clock() - stime; - printf("\t\ttime for symmetry: %8.4g\n", - ((double)stime)/CLOCKS_PER_SEC); - const FGSTensor* mres = dg.rcont->get(*si); - res.add(-1.0, *mres); - double normtmp = res.getData().getMax(); - printf("\t\terror normMax: %10.6g\n", normtmp); - if (normtmp > maxnorm) - maxnorm = normtmp; - } - } - return maxnorm < 1.0e-10; -} - -bool TestRunnable::unfold_zcont(int nf, int ny, int nu, int nup, int nbigg, - int ng, int dim) -{ - clock_t gen_time = clock(); - SparseDerivGenerator dg(nf, ny, nu, nup, nbigg, ng, - 5, 0.55, dim); - gen_time = clock()-gen_time; - for (int d = 1; d <= dim; d++) { - printf("\tfill of dim=%d tensor: %3.2f %%\n", - d, 100*dg.ts[d-1]->getFillFactor()); - } - printf("\ttime for monom generation: %8.4g\n", - ((double)gen_time)/CLOCKS_PER_SEC); - - clock_t con_time = clock(); - UGSContainer uG_cont(*(dg.bigg)); - UGSContainer ug_cont(*(dg.g)); - con_time = clock()-con_time; - printf("\ttime for container unfold: %8.4g\n", - ((double)con_time)/CLOCKS_PER_SEC); - - IntSequence nvs(4); - nvs[0] = ny; nvs[1] = nu; nvs[2] = nup; nvs[3] = 1; - double maxnorm = 0.0; - - // form ZContainer - UnfoldedZContainer zc(&uG_cont, nbigg, &ug_cont, ng, ny, nu); - - for (int d = 2; d <= dim; d++) { - SymmetrySet ss(d, 4); - for (symiterator si(ss); !si.isEnd(); ++si) { - printf("\tSymmetry: ");(*si).print(); - UGSTensor res(nf, TensorDimens(*si, nvs)); - res.getData().zeros(); - clock_t stime = clock(); - for (int l = 1; l <= (*si).dimen(); l++) { - zc.multAndAdd(*(dg.ts[l-1]), res); - } - stime = clock() - stime; - printf("\t\ttime for symmetry: %8.4g\n", - ((double)stime)/CLOCKS_PER_SEC); - FGSTensor fold_res(res); - const FGSTensor* mres = dg.rcont->get(*si); - fold_res.add(-1.0, *mres); - double normtmp = fold_res.getData().getMax(); - printf("\t\terror normMax: %10.6g\n", normtmp); - if (normtmp > maxnorm) - maxnorm = normtmp; - } - } - return maxnorm < 1.0e-10; -} - -bool TestRunnable::folded_contraction(int r, int nv, int dim) -{ - Factory fact; - Vector* x = fact.makeVector(nv); - - FFSTensor* forig = fact.make(r, nv, dim); - FFSTensor* f = new FFSTensor(*forig); - clock_t ctime = clock(); - for (int d = dim-1; d > 0; d--) { - FFSTensor* fnew = new FFSTensor(*f, ConstVector(*x)); - delete f; - f = fnew; - } - ctime = clock() - ctime; - Vector res(forig->nrows()); - res.zeros(); - f->multaVec(res, *x); - - UFSTensor u(*forig); - clock_t utime = clock(); - URSingleTensor ux(*x, dim); - Vector v(u.nrows()); - v.zeros(); - u.multaVec(v, ux.getData()); - utime = clock() - utime; - - v.add(-1.0, res); - printf("\ttime for folded contraction: %8.4g\n", - ((double)ctime)/CLOCKS_PER_SEC); - printf("\ttime for unfolded power: %8.4g\n", - ((double)utime)/CLOCKS_PER_SEC); - printf("\terror normMax: %10.6g\n", v.getMax()); - printf("\terror norm1: %10.6g\n", v.getNorm1()); - - delete f; - delete x; - - return (v.getMax() < 1.e-10); -} - -bool TestRunnable::unfolded_contraction(int r, int nv, int dim) -{ - Factory fact; - Vector* x = fact.makeVector(nv); - - FFSTensor* forig = fact.make(r, nv, dim); - UFSTensor uorig(*forig); - delete forig; - UFSTensor* u = new UFSTensor(uorig); - clock_t ctime = clock(); - for (int d = dim-1; d > 0; d--) { - UFSTensor* unew = new UFSTensor(*u, ConstVector(*x)); - delete u; - u = unew; - } - ctime = clock() - ctime; - Vector res(uorig.nrows()); - res.zeros(); - u->multaVec(res, *x); - - clock_t utime = clock(); - URSingleTensor ux(*x, dim); - Vector v(uorig.nrows()); - v.zeros(); - uorig.multaVec(v, ux.getData()); - utime = clock() - utime; - - v.add(-1.0, res); - printf("\ttime for unfolded contraction: %8.4g\n", - ((double)ctime)/CLOCKS_PER_SEC); - printf("\ttime for unfolded power: %8.4g\n", - ((double)utime)/CLOCKS_PER_SEC); - printf("\terror normMax: %10.6g\n", v.getMax()); - printf("\terror norm1: %10.6g\n", v.getNorm1()); - - delete u; - delete x; - - return (v.getMax() < 1.e-10); -} - -bool TestRunnable::poly_eval(int r, int nv, int maxdim) -{ - Factory fact; - Vector* x = fact.makeVector(nv); - - Vector out_ft(r); out_ft.zeros(); - Vector out_fh(r); out_fh.zeros(); - Vector out_ut(r); out_ut.zeros(); - Vector out_uh(r); out_uh.zeros(); - - UTensorPolynomial* up; - { - FTensorPolynomial* fp = fact.makePoly(r, nv, maxdim); - - clock_t ft_cl = clock(); - fp->evalTrad(out_ft, *x); - ft_cl = clock() - ft_cl; - printf("\ttime for folded power eval: %8.4g\n", - ((double)ft_cl)/CLOCKS_PER_SEC); - - clock_t fh_cl = clock(); - fp->evalHorner(out_fh, *x); - fh_cl = clock() - fh_cl; - printf("\ttime for folded horner eval: %8.4g\n", - ((double)fh_cl)/CLOCKS_PER_SEC); - - up = new UTensorPolynomial(*fp); - delete fp; - } - - clock_t ut_cl = clock(); - up->evalTrad(out_ut, *x); - ut_cl = clock() - ut_cl; - printf("\ttime for unfolded power eval: %8.4g\n", - ((double)ut_cl)/CLOCKS_PER_SEC); - - clock_t uh_cl = clock(); - up->evalHorner(out_uh, *x); - uh_cl = clock() - uh_cl; - printf("\ttime for unfolded horner eval: %8.4g\n", - ((double)uh_cl)/CLOCKS_PER_SEC); - - out_ft.add(-1.0, out_ut); - double max_ft = out_ft.getMax(); - out_fh.add(-1.0, out_ut); - double max_fh = out_fh.getMax(); - out_uh.add(-1.0, out_ut); - double max_uh = out_uh.getMax(); - - printf("\tfolded power error norm max: %10.6g\n", max_ft); - printf("\tfolded horner error norm max: %10.6g\n", max_fh); - printf("\tunfolded horner error norm max: %10.6g\n", max_uh); - - delete up; - delete x; - return (max_ft+max_fh+max_uh < 1.0e-10); -} - - -/****************************************************/ -/* definition of TestRunnable subclasses */ -/****************************************************/ -class SmallIndexForwardFold : public TestRunnable { -public: - SmallIndexForwardFold() - : TestRunnable("small index forward for fold (44)(222)", 5, 4) {} - bool run() const - { - Symmetry s(2,3); - IntSequence nvs(2); nvs[0] = 4; nvs[1] = 2; - return index_forward(s, nvs); - } -}; - -class SmallIndexForwardUnfold : public TestRunnable { -public: - SmallIndexForwardUnfold() - : TestRunnable("small index forward for unfold (44)(222)", 5, 4) {} - bool run() const - { - Symmetry s(2,3); - IntSequence nvs(2); nvs[0] = 4; nvs[1] = 2; - return index_forward(s, nvs); - } -}; - -class IndexForwardFold : public TestRunnable { -public: - IndexForwardFold() - : TestRunnable("index forward for fold (55)(222)(22)", 7, 5) {} - bool run() const - { - Symmetry s(2,3,2); - IntSequence nvs(3); nvs[0] = 5; nvs[1] = 2; nvs[2] = 2; - return index_forward(s, nvs); - } -}; - -class IndexForwardUnfold : public TestRunnable { -public: - IndexForwardUnfold() - : TestRunnable("index forward for unfold (55)(222)(22)", 7, 5) {} - bool run() const - { - Symmetry s(2,3,2); - IntSequence nvs(3); nvs[0] = 5; nvs[1] = 2; nvs[2] = 2; - return index_forward(s, nvs); - } -}; - -class SmallIndexBackwardFold : public TestRunnable { -public: - SmallIndexBackwardFold() - : TestRunnable("small index backward for fold (3)(3)(222)", 5, 3) {} - bool run() const - { - Symmetry s(1,1,3); - IntSequence nvs(3); nvs[0] = 3; nvs[1] = 3; nvs[2] = 2; - return index_backward(s, nvs); - } -}; - -class IndexBackwardFold : public TestRunnable { -public: - IndexBackwardFold() - : TestRunnable("index backward for fold (44)(222)(44)", 7, 4) {} - bool run() const - { - Symmetry s(2,3,2); - IntSequence nvs(3); nvs[0] = 4; nvs[1] = 2; nvs[2] = 4; - return index_backward(s, nvs); - } -}; - -class SmallIndexBackwardUnfold : public TestRunnable { -public: - SmallIndexBackwardUnfold() - : TestRunnable("small index backward for unfold (3)(3)(222)", 5, 3) {} - bool run() const - { - Symmetry s(1,1,3); - IntSequence nvs(3); nvs[0] = 3; nvs[1] = 3; nvs[2] = 2; - return index_backward(s, nvs); - } -}; - -class IndexBackwardUnfold : public TestRunnable { -public: - IndexBackwardUnfold() - : TestRunnable("index backward for unfold (44)(222)(44)", 7, 4) {} - bool run() const - { - Symmetry s(2,3,2); - IntSequence nvs(3); nvs[0] = 4; nvs[1] = 2; nvs[2] = 4; - return index_backward(s, nvs); - } -}; - -class SmallIndexOffsetFold : public TestRunnable { -public: - SmallIndexOffsetFold() - : TestRunnable("small index offset for fold (44)(222)", 5, 4) {} - bool run() const - { - Symmetry s(2,3); - IntSequence nvs(2); nvs[0] = 4; nvs[1] = 2; - return index_offset(s, nvs); - } -}; - -class SmallIndexOffsetUnfold : public TestRunnable { -public: - SmallIndexOffsetUnfold() - : TestRunnable("small index offset for unfold (44)(222)", 5, 4) {} - bool run() const - { - Symmetry s(2,3); - IntSequence nvs(2); nvs[0] = 4; nvs[1] = 2; - return index_offset(s, nvs); - } -}; - -class IndexOffsetFold : public TestRunnable { -public: - IndexOffsetFold() - : TestRunnable("index offset for fold (55)(222)(22)", 5, 5) {} - bool run() const - { - Symmetry s(2,3,2); - IntSequence nvs(3); nvs[0] = 5; nvs[1] = 2; nvs[2] = 2; - return index_offset(s, nvs); - } -}; - -class IndexOffsetUnfold : public TestRunnable { -public: - IndexOffsetUnfold() - : TestRunnable("index offset for unfold (55)(222)(22)", 7, 5) {} - bool run() const - { - Symmetry s(2,3,2); - IntSequence nvs(3); nvs[0] = 5; nvs[1] = 2; nvs[2] = 2; - return index_offset(s, nvs); - } -}; - -class SmallFoldUnfoldFS : public TestRunnable { -public: - SmallFoldUnfoldFS() - : TestRunnable("small fold-unfold for full symmetry (444)", 3, 4) {} - bool run() const - { - return fs_fold_unfold(5, 4, 3); - } -}; - - -class SmallFoldUnfoldGS : public TestRunnable { -public: - SmallFoldUnfoldGS() - : TestRunnable("small fold-unfold for gen symmetry (3)(33)(22)", 5, 3) {} - bool run() const - { - Symmetry s(1,2,2); - IntSequence nvs(3); nvs[0] = 3; nvs[1] = 3; nvs[2] = 2; - return gs_fold_unfold(5, s, nvs); - } -}; - -class FoldUnfoldFS : public TestRunnable { -public: - FoldUnfoldFS() - : TestRunnable("fold-unfold for full symmetry (9999)", 4, 9) {} - bool run() const - { - return fs_fold_unfold(5, 9, 4); - } -}; - - -class FoldUnfoldGS : public TestRunnable { -public: - FoldUnfoldGS() - : TestRunnable("fold-unfold for gen symmetry (66)(2)(66)", 5, 6) {} - bool run() const - { - Symmetry s(2,1,2); - IntSequence nvs(3); nvs[0] = 6; nvs[1] = 2; nvs[2] = 6; - return gs_fold_unfold(5, s, nvs); - } -}; - -class SmallFoldUnfoldR : public TestRunnable { -public: - SmallFoldUnfoldR() - : TestRunnable("small fold-unfold for row full symmetry (333)", 3, 3) {} - bool run() const - { - return r_fold_unfold(5, 3, 3); - } -}; - -class FoldUnfoldR : public TestRunnable { -public: - FoldUnfoldR() - : TestRunnable("fold-unfold for row full symmetry (66666)", 5, 6) {} - bool run() const - { - return r_fold_unfold(5, 6, 5); - } -}; - -class SmallDenseProd : public TestRunnable { -public: - SmallDenseProd() - : TestRunnable("small dense prod bsym=1-2,nvs=3-2,h=2-3,r=2",3,3) {} - bool run() const - { - IntSequence bnvs(2); bnvs[0]=3; bnvs[1]=2; - return dense_prod(Symmetry(1,2), bnvs, 2, 3, 2); - } -}; - -class DenseProd : public TestRunnable { -public: - DenseProd() - : TestRunnable("dense prod bsym=2-3,nvs=10-7,h=3-15,r=10",5,15) {} - bool run() const - { - IntSequence bnvs(2); bnvs[0]=10; bnvs[1]=7; - return dense_prod(Symmetry(2,3), bnvs, 3, 15, 10); - } -}; - -class BigDenseProd : public TestRunnable { -public: - BigDenseProd() - : TestRunnable("dense prod bsym=3-2,nvs=13-11,h=3-20,r=20",6,20) {} - bool run() const - { - IntSequence bnvs(2); bnvs[0]=13; bnvs[1]=11; - return dense_prod(Symmetry(3,2), bnvs, 3, 20, 20); - } -}; - -class SmallFoldedMonomial : public TestRunnable { -public: - SmallFoldedMonomial() - : TestRunnable("folded vrs. monoms (g,x,y,u)=(10,4,5,3), dim=4", 4, 8) {} - bool run() const - { - return folded_monomial(10, 4, 5, 3, 4); - } -}; - -class FoldedMonomial : public TestRunnable { -public: - FoldedMonomial() - : TestRunnable("folded vrs. monoms (g,x,y,u)=(20,12,10,5), dim=4", 4, 15) {} - bool run() const - { - return folded_monomial(20, 12, 10, 5, 4); - } -}; - -class SmallUnfoldedMonomial : public TestRunnable { -public: - SmallUnfoldedMonomial() - : TestRunnable("unfolded vrs. monoms (g,x,y,u)=(10,4,5,3), dim=4", 4, 8) {} - bool run() const - { - return unfolded_monomial(10, 4, 5, 3, 4); - } -}; - -class UnfoldedMonomial : public TestRunnable { -public: - UnfoldedMonomial() - : TestRunnable("unfolded vrs. monoms (g,x,y,u)=(20,12,10,5), dim=4", 4, 15) {} - bool run() const - { - return unfolded_monomial(20, 12, 10, 5, 4); - } -}; - -class FoldedContractionSmall : public TestRunnable { -public: - FoldedContractionSmall() - : TestRunnable("folded contraction small (r=5, nv=4, dim=3)", 3, 4) {} - bool run() const - { - return folded_contraction(5, 4, 3); - } -}; - -class FoldedContractionBig : public TestRunnable { -public: - FoldedContractionBig() - : TestRunnable("folded contraction big (r=20, nv=12, dim=5)", 5, 12) {} - bool run() const - { - return folded_contraction(20, 12, 5); - } -}; - -class UnfoldedContractionSmall : public TestRunnable { -public: - UnfoldedContractionSmall() - : TestRunnable("unfolded contraction small (r=5, nv=4, dim=3)", 3, 4) {} - bool run() const - { - return unfolded_contraction(5, 4, 3); - } -}; - -class UnfoldedContractionBig : public TestRunnable { -public: - UnfoldedContractionBig() - : TestRunnable("unfolded contraction big (r=20, nv=12, dim=5)", 5, 12) {} - bool run() const - { - return unfolded_contraction(20, 12, 5); - } -}; - -class PolyEvalSmall : public TestRunnable { -public: - PolyEvalSmall() - : TestRunnable("polynomial evaluation small (r=4, nv=5, maxdim=4)", 4, 5) {} - bool run() const - { - return poly_eval(4, 5, 4); - } -}; - -class PolyEvalBig : public TestRunnable { -public: - PolyEvalBig() - : TestRunnable("polynomial evaluation big (r=244, nv=97, maxdim=2)", 2, 97) {} - bool run() const - { - return poly_eval(244, 97, 2); - } -}; - -class FoldZContSmall : public TestRunnable { -public: - FoldZContSmall() - : TestRunnable("folded Z container (r=3,ny=2,nu=2,nup=1,G=2,g=2,dim=3)", - 3, 8) {} - bool run() const - { - return fold_zcont(3, 2, 2, 1, 2, 2, 3); - } -}; - -class FoldZCont : public TestRunnable { -public: - FoldZCont() - : TestRunnable("folded Z container (r=13,ny=5,nu=7,nup=4,G=6,g=7,dim=4)", - 4, 25) {} - bool run() const - { - return fold_zcont(13, 5, 7, 4, 6, 7, 4); - } -}; - -class UnfoldZContSmall : public TestRunnable { -public: - UnfoldZContSmall() - : TestRunnable("unfolded Z container (r=3,ny=2,nu=2,nup=1,G=2,g=2,dim=3)", - 3, 8) {} - bool run() const - { - return unfold_zcont(3, 2, 2, 1, 2, 2, 3); - } -}; - -class UnfoldZCont : public TestRunnable { -public: - UnfoldZCont() - : TestRunnable("unfolded Z container (r=13,ny=5,nu=7,nup=4,G=6,g=7,dim=4", - 4, 25) {} - bool run() const - { - return unfold_zcont(13, 5, 7, 4, 6, 7, 4); - } -}; - - - -int main() -{ - TestRunnable* all_tests[50]; - // fill in vector of all tests - int num_tests = 0; - all_tests[num_tests++] = new SmallIndexForwardFold(); - all_tests[num_tests++] = new SmallIndexForwardUnfold(); - all_tests[num_tests++] = new IndexForwardFold(); - all_tests[num_tests++] = new IndexForwardUnfold(); - all_tests[num_tests++] = new SmallIndexBackwardFold(); - all_tests[num_tests++] = new IndexBackwardFold(); - all_tests[num_tests++] = new SmallIndexBackwardUnfold(); - all_tests[num_tests++] = new IndexBackwardUnfold(); - all_tests[num_tests++] = new SmallIndexOffsetFold(); - all_tests[num_tests++] = new SmallIndexOffsetUnfold(); - all_tests[num_tests++] = new IndexOffsetFold(); - all_tests[num_tests++] = new IndexOffsetUnfold(); - all_tests[num_tests++] = new SmallFoldUnfoldFS(); - all_tests[num_tests++] = new SmallFoldUnfoldGS(); - all_tests[num_tests++] = new FoldUnfoldFS(); - all_tests[num_tests++] = new FoldUnfoldGS(); - all_tests[num_tests++] = new SmallFoldUnfoldR(); - all_tests[num_tests++] = new FoldUnfoldR(); - all_tests[num_tests++] = new SmallDenseProd(); - all_tests[num_tests++] = new DenseProd(); - all_tests[num_tests++] = new BigDenseProd(); - all_tests[num_tests++] = new SmallFoldedMonomial(); - all_tests[num_tests++] = new FoldedMonomial(); - all_tests[num_tests++] = new SmallUnfoldedMonomial(); - all_tests[num_tests++] = new UnfoldedMonomial(); - all_tests[num_tests++] = new FoldedContractionSmall(); - all_tests[num_tests++] = new FoldedContractionBig(); - all_tests[num_tests++] = new UnfoldedContractionSmall(); - all_tests[num_tests++] = new UnfoldedContractionBig(); - all_tests[num_tests++] = new PolyEvalSmall(); - all_tests[num_tests++] = new PolyEvalBig(); - all_tests[num_tests++] = new FoldZContSmall(); - all_tests[num_tests++] = new FoldZCont(); - all_tests[num_tests++] = new UnfoldZContSmall(); - all_tests[num_tests++] = new UnfoldZCont(); - - // find maximum dimension and maximum nvar - int dmax=0; - int nvmax = 0; - for (int i = 0; i < num_tests; i++) { - if (dmax < all_tests[i]->dim) - dmax = all_tests[i]->dim; - if (nvmax < all_tests[i]->nvar) - nvmax = all_tests[i]->nvar; - } - tls.init(dmax, nvmax); // initialize library - - // launch the tests - int success = 0; - for (int i = 0; i < num_tests; i++) { - try { - if (all_tests[i]->test()) - success++; - } catch (const TLException& e) { - printf("Caugth TL exception in <%s>:\n", all_tests[i]->getName()); - e.print(); - } catch (SylvException& e) { - printf("Caught Sylv exception in <%s>:\n", all_tests[i]->getName()); - e.printMessage(); - } - } - - printf("There were %d tests that failed out of %d tests run.\n", - num_tests - success, num_tests); - - // destroy - for (int i = 0; i < num_tests; i++) { - delete all_tests[i]; - } - - return 0; -} diff --git a/mex/build/libdynare++.am b/mex/build/libdynare++.am index f65b31ead..0ee183beb 100644 --- a/mex/build/libdynare++.am +++ b/mex/build/libdynare++.am @@ -53,27 +53,49 @@ SYLV_SRCS = \ $(TOPDIR)/sylv/cc/KronUtils.cpp TL_SRCS = \ - $(TOPDIR)/tl/cc/normal_moments.cpp \ - $(TOPDIR)/tl/cc/int_sequence.cpp \ - $(TOPDIR)/tl/cc/tensor.cpp \ - $(TOPDIR)/tl/cc/ps_tensor.cpp \ - $(TOPDIR)/tl/cc/pyramid_prod2.cpp \ - $(TOPDIR)/tl/cc/equivalence.cpp \ - $(TOPDIR)/tl/cc/fine_container.cpp \ - $(TOPDIR)/tl/cc/kron_prod.cpp \ - $(TOPDIR)/tl/cc/t_polynomial.cpp \ - $(TOPDIR)/tl/cc/symmetry.cpp \ - $(TOPDIR)/tl/cc/stack_container.cpp \ - $(TOPDIR)/tl/cc/twod_matrix.cpp \ - $(TOPDIR)/tl/cc/sparse_tensor.cpp \ - $(TOPDIR)/tl/cc/sthread.cpp \ - $(TOPDIR)/tl/cc/gs_tensor.cpp \ - $(TOPDIR)/tl/cc/pyramid_prod.cpp \ - $(TOPDIR)/tl/cc/fs_tensor.cpp \ - $(TOPDIR)/tl/cc/permutation.cpp \ - $(TOPDIR)/tl/cc/rfs_tensor.cpp \ - $(TOPDIR)/tl/cc/t_container.cpp \ - $(TOPDIR)/tl/cc/tl_static.cpp + $(TOPDIR)/tl/cc/equivalence.cc \ + $(TOPDIR)/tl/cc/equivalence.hh \ + $(TOPDIR)/tl/cc/fine_container.cc \ + $(TOPDIR)/tl/cc/fine_container.hh \ + $(TOPDIR)/tl/cc/fs_tensor.cc \ + $(TOPDIR)/tl/cc/fs_tensor.hh \ + $(TOPDIR)/tl/cc/gs_tensor.cc \ + $(TOPDIR)/tl/cc/gs_tensor.hh \ + $(TOPDIR)/tl/cc/int_sequence.cc \ + $(TOPDIR)/tl/cc/int_sequence.hh \ + $(TOPDIR)/tl/cc/kron_prod.cc \ + $(TOPDIR)/tl/cc/kron_prod.hh \ + $(TOPDIR)/tl/cc/normal_moments.cc \ + $(TOPDIR)/tl/cc/normal_moments.hh \ + $(TOPDIR)/tl/cc/permutation.cc \ + $(TOPDIR)/tl/cc/permutation.hh \ + $(TOPDIR)/tl/cc/ps_tensor.cc \ + $(TOPDIR)/tl/cc/ps_tensor.hh \ + $(TOPDIR)/tl/cc/pyramid_prod.cc \ + $(TOPDIR)/tl/cc/pyramid_prod.hh \ + $(TOPDIR)/tl/cc/pyramid_prod2.cc \ + $(TOPDIR)/tl/cc/pyramid_prod2.hh \ + $(TOPDIR)/tl/cc/rfs_tensor.cc \ + $(TOPDIR)/tl/cc/rfs_tensor.hh \ + $(TOPDIR)/tl/cc/sparse_tensor.cc \ + $(TOPDIR)/tl/cc/sparse_tensor.hh \ + $(TOPDIR)/tl/cc/stack_container.cc \ + $(TOPDIR)/tl/cc/stack_container.hh \ + $(TOPDIR)/tl/cc/sthread.cc \ + $(TOPDIR)/tl/cc/sthread.hh \ + $(TOPDIR)/tl/cc/symmetry.cc \ + $(TOPDIR)/tl/cc/symmetry.hh \ + $(TOPDIR)/tl/cc/t_container.cc \ + $(TOPDIR)/tl/cc/t_container.hh \ + $(TOPDIR)/tl/cc/t_polynomial.cc \ + $(TOPDIR)/tl/cc/t_polynomial.hh \ + $(TOPDIR)/tl/cc/tensor.cc \ + $(TOPDIR)/tl/cc/tensor.hh \ + $(TOPDIR)/tl/cc/tl_exception.hh \ + $(TOPDIR)/tl/cc/tl_static.cc \ + $(TOPDIR)/tl/cc/tl_static.hh \ + $(TOPDIR)/tl/cc/twod_matrix.cc \ + $(TOPDIR)/tl/cc/twod_matrix.hh INTEG_SRCS = \ $(TOPDIR)/integ/cc/quadrature.cc \ diff --git a/mex/sources/k_order_perturbation/k_ord_dynare.hh b/mex/sources/k_order_perturbation/k_ord_dynare.hh index b90cef267..45c945822 100644 --- a/mex/sources/k_order_perturbation/k_ord_dynare.hh +++ b/mex/sources/k_order_perturbation/k_ord_dynare.hh @@ -21,16 +21,16 @@ #define K_ORD_DYNARE3_H #include #include -#include "t_container.h" -#include "sparse_tensor.h" +#include "t_container.hh" +#include "sparse_tensor.hh" #include "decision_rule.hh" #include "dynamic_model.hh" #include "exception.h" #include "dynare_exception.h" -#include "fs_tensor.h" +#include "fs_tensor.hh" #include "SylvException.h" -#include "tl_exception.h" +#include "tl_exception.hh" #include "kord_exception.hh" #include "nlsolve.h" #include "approximation.hh" diff --git a/windows/dynare.nsi b/windows/dynare.nsi index cda1a0cd5..c3d9dd874 100644 --- a/windows/dynare.nsi +++ b/windows/dynare.nsi @@ -146,7 +146,7 @@ Section "Documentation and examples (Dynare and Dynare++)" File ..\doc\dynare.html\*.html ..\doc\dynare.html\*.png SetOutPath $INSTDIR\doc\dynare++ - File ..\dynare++\doc\dynare++-tutorial.pdf ..\dynare++\doc\dynare++-ramsey.pdf ..\dynare++\sylv\sylvester.pdf ..\dynare++\tl\cc\tl.pdf + File ..\dynare++\doc\dynare++-tutorial.pdf ..\dynare++\doc\dynare++-ramsey.pdf ..\dynare++\sylv\sylvester.pdf CreateShortcut "${SMLOC}\Documentation.lnk" "$INSTDIR\doc"