@@ -57,15 +57,63 @@ struct MultivariatePolynomialContainer {
5757class MultivariatePolynomialParametersHelper
5858{
5959 public:
60+ // / \returns number of parameters for given dimension and degree of polynomials at compile time
61+ // / calculates the number of parameters for a multivariate polynomial for given degree: nParameters = (n+d-1 d) -> binomial coefficient
62+ // / see: https://mathoverflow.net/questions/225953/number-of-polynomial-terms-for-certain-degree-and-certain-number-of-variables
63+ template <uint32_t Degree, uint32_t Dim>
64+ GPUd () static constexpr uint32_t getNParametersAllTerms ()
65+ {
66+ if constexpr (Degree == 0 ) {
67+ return binomialCoeff<Dim - 1 , 0 >();
68+ } else {
69+ return binomialCoeff<Dim - 1 + Degree, Degree>() + getNParametersAllTerms<Degree - 1 , Dim>();
70+ }
71+ }
72+
6073 // / \returns number of parameters for given dimension and degree of polynomials
6174 // / calculates the number of parameters for a multivariate polynomial for given degree: nParameters = (n+d-1 d) -> binomial coefficient
6275 // / see: https://mathoverflow.net/questions/225953/number-of-polynomial-terms-for-certain-degree-and-certain-number-of-variables
63- GPUd () static constexpr uint32_t getNParametersAllTerms (const uint32_t degree, const uint32_t dim) { return (degree == 0 ) ? binomialCoeff (dim - 1 , 0 ) : binomialCoeff (dim - 1 + degree, degree) + getNParametersAllTerms (degree - 1 , dim); }
76+ GPUd () static constexpr uint32_t getNParametersAllTerms (uint32_t degree, uint32_t dim)
77+ {
78+ if (degree == 0 ) {
79+ return binomialCoeff (dim - 1 , 0 );
80+ } else {
81+ return binomialCoeff (dim - 1 + degree, degree) + getNParametersAllTerms (degree - 1 , dim);
82+ }
83+ }
84+
85+ // / \returns the number of parameters at compile time for interaction terms only (see: https://en.wikipedia.org/wiki/Combination)
86+ template <uint32_t Degree, uint32_t Dim>
87+ GPUd () static constexpr uint32_t getNParametersInteractionOnly ()
88+ {
89+ if constexpr (Degree == 0 ) {
90+ return binomialCoeff<Dim - 1 , 0 >();
91+ } else {
92+ return binomialCoeff<Dim, Degree>() + getNParametersInteractionOnly<Degree - 1 , Dim>();
93+ }
94+ }
6495
6596 // / \returns the number of parameters for interaction terms only (see: https://en.wikipedia.org/wiki/Combination)
66- GPUd () static constexpr uint32_t getNParametersInteractionOnly (const uint32_t degree, const uint32_t dim) { return (degree == 0 ) ? binomialCoeff (dim - 1 , 0 ) : binomialCoeff (dim, degree) + getNParametersInteractionOnly (degree - 1 , dim); }
97+ GPUd () static constexpr uint32_t getNParametersInteractionOnly (uint32_t degree, uint32_t dim)
98+ {
99+ if (degree == 0 ) {
100+ return binomialCoeff (dim - 1 , 0 );
101+ } else {
102+ return binomialCoeff (dim, degree) + getNParametersInteractionOnly (degree - 1 , dim);
103+ }
104+ }
105+
106+ template <uint32_t Degree, uint32_t Dim, bool InteractionOnly>
107+ GPUd () static constexpr uint32_t getNParameters ()
108+ {
109+ if constexpr (InteractionOnly) {
110+ return getNParametersInteractionOnly<Degree, Dim>();
111+ } else {
112+ return getNParametersAllTerms<Degree, Dim>();
113+ }
114+ }
67115
68- GPUd () static constexpr uint32_t getNParameters (const uint32_t degree, const uint32_t dim, const bool interactionOnly)
116+ GPUd () static constexpr uint32_t getNParameters (uint32_t degree, uint32_t dim, bool interactionOnly)
69117 {
70118 if (interactionOnly) {
71119 return getNParametersInteractionOnly (degree, dim);
@@ -75,13 +123,36 @@ class MultivariatePolynomialParametersHelper
75123 }
76124
77125 private:
126+ // / calculate factorial of n at compile time
127+ // / \return returns n!
128+ template <uint32_t N>
129+ GPUd () static constexpr uint32_t factorial ()
130+ {
131+ if constexpr (N == 0 || N == 1 ) {
132+ return 1 ;
133+ } else {
134+ return N * factorial<N - 1 >();
135+ }
136+ }
137+
78138 // / calculate factorial of n
79139 // / \return returns n!
80- GPUd () static constexpr uint32_t factorial (const uint32_t n) { return (n == 0 ) || (n == 1 ) ? 1 : n * factorial (n - 1 ); }
140+ GPUd () static constexpr uint32_t factorial (uint32_t n) { return n == 0 || n == 1 ? 1 : n * factorial (n - 1 ); }
141+
142+ // / calculates binomial coefficient at compile time
143+ // / \return returns (n k)
144+ template <uint32_t N, uint32_t K>
145+ GPUd () static constexpr uint32_t binomialCoeff ()
146+ {
147+ return factorial<N>() / (factorial<K>() * factorial<N - K>());
148+ }
81149
82150 // / calculates binomial coefficient
83151 // / \return returns (n k)
84- GPUd () static constexpr uint32_t binomialCoeff (const uint32_t n, const uint32_t k) { return factorial (n) / (factorial (k) * factorial (n - k)); }
152+ GPUd () static constexpr uint32_t binomialCoeff (uint32_t n, uint32_t k)
153+ {
154+ return factorial (n) / (factorial (k) * factorial (n - k));
155+ }
85156};
86157
87158// / Helper struct for evaluating a multidimensional polynomial using compile time evaluated formula
@@ -103,7 +174,10 @@ class MultivariatePolynomialHelper : public MultivariatePolynomialParametersHelp
103174 // / evaluates the polynomial for given parameters and coordinates
104175 // / \param par parameters of the polynomials
105176 // / \param x input coordinates
106- GPUd () static constexpr float evalPol (GPUgeneric() const float par[/* number of parameters*/ ], const float x[/* number of dimensions*/ ]) { return par[0 ] + loopDegrees<1 >(par, x); }
177+ GPUd () static constexpr float evalPol (GPUgeneric() const float par[/* number of parameters*/ ], const float x[/* number of dimensions*/ ])
178+ {
179+ return par[0 ] + loopDegrees<1 >(par, x);
180+ }
107181
108182 // / \return returns number of dimensions of the polynomials
109183 GPUd () static constexpr uint32_t getDim () { return Dim; }
@@ -118,19 +192,36 @@ class MultivariatePolynomialHelper : public MultivariatePolynomialParametersHelp
118192 // / computes power of 10
119193 GPUd () static constexpr uint32_t pow10 (const uint32_t n) { return n == 0 ? 1 : 10 * pow10 (n - 1 ); }
120194
195+ template <uint32_t N>
196+ GPUd () static constexpr uint32_t pow10 ()
197+ {
198+ if constexpr (N == 0 ) {
199+ return 1 ;
200+ } else {
201+ return 10 * pow10<N - 1 >();
202+ }
203+ }
204+
121205 // / helper for modulo to extract the digit in an integer a at position b (can be obtained with pow10(digitposition)): e.g. a=1234 b=pow10(2)=100 -> returns 2
122206 GPUd () static constexpr uint32_t mod10 (const uint32_t a, const uint32_t b) { return (a / b) % 10 ; }
123207
208+ template <uint32_t A, uint32_t B>
209+ GPUd () static constexpr uint32_t mod10 ()
210+ {
211+ return (A / B) % 10 ;
212+ }
213+
124214 // / resetting digits of pos for given position to refDigit
125215 GPUd () static constexpr uint32_t resetIndices (const uint32_t degreePol, const uint32_t pos, const uint32_t leftDigit, const uint32_t iter, const uint32_t refDigit);
126216
127- GPUd () static constexpr uint32_t getNewPos (const uint32_t degreePol, const uint32_t pos, const uint32_t digitPos);
217+ template <uint32_t DegreePol, uint32_t Pos, uint32_t DigitPos>
218+ GPUd () static constexpr uint32_t getNewPos ();
128219
129220 // / calculates term e.g. x^3*y
130221 // / \tparam DegreePol max degree of the polynomials
131222 // / \pos decoded information about the current term e.g. 1233 -> x[1]*x[2]*x[3]*x[3] (otherwise an array could be used)
132- template <uint32_t DegreePol>
133- GPUd () static constexpr float prodTerm (const float x[], const uint32_t pos );
223+ template <uint32_t DegreePol, uint32_t Pos >
224+ GPUd () static constexpr float prodTerm (const float x[]);
134225
135226 // / helper function for checking for interaction terms
136227 template <uint32_t DegreePol, uint32_t posNew>
@@ -203,7 +294,10 @@ class MultivariatePolynomialHelper<0, 0, false> : public MultivariatePolynomialP
203294 // / evaluating the polynomial
204295 // / \param par coefficients of the polynomial
205296 // / \param x input coordinates
206- float evalPol (const float par[/* number of parameters*/ ], const float x[/* number of dimensions*/ ]) const { return evalPol (par, x, mDegree , mDim , mInteractionOnly ); }
297+ float evalPol (const float par[/* number of parameters*/ ], const float x[/* number of dimensions*/ ]) const
298+ {
299+ return evalPol (par, x, mDegree , mDim , mInteractionOnly );
300+ }
207301
208302 // / evalutes the polynomial
209303 float evalPol (const float par[], const float x[], const uint32_t degree, const uint32_t dim, const bool interactionOnly) const ;
@@ -248,35 +342,39 @@ GPUd() constexpr uint32_t MultivariatePolynomialHelper<Dim, Degree, InteractionO
248342}
249343
250344template <uint32_t Dim, uint32_t Degree, bool InteractionOnly>
251- GPUd () constexpr uint32_t MultivariatePolynomialHelper<Dim, Degree, InteractionOnly>::getNewPos(const uint32_t degreePol, const uint32_t pos, const uint32_t digitPos)
345+ template <uint32_t DegreePol, uint32_t Pos, uint32_t DigitPos>
346+ GPUd () constexpr uint32_t MultivariatePolynomialHelper<Dim, Degree, InteractionOnly>::getNewPos()
252347{
253- if (degreePol > digitPos ) {
348+ if constexpr (DegreePol > DigitPos ) {
254349 // check if digit of current position is at is max position
255- if (mod10 (pos , pow10 (digitPos) ) == Dim) {
350+ if constexpr (mod10<Pos , pow10<DigitPos>()>( ) == Dim) {
256351 // increase digit of left position
257- const uint32_t leftDigit = digitPos + 1 ;
258- const uint32_t posTmp = pos + pow10 (leftDigit);
259- const uint32_t refDigit = mod10 (posTmp, pow10 (digitPos + 1 ));
352+ constexpr uint32_t LeftDigit = DigitPos + 1 ;
353+ constexpr uint32_t PowLeftDigit = pow10<LeftDigit>();
354+ constexpr uint32_t PosTmp = Pos + PowLeftDigit;
355+ constexpr uint32_t RefDigit = mod10<PosTmp, PowLeftDigit>();
260356
261357 // resetting digits to the right if digit exceeds number of dimensions
262- const uint32_t posReset = resetIndices (degreePol, posTmp, leftDigit - 1 , degreePol - digitPos, refDigit );
358+ constexpr uint32_t PosReset = resetIndices (DegreePol, PosTmp, LeftDigit - 1 , DegreePol - DigitPos, RefDigit );
263359
264360 // check next digit
265- return getNewPos (degreePol, posReset, digitPos + 1 );
361+ return getNewPos<DegreePol, PosReset, DigitPos + 1 >();
362+ } else {
363+ return getNewPos<DegreePol, Pos, DigitPos + 1 >();
266364 }
267- return getNewPos (degreePol, pos, digitPos + 1 );
365+ } else {
366+ return Pos;
268367 }
269- return pos;
270368}
271369
272370template <uint32_t Dim, uint32_t Degree, bool InteractionOnly>
273- template <uint32_t DegreePol>
274- GPUd () constexpr float MultivariatePolynomialHelper<Dim, Degree, InteractionOnly>::prodTerm(const float x[], const uint32_t pos )
371+ template <uint32_t DegreePol, uint32_t Pos >
372+ GPUd () constexpr float MultivariatePolynomialHelper<Dim, Degree, InteractionOnly>::prodTerm(const float x[])
275373{
276374 if constexpr (DegreePol > 0 ) {
277375 // extract index of the dimension which is decoded in the digit
278- const uint32_t index = mod10 (pos , pow10 ( DegreePol - 1 ) );
279- return x[index] * prodTerm<DegreePol - 1 >(x, pos );
376+ const uint32_t index = mod10<Pos , pow10< DegreePol - 1 >()>( );
377+ return x[index] * prodTerm<DegreePol - 1 , Pos >(x);
280378 }
281379 return 1 ;
282380}
@@ -286,7 +384,7 @@ template <uint32_t DegreePol, uint32_t posNew>
286384constexpr bool MultivariatePolynomialHelper<Dim, Degree, InteractionOnly>::checkInteraction()
287385{
288386 if constexpr (DegreePol > 1 ) {
289- constexpr bool isInteraction = mod10 ( posNew, pow10 ( DegreePol - 1 ) ) == mod10 ( posNew, pow10 ( DegreePol - 2 ) );
387+ constexpr bool isInteraction = mod10< posNew, pow10< DegreePol - 1 >()>( ) == mod10< posNew, pow10< DegreePol - 2 >()>( );
290388 if constexpr (isInteraction) {
291389 return true ;
292390 }
@@ -300,16 +398,16 @@ template <uint32_t DegreePol, uint32_t Pos, uint32_t Index>
300398GPUd () constexpr float MultivariatePolynomialHelper<Dim, Degree, InteractionOnly>::sumTerms(GPUgeneric() const float par[], const float x[])
301399{
302400 // checking if the current position is reasonable e.g. if the max dimension is x[4]: for Pos=15 -> x[1]*x[5] the position is set to 22 -> x[2]*x[2]
303- constexpr uint32_t posNew = getNewPos ( DegreePol, Pos, 0 );
304- if constexpr (mod10 (posNew , pow10 ( DegreePol) ) != 1 ) {
401+ constexpr uint32_t PosNew = getNewPos< DegreePol, Pos, 0 >( );
402+ if constexpr (mod10<PosNew , pow10< DegreePol>()>( ) != 1 ) {
305403
306404 // check if all digits in posNew are unequal: For interaction_only terms with x[Dim]*x[Dim]... etc. can be skipped
307- if constexpr (InteractionOnly && checkInteraction<DegreePol, posNew>()) {
308- return sumTerms<DegreePol, posNew + 1 , Index>(par, x);
405+ if constexpr (InteractionOnly && checkInteraction<DegreePol, PosNew>()) {
406+ return sumTerms<DegreePol, PosNew + 1 , Index>(par, x);
407+ } else {
408+ // sum up the term for corrent term and set posotion for next combination
409+ return par[Index] * prodTerm<DegreePol, PosNew>(x) + sumTerms<DegreePol, PosNew + 1 , Index + 1 >(par, x);
309410 }
310-
311- // sum up the term for corrent term and set posotion for next combination
312- return par[Index] * prodTerm<DegreePol>(x, posNew) + sumTerms<DegreePol, posNew + 1 , Index + 1 >(par, x);
313411 }
314412 return 0 ;
315413}
@@ -319,7 +417,7 @@ template <uint32_t DegreePol>
319417GPUd () constexpr float MultivariatePolynomialHelper<Dim, Degree, InteractionOnly>::loopDegrees(GPUgeneric() const float par[], const float x[])
320418{
321419 if constexpr (DegreePol <= Degree) {
322- constexpr uint32_t index{getNParameters ( DegreePol - 1 , Dim, InteractionOnly)}; // offset of the index for accessing the parameters
420+ constexpr uint32_t index{getNParameters< DegreePol - 1 , Dim, InteractionOnly>( )}; // offset of the index for accessing the parameters
323421 return sumTerms<DegreePol, 0 , index>(par, x) + loopDegrees<DegreePol + 1 >(par, x);
324422 }
325423 return 0 ;
0 commit comments