Program Listing for File posynomial.hpp

Return to documentation for file (ellip/posynomial.hpp)

#ifndef _POSYNOMIAL_HPP
#define _POSYNOMIAL_HPP

#include "monomial.hpp"
#include <cassert>
#include <valarray>
#include <vector>

template <typename _Tp>
class posynomial
{
    using Vec = std::valarray<double>;
    using _Self = posynomial<_Tp>;

  public:
    explicit posynomial(size_t n, size_t N)
        : _M(N, monomial<_Tp>(n))
    {
    }

    posynomial(const monomial<_Tp>& m)
        : _M(1, m)
    {
    }

    template <typename Up, class Map>
    posynomial(const posynomial<Up>& posyn, const Map& polarity)
        : _M(posyn._M.size(), monomial<_Tp>(posyn._M[0]._a.size()))
    {
        for (auto i = 0; i != _M.size(); ++i)
        {
            _M[i] = monomial<_Tp>(posyn._M[i], polarity);
        }
    }

    ~posynomial() { }

    _Self& operator+=(const monomial<_Tp>& m)
    {
        _M.push_back(m);
        return *this;
    }

    _Self& operator+=(const _Self& P)
    {
        for (auto i = 0; i != P._M.size(); ++i)
            _M.push_back(P._M[i]);
        return *this;
    }

    _Self& operator*=(const monomial<_Tp>& m)
    {
        for (auto i = 0; i != _M.size(); ++i)
            _M[i] *= m;
        return *this;
    }

    _Self& operator/=(const monomial<_Tp>& m)
    {
        for (auto i = 0; i != _M.size(); ++i)
            _M[i] /= m;
        return *this;
    }

    _Self& operator*=(const _Tp& c)
    {
        for (auto i = 0; i != _M.size(); ++i)
            _M[i] *= c;
        return *this;
    }

    _Self& operator/=(const _Tp& c)
    {
        for (auto i = 0; i != _M.size(); ++i)
            _M[i] /= c;
        return *this;
    }

    _Self operator*(const _Self& P) const
    {
        _Self res(_M[0]._a.size(), _M.size() * P._M.size());
        size_t k = 0;
        for (auto i = 0; i != _M.size(); ++i)
        {
            for (auto j = 0; j != P._M.size(); ++j)
            {
                res._M[k++] = _M[i] * P._M[j];
            }
        }
        return res;
    }

    template <typename _Up>
    _Tp lse(const _Up& y) const
    {
        assert(_M[0]._a.size() == y.size());
        const size_t N = _M.size();
        std::valarray<_Tp> p(_Tp(0.0), N);

        for (auto i = 0; i != N; ++i)
            p[i] = _M[i].lse(y);

        if (N == 1) // monomial
            return p[0];

        // f <- log(sum_i(exp(y_i)))
        _Tp sum(0.0);
        for (auto i = 0; i != N; ++i)
        {
            sum += exp(p[i]);
        }

        //_Tp sum = exp(p).sum();
        return log(sum);
    }


    template <typename _Up>
    std::valarray<_Tp> log_exp_gradient(const _Up& y) const
    {
        assert(_M[0]._a.size() == y.size());
        const size_t n = y.size();
        const size_t N = _M.size();
        std::valarray<_Tp> g(n);

        if (N == 1)
        { // monomial
            const Vec& gt = _M[0].gradient(y);
            for (auto i = 0; i != n; ++i)
            {
                g[i] = gt[i];
            }
            return g;
        }


        // g = Aj' * (exp(yj)./sum(exp(yj)));
        // Note that exp(yj) has been previous calculated in _p during the
        // function evaluation.
        std::valarray<_Tp> z(N);

        _Tp sum(0.0);
        for (auto i = 0; i != N; ++i)
        {
            z[i] = exp(_M[i].lse(y));
            sum += z[i];
        }

        z /= sum;

        for (auto i = 0; i != n; ++i)
        {
            g[i] = 0.;
            for (auto l = 0; l != N; ++l)
            {
                g[i] += _M[l]._a[i] * z[l];
            }
        }

        return g;
    }

    template <typename _Up>
    _Tp log_exp_fvalue_with_gradient(const _Up& y, std::valarray<_Tp>& g) const
    {
        assert(_M[0]._a.size() == y.size());
        const size_t n = y.size();
        const size_t N = _M.size();
        std::valarray<_Tp> p(N);

        for (auto i = 0; i != N; ++i)
            p[i] = _M[i].lse(y);

        if (N == 1)
        { // i.e. monomial
            const Vec& gt = _M[0].gradient(y);
            for (auto i = 0; i != n; ++i)
            {
                g[i] = gt[i];
            }
            return p[0];
        }

        // f <- log(sum_i(exp(y_i)))
        _Tp sum(0.0);
        for (auto i = 0; i != N; ++i)
        {
            p[i] = exp(p[i]);
            sum += p[i];
        }

        // g = Aj' * (exp(yj)./sum(exp(yj)));
        p /= sum;

        for (auto i = 0; i != n; ++i)
        {
            g[i] = 0.0;
            for (auto l = 0; l != N; ++l)
            {
                g[i] += _M[l]._a[i] * p[l];
            }
        }

        return log(sum);
    }


    template <typename _Up>
    std::valarray<_Tp> lse_gradient(const _Up& y, _Tp& f) const
    {
        assert(_M[0]._a.size() == y.size());
        const size_t n = y.size();
        const size_t N = _M.size();
        std::valarray<_Tp> p(N);

        for (auto i = 0; i != N; ++i)
            p[i] = _M[i].lse(y);

        if (N == 1)
        { // i.e. monomial
            f = p[0];
            return _M[0].lse_gradient(y);
        }

        // f <- log(sum_i(exp(y_i)))
        _Tp sum(0.0);
        for (auto i = 0; i != N; ++i)
        {
            p[i] = exp(p[i]);
            sum += p[i];
        }

        // g = Aj' * (exp(yj)./sum(exp(yj)));
        p /= sum;

        std::valarray<_Tp> g(n);
        for (auto i = 0; i != n; ++i)
        {
            g[i] = 0.0;
            for (auto l = 0; l != N; ++l)
            {
                g[i] += _M[l]._a[i] * p[l];
            }
        }

        f = log(sum);
        return g;
    }

  public:
    std::vector<monomial<_Tp>> _M;
  public:
    posynomial(const _Self& Q)
        : _M(Q._M)
    {
    }

  public:
    _Self& operator=(const _Self& Q)
    {
        _M = Q._M;
        return *this;
    }
};

template <typename _Tp>
posynomial<_Tp> operator+(const monomial<_Tp>& m1, const monomial<_Tp>& m2)
{
    return posynomial<_Tp>(m1) += m2;
}

template <typename _Tp>
posynomial<_Tp> operator+(posynomial<_Tp> p, const monomial<_Tp>& m)
{
    p += m;
    return p;
}

template <typename _Tp>
posynomial<_Tp> operator*(posynomial<_Tp> p, const monomial<_Tp>& m)
{
    p *= m;
    return p;
}

template <typename _Tp>
posynomial<_Tp> operator/(posynomial<_Tp> p, const monomial<_Tp>& m)
{
    p /= m;
    return p;
}

template <typename _Tp>
posynomial<_Tp> operator*(posynomial<_Tp> p, const _Tp& c)
{
    p *= c;
    return p;
}

template <typename _Tp>
posynomial<_Tp> operator/(posynomial<_Tp> p, const _Tp& c)
{
    p /= c;
    return p;
}

#endif