dynare/dynare++/tl/cc/int_sequence.cc

296 lines
7.0 KiB
C++

// Copyright 2004, Ondra Kamenik
#include "int_sequence.hh"
#include "symmetry.hh"
#include "tl_exception.hh"
#include "pascal_triangle.hh"
#include <iostream>
#include <limits>
#include <numeric>
IntSequence
IntSequence::unfold(const Symmetry &sy) const
{
IntSequence r(sy.dimen());
int k = 0;
for (int i = 0; i < sy.num(); i++)
for (int j = 0; j < sy[i]; j++, k++)
r[k] = operator[](i);
return r;
}
Symmetry
IntSequence::getSymmetry() const
{
Symmetry r(getNumDistinct());
int p = 0;
if (size() > 0)
r[p] = 1;
for (int i = 1; i < size(); i++)
{
if (operator[](i) != operator[](i-1))
p++;
r[p]++;
}
return r;
}
/* This constructs an ordered integer sequence from the given ordered
sequence inserting the given number to the sequence. */
IntSequence
IntSequence::insert(int i) const
{
IntSequence r(size()+1);
int j;
for (j = 0; j < size() && operator[](j) < i; j++)
r[j] = operator[](j);
r[j] = i;
for (; j < size(); j++)
r[j+1] = operator[](j);
return r;
}
IntSequence
IntSequence::insert(int i, int pos) const
{
TL_RAISE_IF(pos < 0 || pos > size(),
"Wrong position for IntSequence::insert()");
IntSequence r(size()+1);
int j;
for (j = 0; j < pos; j++)
r[j] = operator[](j);
r[j] = i;
for (; j < size(); j++)
r[j+1] = operator[](j);
return r;
}
IntSequence &
IntSequence::operator=(const IntSequence &s)
{
TL_RAISE_IF(length != s.length, "Wrong length for in-place IntSequence::operator=");
std::copy_n(s.data, length, data);
return *this;
}
IntSequence &
IntSequence::operator=(IntSequence &&s)
{
TL_RAISE_IF(length != s.length, "Wrong length for in-place IntSequence::operator=");
std::copy_n(s.data, length, data);
return *this;
}
bool
IntSequence::operator==(const IntSequence &s) const
{
return std::equal(data, data+length,
s.data, s.data+s.length);
}
bool
IntSequence::operator<(const IntSequence &s) const
{
return std::lexicographical_compare(data, data+length,
s.data, s.data+s.length);
}
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());
}
void
IntSequence::sort()
{
std::sort(data, data+length);
}
/* 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 (operator[](i-1) > operator[](i))
operator[](i) = operator[](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 (operator[](j-1) > operator[](j))
operator[](j) = operator[](j-1);
cum += s[i];
}
}
/* This returns sum of all elements. Useful for symmetries. */
int
IntSequence::sum() const
{
return std::accumulate(data, data+length, 0);
}
/* This returns product of subsequent items. Useful for Kronecker product
dimensions. */
int
IntSequence::mult(int i1, int i2) const
{
return std::accumulate(data+i1, data+i2,
1, std::multiplies<int>());
}
/* 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 (length > 0)
res++;
for (int i = 1; i < length; 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
{
if (length == 0)
return std::numeric_limits<int>::min();
return *std::max_element(data, data+length);
}
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
{
return std::all_of(data, data+length,
[](int x) { return x >= 0; });
}
bool
IntSequence::isConstant() const
{
if (length < 2)
return true;
return std::all_of(data+1, data+length,
[this](int x) { return x == operator[](0); });
}
bool
IntSequence::isSorted() const
{
return std::is_sorted(data, data+length);
}
/* Debug print. */
void
IntSequence::print() const
{
std::cout << '[';
for (int i = 0; i < size(); i++)
std::cout << operator[](i) << ' ';
std::cout << ']' << std::endl;
}
/* Here we calculate the multinomial coefficients
$\left(\matrix{a\cr b_1,\ldots,b_n}\right)$, where $a=b_1+\ldots+b_n$.
See:
https://en.wikipedia.org/wiki/Binomial_coefficient#Generalization_to_multinomials
https://en.wikipedia.org/wiki/Multinomial_theorem
For n=1, the coefficient is equal to 1.
For n=2, the multinomial coeffs correspond to the binomial coeffs, i.e. the binomial
(a; b) is equal to the multinomial (a; b,a-b).
For n>=3, we have 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)$$ (where the first factor
on the right hand side is to be interpreted as a binomial coefficient)
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. This can be easily seen if the multinomial coefficient is interpreted
as the number of unique permutations of a word, where $a$ is the length of
the word, $n$ is the number of distinct letters, and the $b_i$ are the
number of repetitions of each letter. For example, for a symmetry of the
form $y^4 u^2 v^3$, we want to compute the number of permutations of the word
$yyyyuuvvv$. This is equal to the multinomial coefficient (9; 4,2,3). */
int
IntSequence::noverseq()
{
if (size() == 0 || size() == 1)
return 1;
data[1] += data[0];
return PascalTriangle::noverk(data[1], data[0]) * IntSequence(*this, 1, size()).noverseq();
}