1 /*
2  * Copyright © 2004 Ondra Kamenik
3  * Copyright © 2019 Dynare Team
4  *
5  * This file is part of Dynare.
6  *
7  * Dynare is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * Dynare is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with Dynare.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 // Stack of containers.
22 
23 /* Here we develop abstractions for stacked containers of tensors. For
24    instance, in perturbation methods for DSGE models, we need the function:
25 
26                    ⎛ G(y*,u,u′,σ) ⎞
27     z(y*,u,u′,σ) = ⎢  g(y*,u,σ)   ⎥
28                    ⎢      y*      ⎥
29                    ⎝      u       ⎠
30 
31    and we need to calculate one step of Faà Di Bruno formula:
32 
33 34     [B_sᵏ]_α₁…αₗ = [f_zˡ]_β₁…βₗ    ∑     ∏  [z_{s^|cₘ|}]_cₘ(α)^βₘ
35                                 c∈ℳₗ,ₖ ᵐ⁼¹
36 
37    where we have containers for derivatives of G and g.
38 
39    The main purpose of this file is to define abstractions for stack of
40    containers and possibly raw variables, and code multAndAdd() method
41    calculating (one step of) the Faà Di Bruno formula for folded and
42    unfolded tensors. Note also, that tensors [f_zˡ] are sparse.
43 
44    The abstractions are built as follows. At the top, there is an
45    interface describing stack of columns. It contains pure virtual
46    methods needed for manipulating the container stack. For technical
47    reasons it is a template. Both versions (folded, and unfolded) provide
48    all interface necessary for implementation of multAndAdd(). The second
49    way of inheritance is first general implementation of the interface
50    StackContainer, and then specific (ZContainer for our specific z).
51    The only method which is virtual also after StackContainer is
52    getType(), which is implemented in the specialization and determines
53    behaviour of the stack. The complete classes are obtained by
54    inheriting from the both branches, as it is drawn below:
55 
56 
57                         StackContainerInterface<FGSTensor>
58                           ↙                           ↘
59             StackContainer<FGSTensor>             FoldedStackContainer
60 61               ZContainer<FGSTensor>                 ↙
62 63                                    FoldedZContainer
64 
65 
66                         StackContainerInterface<UGSTensor>
67                           ↙                           ↘
68             StackContainer<UGSTensor>           UnfoldedStackContainer
69 70               ZContainer<UGSTensor>                 ↙
71 72                                   UnfoldedZContainer
73 
74 
75    We have also two supporting classes StackProduct and KronProdStack
76    and a number of worker classes used as threads. */
77 
78 #ifndef STACK_CONTAINER_H
79 #define STACK_CONTAINER_H
80 
81 #include "int_sequence.hh"
82 #include "equivalence.hh"
83 #include "tl_static.hh"
84 #include "t_container.hh"
85 #include "kron_prod.hh"
86 #include "permutation.hh"
87 #include "sthread.hh"
88 
89 /* Here is the general interface to stack container. The subclasses
90    maintain IntSequence of stack sizes, i.e. size of G, g, y, and
91    u. Then a convenience IntSequence of stack offsets. Then vector of
92    pointers to containers, in our example G, and g.
93 
94    A non-virtual subclass must implement getType() which determines
95    dependency of stack items on symmetries. There are three possible types
96    for a symmetry. Either the stack item derivative wrt. the symmetry is
97    a matrix, or a unit matrix, or zero.
98 
99    Method isZero() returns true if the derivative of a given stack item
100    wrt. to given symmetry is zero as defined by getType() or the
101    derivative is not present in the container. In this way, we can
102    implement the formula conditional some of the tensors are zero, which
103    is not true (they are only missing).
104 
105    Method createPackedColumn() returns a vector of stack derivatives with
106    respect to the given symmetry and of the given column, where all zeros
107    from zero types, or unit matrices are deleted. See kron_prod.hh for an
108    explanation. */
109 
110 template<class _Ttype>
111 class StackContainerInterface
112 {
113 public:
114   using _Ctype = TensorContainer<_Ttype>;
115   enum class itype { matrix, unit, zero };
116 public:
117   StackContainerInterface() = default;
118   virtual ~StackContainerInterface() = default;
119   virtual const IntSequence &getStackSizes() const = 0;
120   virtual IntSequence &getStackSizes() = 0;
121   virtual const IntSequence &getStackOffsets() const = 0;
122   virtual IntSequence &getStackOffsets() = 0;
123   virtual int numConts() const = 0;
124   virtual const _Ctype &getCont(int i) const = 0;
125   virtual itype getType(int i, const Symmetry &s) const = 0;
126   virtual int numStacks() const = 0;
127   virtual bool isZero(int i, const Symmetry &s) const = 0;
128   virtual const _Ttype &getMatrix(int i, const Symmetry &s) const = 0;
129   virtual int getLengthOfMatrixStacks(const Symmetry &s) const = 0;
130   virtual int getUnitPos(const Symmetry &s) const = 0;
131   virtual std::unique_ptr<Vector> createPackedColumn(const Symmetry &s,
132                                                      const IntSequence &coor,
133                                                      int &iu) const = 0;
134   int
getAllSize() const135   getAllSize() const
136   {
137     return getStackOffsets()[numStacks()-1]
138       + getStackSizes()[numStacks()-1];
139   }
140 };
141 
142 /* Here is StackContainer, which implements almost all interface
143    StackContainerInterface but one method getType() which is left for
144    implementation to specializations.
145 
146    It does not own its tensors. */
147 
148 template<class _Ttype>
149 class StackContainer : virtual public StackContainerInterface<_Ttype>
150 {
151 public:
152   using _Stype = StackContainerInterface<_Ttype>;
153   using _Ctype = typename StackContainerInterface<_Ttype>::_Ctype;
154   using itype = typename StackContainerInterface<_Ttype>::itype;
155 protected:
156   int num_conts;
157   IntSequence stack_sizes;
158   IntSequence stack_offsets;
159   std::vector<const _Ctype *> conts;
160 public:
StackContainer(int ns,int nc)161   StackContainer(int ns, int nc)
162     : stack_sizes(ns, 0), stack_offsets(ns, 0),
163       conts(nc)
164   {
165   }
166   const IntSequence &
getStackSizes() const167   getStackSizes() const override
168   {
169     return stack_sizes;
170   }
171   IntSequence &
getStackSizes()172   getStackSizes() override
173   {
174     return stack_sizes;
175   }
176   const IntSequence &
getStackOffsets() const177   getStackOffsets() const override
178   {
179     return stack_offsets;
180   }
181   IntSequence &
getStackOffsets()182   getStackOffsets() override
183   {
184     return stack_offsets;
185   }
186   int
numConts() const187   numConts() const override
188   {
189     return conts.size();
190   }
191   const _Ctype &
getCont(int i) const192   getCont(int i) const override
193   {
194     return *(conts[i]);
195   }
196   itype getType(int i, const Symmetry &s) const override = 0;
197   int
numStacks() const198   numStacks() const override
199   {
200     return stack_sizes.size();
201   }
202   bool
isZero(int i,const Symmetry & s) const203   isZero(int i, const Symmetry &s) const override
204   {
205     TL_RAISE_IF(i < 0 || i >= numStacks(),
206                 "Wrong index to stack in StackContainer::isZero.");
207     return (getType(i, s) == itype::zero
208             || (getType(i, s) == itype::matrix && !conts[i]->check(s)));
209   }
210 
211   const _Ttype &
getMatrix(int i,const Symmetry & s) const212   getMatrix(int i, const Symmetry &s) const override
213   {
214     TL_RAISE_IF(isZero(i, s) || getType(i, s) == itype::unit,
215                 "Matrix is not returned in StackContainer::getMatrix");
216     return conts[i]->get(s);
217   }
218 
219   int
getLengthOfMatrixStacks(const Symmetry & s) const220   getLengthOfMatrixStacks(const Symmetry &s) const override
221   {
222     int res = 0;
223     int i = 0;
224     while (i < numStacks() && getType(i, s) == itype::matrix)
225       res += stack_sizes[i++];
226     return res;
227   }
228 
229   int
getUnitPos(const Symmetry & s) const230   getUnitPos(const Symmetry &s) const override
231   {
232     if (s.dimen() != 1)
233       return -1;
234     int i = numStacks()-1;
235     while (i >= 0 && getType(i, s) != itype::unit)
236       i--;
237     return i;
238   }
239 
240   std::unique_ptr<Vector>
createPackedColumn(const Symmetry & s,const IntSequence & coor,int & iu) const241   createPackedColumn(const Symmetry &s,
242                      const IntSequence &coor, int &iu) const override
243   {
244     TL_RAISE_IF(s.dimen() != coor.size(),
245                 "Incompatible coordinates for symmetry in StackContainer::createPackedColumn");
246 
247     int len = getLengthOfMatrixStacks(s);
248     iu = -1;
249     int i = 0;
250     if (-1 != (i = getUnitPos(s)))
251       {
252         iu = stack_offsets[i] + coor[0];
253         len++;
254       }
255 
256     auto res = std::make_unique<Vector>(len);
257     i = 0;
258     while (i < numStacks() && getType(i, s) == itype::matrix)
259       {
260         const _Ttype &t = getMatrix(i, s);
261         Tensor::index ind(t, coor);
262         Vector subres(*res, stack_offsets[i], stack_sizes[i]);
263         subres = ConstGeneralMatrix(t).getCol(*ind);
264         i++;
265       }
266     if (iu != -1)
267       (*res)[len-1] = 1;
268 
269     return res;
270   }
271 
272 protected:
273   void
calculateOffsets()274   calculateOffsets()
275   {
276     stack_offsets[0] = 0;
277     for (int i = 1; i < stack_offsets.size(); i++)
278       stack_offsets[i] = stack_offsets[i-1] + stack_sizes[i-1];
279   }
280 };
281 
282 class WorkerFoldMAADense;
283 class WorkerFoldMAASparse1;
284 class WorkerFoldMAASparse2;
285 class WorkerFoldMAASparse4;
286 class FoldedStackContainer : virtual public StackContainerInterface<FGSTensor>
287 {
288   friend class WorkerFoldMAADense;
289   friend class WorkerFoldMAASparse1;
290   friend class WorkerFoldMAASparse2;
291   friend class WorkerFoldMAASparse4;
292 public:
293   static constexpr double fill_threshold = 0.00005;
294   void
multAndAdd(int dim,const TensorContainer<FSSparseTensor> & c,FGSTensor & out) const295   multAndAdd(int dim, const TensorContainer<FSSparseTensor> &c,
296              FGSTensor &out) const
297   {
298     if (c.check(Symmetry{dim}))
299       multAndAdd(c.get(Symmetry{dim}), out);
300   }
301   void multAndAdd(const FSSparseTensor &t, FGSTensor &out) const;
302   void multAndAdd(int dim, const FGSContainer &c, FGSTensor &out) const;
303 protected:
304   void multAndAddSparse1(const FSSparseTensor &t, FGSTensor &out) const;
305   void multAndAddSparse2(const FSSparseTensor &t, FGSTensor &out) const;
306   void multAndAddSparse3(const FSSparseTensor &t, FGSTensor &out) const;
307   void multAndAddSparse4(const FSSparseTensor &t, FGSTensor &out) const;
308   void multAndAddStacks(const IntSequence &fi, const FGSTensor &g,
309                         FGSTensor &out, std::mutex &mut) const;
310   void multAndAddStacks(const IntSequence &fi, const GSSparseTensor &g,
311                         FGSTensor &out, std::mutex &mut) const;
312 };
313 
314 class WorkerUnfoldMAADense;
315 class WorkerUnfoldMAASparse1;
316 class WorkerUnfoldMAASparse2;
317 class UnfoldedStackContainer : virtual public StackContainerInterface<UGSTensor>
318 {
319   friend class WorkerUnfoldMAADense;
320   friend class WorkerUnfoldMAASparse1;
321   friend class WorkerUnfoldMAASparse2;
322 public:
323   static constexpr double fill_threshold = 0.00005;
324   void
multAndAdd(int dim,const TensorContainer<FSSparseTensor> & c,UGSTensor & out) const325   multAndAdd(int dim, const TensorContainer<FSSparseTensor> &c,
326              UGSTensor &out) const
327   {
328     if (c.check(Symmetry{dim}))
329       multAndAdd(c.get(Symmetry{dim}), out);
330   }
331   void multAndAdd(const FSSparseTensor &t, UGSTensor &out) const;
332   void multAndAdd(int dim, const UGSContainer &c, UGSTensor &out) const;
333 protected:
334   void multAndAddSparse1(const FSSparseTensor &t, UGSTensor &out) const;
335   void multAndAddSparse2(const FSSparseTensor &t, UGSTensor &out) const;
336   void multAndAddStacks(const IntSequence &fi, const UGSTensor &g,
337                         UGSTensor &out, std::mutex &mut) const;
338 };
339 
340 /* Here is the specialization of the StackContainer. We implement
341    here the z needed in DSGE context. We implement getType() and define
342    a constructor feeding the data and sizes.
343 
344    Note that it has two containers, the first is dependent on four variables
345    G(y*,u,u′,σ), and the second dependent on three variables g(y*,u,σ). So that
346    we would be able to stack them, we make the second container g be dependent
347    on four variables, the third being u′ a dummy and always returning zero if
348    dimension of u′ is positive. */
349 
350 template<class _Ttype>
351 class ZContainer : public StackContainer<_Ttype>
352 {
353 public:
354   using _Tparent = StackContainer<_Ttype>;
355   using _Stype = StackContainerInterface<_Ttype>;
356   using _Ctype = typename _Tparent::_Ctype;
357   using itype = typename _Tparent::itype;
ZContainer(const _Ctype * gss,int ngss,const _Ctype * g,int ng,int ny,int nu)358   ZContainer(const _Ctype *gss, int ngss, const _Ctype *g, int ng,
359              int ny, int nu)
360     : _Tparent(4, 2)
361   {
362     _Tparent::stack_sizes = { ngss, ng, ny, nu };
363     _Tparent::conts[0] = gss;
364     _Tparent::conts[1] = g;
365     _Tparent::calculateOffsets();
366   }
367 
368   /* Here we say, what happens if we derive z. recall the top of the
369      file, how z looks, and code is clear. */
370 
371   itype
getType(int i,const Symmetry & s) const372   getType(int i, const Symmetry &s) const override
373   {
374     if (i == 0)
375       return itype::matrix;
376     if (i == 1)
377       if (s[2] > 0)
378         return itype::zero;
379       else
380         return itype::matrix;
381     if (i == 2)
382       if (s == Symmetry{1, 0, 0, 0})
383         return itype::unit;
384       else
385         return itype::zero;
386     if (i == 3)
387       if (s == Symmetry{0, 1, 0, 0})
388         return itype::unit;
389       else
390         return itype::zero;
391 
392     TL_RAISE("Wrong stack index in ZContainer::getType");
393   }
394 
395 };
396 
397 class FoldedZContainer : public ZContainer<FGSTensor>,
398                          public FoldedStackContainer
399 {
400 public:
401   using _Ctype = TensorContainer<FGSTensor>;
FoldedZContainer(const _Ctype * gss,int ngss,const _Ctype * g,int ng,int ny,int nu)402   FoldedZContainer(const _Ctype *gss, int ngss, const _Ctype *g, int ng,
403                    int ny, int nu)
404     : ZContainer<FGSTensor>(gss, ngss, g, ng, ny, nu)
405   {
406   }
407 };
408 
409 class UnfoldedZContainer : public ZContainer<UGSTensor>,
410                            public UnfoldedStackContainer
411 {
412 public:
413   using _Ctype = TensorContainer<UGSTensor>;
UnfoldedZContainer(const _Ctype * gss,int ngss,const _Ctype * g,int ng,int ny,int nu)414   UnfoldedZContainer(const _Ctype *gss, int ngss, const _Ctype *g, int ng,
415                      int ny, int nu)
416     : ZContainer<UGSTensor>(gss, ngss, g, ng, ny, nu)
417   {
418   }
419 };
420 
421 /* Here we have another specialization of container used in context of
422    DSGE. We define a container for
423 
424     G(y,u,u′,σ)=g**(g*(y,u,σ),u′,σ)
425 
426    For some reason, the symmetry of g** has length 4 although it
427    is really dependent on three variables (To now the reason, consult
428    the ZContainer class declaration). So, it has four stacks, the
429    third one is dummy, and always returns zero. The first stack
430    corresponds to a container of g*. */
431 
432 template<class _Ttype>
433 class GContainer : public StackContainer<_Ttype>
434 {
435 public:
436   using _Tparent = StackContainer<_Ttype>;
437   using _Stype = StackContainerInterface<_Ttype>;
438   using _Ctype = typename StackContainer<_Ttype>::_Ctype;
439   using itype = typename StackContainer<_Ttype>::itype;
GContainer(const _Ctype * gs,int ngs,int nu)440   GContainer(const _Ctype *gs, int ngs, int nu)
441     : StackContainer<_Ttype>(4, 1)
442   {
443     _Tparent::stack_sizes = { ngs, nu, nu, 1 };
444     _Tparent::conts[0] = gs;
445     _Tparent::calculateOffsets();
446   }
447 
448   /* Here we define the dependencies in g**(g*(y,u,σ),u′,σ). Also note, that
449      first derivative of g* w.r.t. σ is always zero, so we also add this
450      information. */
451 
452   itype
getType(int i,const Symmetry & s) const453   getType(int i, const Symmetry &s) const override
454   {
455     if (i == 0)
456       if (s[2] > 0 || s == Symmetry{0, 0, 0, 1})
457         return itype::zero;
458       else
459         return itype::matrix;
460     if (i == 1)
461       if (s == Symmetry{0, 0, 1, 0})
462         return itype::unit;
463       else
464         return itype::zero;
465     if (i == 2)
466       return itype::zero;
467     if (i == 3)
468       if (s == Symmetry{0, 0, 0, 1})
469         return itype::unit;
470       else
471         return itype::zero;
472 
473     TL_RAISE("Wrong stack index in GContainer::getType");
474   }
475 
476 };
477 
478 class FoldedGContainer : public GContainer<FGSTensor>,
479                          public FoldedStackContainer
480 {
481 public:
482   using _Ctype = TensorContainer<FGSTensor>;
FoldedGContainer(const _Ctype * gs,int ngs,int nu)483   FoldedGContainer(const _Ctype *gs, int ngs, int nu)
484     : GContainer<FGSTensor>(gs, ngs, nu)
485   {
486   }
487 };
488 
489 class UnfoldedGContainer : public GContainer<UGSTensor>,
490                            public UnfoldedStackContainer
491 {
492 public:
493   using _Ctype = TensorContainer<UGSTensor>;
UnfoldedGContainer(const _Ctype * gs,int ngs,int nu)494   UnfoldedGContainer(const _Ctype *gs, int ngs, int nu)
495     : GContainer<UGSTensor>(gs, ngs, nu)
496   {
497   }
498 };
499 
500 /* Here we have a support class for product of StackContainers. It
501    only adds a dimension to StackContainer. It selects the symmetries
502    according to equivalence classes passed to the constructor. The
503    equivalence can have permuted classes by some given
504    permutation. Nothing else is interesting. */
505 
506 template<class _Ttype>
507 class StackProduct
508 {
509 public:
510   using _Stype = StackContainerInterface<_Ttype>;
511   using _Ctype = typename _Stype::_Ctype;
512   using itype = typename _Stype::itype;
513 protected:
514   const _Stype &stack_cont;
515   InducedSymmetries syms;
516   Permutation per;
517 public:
StackProduct(const _Stype & sc,const Equivalence & e,const Symmetry & os)518   StackProduct(const _Stype &sc, const Equivalence &e,
519                const Symmetry &os)
520     : stack_cont(sc), syms(e, os), per(e)
521   {
522   }
StackProduct(const _Stype & sc,const Equivalence & e,const Permutation & p,const Symmetry & os)523   StackProduct(const _Stype &sc, const Equivalence &e,
524                const Permutation &p, const Symmetry &os)
525     : stack_cont(sc), syms(e, p, os), per(e, p)
526   {
527   }
528   int
dimen() const529   dimen() const
530   {
531     return syms.size();
532   }
533   int
getAllSize() const534   getAllSize() const
535   {
536     return stack_cont.getAllSize();
537   }
538   const Symmetry &
getProdSym(int ip) const539   getProdSym(int ip) const
540   {
541     return syms[ip];
542   }
543   bool
isZero(const IntSequence & istacks) const544   isZero(const IntSequence &istacks) const
545   {
546     TL_RAISE_IF(istacks.size() != dimen(),
547                 "Wrong istacks coordinates for StackProduct::isZero");
548 
549     bool res = false;
550     int i = 0;
551     while (i < dimen() && !(res = stack_cont.isZero(istacks[i], syms[i])))
552       i++;
553     return res;
554   }
555 
556   itype
getType(int is,int ip) const557   getType(int is, int ip) const
558   {
559     TL_RAISE_IF(is < 0 || is >= stack_cont.numStacks(),
560                 "Wrong index to stack in StackProduct::getType");
561     TL_RAISE_IF(ip < 0 || ip >= dimen(),
562                 "Wrong index to stack container in StackProduct::getType");
563     return stack_cont.getType(is, syms[ip]);
564   }
565 
566   const _Ttype &
getMatrix(int is,int ip) const567   getMatrix(int is, int ip) const
568   {
569     return stack_cont.getMatrix(is, syms[ip]);
570   }
571 
572   std::vector<std::unique_ptr<Vector>>
createPackedColumns(const IntSequence & coor,IntSequence & iu) const573   createPackedColumns(const IntSequence &coor, IntSequence &iu) const
574   {
575     TL_RAISE_IF(iu.size() != dimen(),
576                 "Wrong storage length for unit flags in StackProduct::createPackedColumns");
577     TL_RAISE_IF(coor.size() != per.size(),
578                 "Wrong size of index coor in StackProduct::createPackedColumns");
579     IntSequence perindex(coor.size());
580     std::vector<std::unique_ptr<Vector>> vs;
581     per.apply(coor, perindex);
582     int off = 0;
583     for (int i = 0; i < dimen(); i++)
584       {
585         IntSequence percoor(perindex, off, syms[i].dimen() + off);
586         vs.push_back(stack_cont.createPackedColumn(syms[i], percoor, iu[i]));
587         off += syms[i].dimen();
588       }
589     return vs;
590   }
591 
592   int
getSize(int is) const593   getSize(int is) const
594   {
595     return stack_cont.getStackSizes()[is];
596   }
597 
598   int
numMatrices(const IntSequence & istacks) const599   numMatrices(const IntSequence &istacks) const
600   {
601     TL_RAISE_IF(istacks.size() != dimen(),
602                 "Wrong size of stack coordinates in StackContainer::numMatrices");
603     int ret = 0;
604     int ip = 0;
605     while (ip < dimen() && getType(istacks[ip], ip) == _Stype::matrix)
606       {
607         ret++;
608         ip++;
609       }
610     return ret;
611   }
612 };
613 
614 /* Here we only inherit from Kronecker product KronProdAllOptim, only to
615    allow for a constructor constructing from StackProduct. */
616 
617 template<class _Ttype>
618 class KronProdStack : public KronProdAllOptim
619 {
620 public:
621   using _Ptype = StackProduct<_Ttype>;
622   using _Stype = StackContainerInterface<_Ttype>;
623 
624   /* Here we construct KronProdAllOptim from StackContainer and given
625      selections of stack items from stack containers in the product. We
626      only decide whether to insert matrix, or unit matrix.
627 
628      At this point, we do not call KronProdAllOptim::optimizeOrder(), so
629      the KronProdStack behaves like KronProdAll (i.e. no optimization
630      is done). */
631 
KronProdStack(const _Ptype & sp,const IntSequence & istack)632   KronProdStack(const _Ptype &sp, const IntSequence &istack)
633     : KronProdAllOptim(sp.dimen())
634   {
635     TL_RAISE_IF(sp.dimen() != istack.size(),
636                 "Wrong stack product dimension for KronProdStack constructor");
637 
638     for (int i = 0; i < sp.dimen(); i++)
639       {
640         TL_RAISE_IF(sp.getType(istack[i], i) == _Stype::itype::zero,
641                     "Attempt to construct KronProdStack from zero matrix");
642         if (sp.getType(istack[i], i) == _Stype::itype::unit)
643           setUnit(i, sp.getSize(istack[i]));
644         if (sp.getType(istack[i], i) == _Stype::itype::matrix)
645           {
646             const TwoDMatrix &m = sp.getMatrix(istack[i], i);
647             TL_RAISE_IF(m.nrows() != sp.getSize(istack[i]),
648                         "Wrong size of returned matrix in KronProdStack constructor");
649             setMat(i, m);
650           }
651       }
652   }
653 };
654 
655 class WorkerFoldMAADense : public sthread::detach_thread
656 {
657   const FoldedStackContainer &cont;
658   Symmetry sym;
659   const FGSContainer &dense_cont;
660   FGSTensor &out;
661 public:
662   WorkerFoldMAADense(const FoldedStackContainer &container,
663                      Symmetry s,
664                      const FGSContainer &dcontainer,
665                      FGSTensor &outten);
666   void operator()(std::mutex &mut) override;
667 };
668 
669 class WorkerFoldMAASparse1 : public sthread::detach_thread
670 {
671   const FoldedStackContainer &cont;
672   const FSSparseTensor &t;
673   FGSTensor &out;
674   IntSequence coor;
675 public:
676   WorkerFoldMAASparse1(const FoldedStackContainer &container,
677                        const FSSparseTensor &ten,
678                        FGSTensor &outten, IntSequence c);
679   void operator()(std::mutex &mut) override;
680 };
681 
682 class WorkerFoldMAASparse2 : public sthread::detach_thread
683 {
684   const FoldedStackContainer &cont;
685   const FSSparseTensor &t;
686   FGSTensor &out;
687   IntSequence coor;
688 public:
689   WorkerFoldMAASparse2(const FoldedStackContainer &container,
690                        const FSSparseTensor &ten,
691                        FGSTensor &outten, IntSequence c);
692   void operator()(std::mutex &mut) override;
693 };
694 
695 class WorkerFoldMAASparse4 : public sthread::detach_thread
696 {
697   const FoldedStackContainer &cont;
698   const FSSparseTensor &t;
699   FGSTensor &out;
700   IntSequence coor;
701 public:
702   WorkerFoldMAASparse4(const FoldedStackContainer &container,
703                        const FSSparseTensor &ten,
704                        FGSTensor &outten, IntSequence c);
705   void operator()(std::mutex &mut) override;
706 };
707 
708 class WorkerUnfoldMAADense : public sthread::detach_thread
709 {
710   const UnfoldedStackContainer &cont;
711   Symmetry sym;
712   const UGSContainer &dense_cont;
713   UGSTensor &out;
714 public:
715   WorkerUnfoldMAADense(const UnfoldedStackContainer &container,
716                        Symmetry s,
717                        const UGSContainer &dcontainer,
718                        UGSTensor &outten);
719   void operator()(std::mutex &mut) override;
720 };
721 
722 class WorkerUnfoldMAASparse1 : public sthread::detach_thread
723 {
724   const UnfoldedStackContainer &cont;
725   const FSSparseTensor &t;
726   UGSTensor &out;
727   IntSequence coor;
728 public:
729   WorkerUnfoldMAASparse1(const UnfoldedStackContainer &container,
730                          const FSSparseTensor &ten,
731                          UGSTensor &outten, IntSequence c);
732   void operator()(std::mutex &mut) override;
733 };
734 
735 class WorkerUnfoldMAASparse2 : public sthread::detach_thread
736 {
737   const UnfoldedStackContainer &cont;
738   const FSSparseTensor &t;
739   UGSTensor &out;
740   IntSequence coor;
741 public:
742   WorkerUnfoldMAASparse2(const UnfoldedStackContainer &container,
743                          const FSSparseTensor &ten,
744                          UGSTensor &outten, IntSequence c);
745   void operator()(std::mutex &mut) override;
746 };
747 
748 #endif
749