Bagaimana cara menambahkan istilah eksponensial besar secara andal tanpa kesalahan luapan?

24

Masalah yang sangat umum di Markov Chain Monte Carlo melibatkan probabilitas komputasi yang merupakan jumlah dari istilah eksponensial besar,

ea1+ea2+...

dimana komponen dapat berkisar dari sangat kecil sampai yang sangat besar. Pendekatan saya adalah memperhitungkan faktor eksponensial terbesar sehingga:aK:=maxi(ai)

a=K+log(ea1K+ea2K+...)
eaea1+ea2+...

Pendekatan ini masuk akal jika semua elemen a besar, tetapi bukan ide yang bagus jika tidak. Tentu saja, elemen yang lebih kecil tidak berkontribusi pada jumlah floating-point, tapi saya tidak yakin bagaimana cara mengatasinya dengan andal. Dalam kode R, pendekatan saya terlihat seperti:

if ( max(abs(a)) > max(a) )
  K <-  min(a)
else
  K <- max(a)
ans <- log(sum(exp(a-K))) + K

Tampaknya masalah yang cukup umum bahwa harus ada solusi standar, tetapi saya tidak yakin apa itu. Terima kasih atas sarannya.

cboettig
sumber
1
Ini suatu hal. Google untuk 'logsumexp'.

Jawaban:

15

Ada solusi langsung dengan hanya dua melewati data:

pertama

K:=maxiai,

yang memberitahu Anda bahwa, jika ada istilah, maka n

ieaineK.

Karena Anda mungkin tidak memiliki mendekati hampir , Anda tidak perlu khawatir meluap dalam perhitungan dalam presisi ganda .n1020

τ:=ieaiKn

Dengan demikian, hitung dan kemudian solusi Anda adalah .τeKτ

Jack Poulson
sumber
Terima kasih atas notasi yang jelas - tetapi saya percaya ini pada dasarnya adalah apa yang saya usulkan (?) Jika saya perlu menghindari kesalahan underflow ketika beberapa kecil, saya kumpulkan saya memerlukan pendekatan penjumlahan Kahan yang diusulkan oleh @gareth ? ai
cboettig
Ah, sekarang saya mengerti apa yang Anda maksud. Anda sebenarnya tidak perlu khawatir tentang underflow, karena menambahkan hasil yang sangat kecil ke solusi Anda tidak boleh mengubahnya. Jika ada jumlah yang sangat besar, maka Anda harus menjumlahkan nilai kecil terlebih dahulu.
Jack Poulson
Kepada para downvoter: maukah Anda memberi tahu saya apa yang salah dengan jawaban saya?
Jack Poulson
bagaimana jika Anda memiliki banyak istilah yang sangat kecil? Itu bisa terjadi bahwa untuk ini. Jika ada banyak istilah seperti ini, Anda akan memiliki kesalahan besar. eaiK0
becko
10

Untuk menjaga ketepatan saat Anda menambahkan ganda bersama-sama Anda perlu menggunakan Penjumlahan Kahan , ini adalah perangkat lunak yang setara dengan membawa register.

Ini bagus untuk sebagian besar nilai, tetapi jika Anda mendapatkan overflow maka Anda mencapai batas presisi ganda IEEE 754 yang sekitar . Pada titik ini Anda memerlukan representasi baru. Anda dapat mendeteksi kelebihan pada waktu tambahan dengan dan juga mendeteksi eksponen ke besar untuk dievaluasi oleh . Pada titik ini Anda dapat memodifikasi interpretasi ganda dengan menggeser eksponen dan melacak pergeseran ini.e709.783doubleMax - sumSoFar < valueToAddexponent > 709.783

Ini untuk sebagian besar adalah mirip dengan pendekatan Anda mengimbangi eksponen, tetapi versi ini disimpan di basis 2 dan tidak memerlukan pencarian awal untuk menemukan eksponen terbesar. Karenanya .value×2shift

#!/usr/bin/env python
from math import exp, log, ceil

doubleMAX = (1.0 + (1.0 - (2 ** -52))) * (2 ** (2 ** 10 - 1))

def KahanSumExp(expvalues):
  expvalues.sort() # gives precision improvement in certain cases 
  shift = 0 
  esum = 0.0 
  carry = 0.0 
  for exponent in expvalues:
    if exponent - shift * log(2) > 709.783:
      n = ceil((exponent - shift * log(2) - 709.783)/log(2))
      shift += n
      carry /= 2*n
      esum /= 2*n
    elif exponent - shift * log(2) < -708.396:
      n = floor((exponent - shift * log(2) - -708.396)/log(2))
      shift += n
      carry *= 2*n
      esum *= 2*n
    exponent -= shift * log(2)
    value = exp(exponent) - carry 
    if doubleMAX - esum < value:
      shift += 1
      esum /= 2
      value /= 2
    tmp = esum + value 
    carry = (tmp - esum) - value 
    esum = tmp
  return esum, shift

values = [10, 37, 34, 0.1, 0.0004, 34, 37.1, 37.2, 36.9, 709, 710, 711]
value, shift = KahanSumExp(values)
print "{0} x 2^{1}".format(value, shift)
Gareth A. Lloyd
sumber
Penjumlahan Kahan hanyalah salah satu dari keluarga metode "penjumlahan terkompensasi". Jika karena alasan tertentu Kahan tidak berfungsi dengan benar, ada sejumlah metode lain untuk menjumlahkan besaran besaran yang berbeda dan tanda yang berlawanan dengan benar.
JM
@ JM dapatkah Anda memberi saya nama-nama metode lain, saya akan sangat tertarik untuk membacanya. Terima kasih.
Gareth A. Lloyd
1

Pendekatan Anda solid.

Anda tidak perlu tahu persis , cukup baik untuk menghindari overflow. Jadi, Anda mungkin dapat memperkirakan analitis sebelum Anda melakukan pengambilan sampel MCMC.KKK

John D. Cook
sumber
0

Ada paket R yang memasok implementasi yang cepat dan efisien dari "trik log-sum-exp"

http://www.inside-r.org/packages/cran/matrixStats/docs/logSumExp

Fungsi logSumExp menerima vektor numerik lX dan output log (jumlah (exp (lX))) sambil menghindari masalah underflow dan overflow menggunakan metode yang Anda jelaskan.

Kantai
sumber