Вычисление функции log-sum-exp в С++

Есть ли в стандартной библиотеке С++ какие-либо функции, которые вычисляют логарифм суммы экспонент? Если нет, то как мне написать свой собственный? Есть ли у вас предложения, не упомянутые в этой вики-статье?

Меня особенно беспокоит возможность того, что слагаемые будут неполными. Это ситуация, когда вы возводите в степень отрицательные числа, которые имеют большое абсолютное значение. Я использую С++11.


person Taylor    schedule 29.08.2017    source источник
comment
В стандартной библиотеке нет ничего для этого конкретного приложения, но Boost.Multiprecision обеспечивает арифметику произвольной точности с очень полной поддержкой трансцендентных и специальных функций.   -  person Jason R    schedule 29.08.2017
comment
Если вы хотите написать свой собственный, то почему вы спросили о поддержке библиотек? У меня нет никаких рекомендаций по реализации, нет.   -  person Jason R    schedule 29.08.2017


Ответы (2)


(Вариант кода shuvro на С++ 11 с использованием стандартной библиотеки в соответствии с вопросом.)

template <typename Iter>
std::iterator_traits<Iter>::value_type
log_sum_exp(Iter begin, Iter end)
{
  using VT = std::iterator_traits<Iter>::value_type{};
  if (begin==end) return VT{};
  using std::exp;
  using std::log;
  auto max_elem = *std::max_element(begin, end);
  auto sum = std::accumulate(begin, end, VT{}, 
     [max_elem](VT a, VT b) { return a + exp(b - max_elem); });
  return max_elem + log(sum);
}

Эта версия является более общей — она будет работать с любым типом значения в любом типе контейнера, если в нем есть соответствующие операторы. В частности, он будет использовать std::exp и std::log, если тип значения не имеет собственных перегрузок.

Чтобы быть действительно устойчивым к потере значимости, даже для неизвестных числовых типов, вероятно, было бы полезно отсортировать значения. Если вы отсортируете входные данные, самым первым значением будет max_elem, поэтому первым членом sum будет exp(VT{0}, что предположительно равно VT{1}. Это явно свободно от нижнего потока.

person MSalters    schedule 30.08.2017

Если вам нужен меньший код, эта реализация может выполнить эту работу:

double log_sum_exp(double arr[], int count) 
{
   if(count > 0 ){
      double maxVal = arr[0];
      double sum = 0;

      for (int i = 1 ; i < count ; i++){
         if (arr[i] > maxVal){
            maxVal = arr[i];
         }
      }

      for (int i = 0; i < count ; i++){
         sum += exp(arr[i] - maxVal);
      }
      return log(sum) + maxVal;

   }
   else
   {
      return 0.0;
   }
}

Вы можете увидеть более надежную реализацию в блоге Takeda 25 (японский) (или , см. на английском языке через Google Translate).

person Shuvro    schedule 29.08.2017
comment
log(exp(x1)+exp(x2)+...+exp(xn))=log(exp(xmax)exp(x1-xmax)+exp(xmax)exp(x2-xmax)+...+ exp(xmax)exp(xn-xmax))=log(exp(xmax)*(exp(x1-xmax)+exp(x2-xmax)+...+exp(xn-xmax))) = xmax + log (exp(x1-xmax)+exp(x2-xmax)+...+exp(xn-xmax)). - Если я не ошибаюсь, умножения на maxVal нет. - person rbelli; 30.08.2017