web-dev-qa-db-ja.com

ペアワイズ集計では、かなり間違った結果を得るにはいくつの項が必要ですか?

与えられたfp数の種、たとえばfloat16を使用すると、まったく間違った結果の合計を構成するのは簡単です。たとえば、python/numpyを使用します。

import numpy as np

one = np.float16(1)
ope = np.nextafter(one,one+one)

np.array((ope,one,-one,-one)).cumsum()
# array([1.001, 2.   , 1.   , 0.   ], dtype=float16)

ここでは、単純な合計を強制するためにcumsumを使用しました。独自のデバイスに任せると、numpyは異なる順序の合計を使用し、より適切な答えが得られます。

np.array((ope,one,-one,-one)).sum()
# 0.000977

上記はキャンセルに基づいています。このクラスの例を除外するために、否定的でない用語のみを許可します。単純な合計の場合、合計が非常に間違っている例を示すのは簡単です。以下は、それぞれ10 ^ -4に等しい10 ^ 4個の同一の用語の合計です。

np.full(10**4,10**-4,np.float16).cumsum()
# array([1.0e-04, 2.0e-04, 3.0e-04, ..., 2.5e-01, 2.5e-01, 2.5e-01],
  dtype=float16)

最後の項は4倍オフです。

繰り返しますが、numpyでペアワイズ合計を使用できるようにすると、はるかに優れた結果が得られます。

np.full(10**4,10**-4,np.float16).sum()
# 1.0

ペアワイズ総和に勝る総和を構築することが可能です。 1の解像度より下のepsを選択すると、1、eps、0、eps、3x0、eps、7x0、eps、15x0、epsなどを使用できますが、これには非常に多くの項が含まれます。

私の質問:float16と非負の項のみを使用して、ペアワイズ合計から少なくとも2倍オフの結果を取得するために必要な項の数。

ボーナス:「非否定的」ではなく「肯定的」である同じ質問。可能ですか?

25
Paul Panzer

真の合計が計算された合計を2倍超えるには、深さ1432(つまり2 ^ 1432項)で十分です。

必要な用語の数を2倍未満に決定する方法についてのアイデアがありました。

動的プログラミングを使用して、次の質問に答えます:深度dとターゲット浮動小数点の合計sが与えられた場合、2^d非負のfloat16sとペアワイズ合計sの真の最大合計は何ですか?

その数量をT(d, s)とします。再発する

T(0, s) = s,    for all s.
T(d, s) =            max            (T(d-1, a) + T(d-1, b)),    for all d, s.
          a, b : float16(a + b) = s

反復の各ステップには、2^29の組み合わせのループ処理が含まれます(a ≤ bを想定でき、負の浮動小数点数と特殊な値は制限から外れているため)。必要な深さは、Hansとあなたの回答によって10^4を超えません。私には実現可能のようです。

DPコード:

#include <algorithm>
#include <cstdio>
#include <vector>

using Float16 = int;
using Fixed = unsigned long long;

static constexpr int kExponentBits = 5;
static constexpr int kFractionBits = 10;
static constexpr Float16 kInfinity = ((1 << kExponentBits) - 1)
                                     << kFractionBits;

Fixed FixedFromFloat16(Float16 a) {
  int exponent = a >> kFractionBits;
  if (exponent == 0) {
    return a;
  }
  Float16 fraction = a - (exponent << kFractionBits);
  Float16 significand = (1 << kFractionBits) + fraction;
  return static_cast<Fixed>(significand) << (exponent - 1);
}

bool Plus(Float16 a, Float16 b, Float16* c) {
  Fixed exact_sum = FixedFromFloat16(a) + FixedFromFloat16(b);
  int exponent = 64 - kFractionBits - __builtin_clzll(exact_sum);
  if (exponent <= 0) {
    *c = static_cast<Float16>(exact_sum);
    return true;
  }
  Fixed ulp = Fixed{1} << (exponent - 1);
  Fixed remainder = exact_sum & (ulp - 1);
  Fixed rounded_sum = exact_sum - remainder;
  if (2 * remainder > ulp ||
      (2 * remainder == ulp && (rounded_sum & ulp) != 0)) {
    rounded_sum += ulp;
  }
  exponent = 64 - kFractionBits - __builtin_clzll(rounded_sum);
  if (exponent >= (1 << kExponentBits) - 1) {
    return false;
  }
  Float16 significand = rounded_sum >> (exponent - 1);
  Float16 fraction = significand - (Float16{1} << kFractionBits);
  *c = (exponent << kFractionBits) + fraction;
  return true;
}

int main() {
  std::vector<Fixed> greatest0(kInfinity);
  for (Float16 a = 0; a < kInfinity; a++) {
    greatest0[a] = FixedFromFloat16(a);
  }
  for (int depth = 1; true; depth++) {
    auto greatest1 = greatest0;
    for (Float16 a = 1; a < kInfinity; a++) {
      Fixed greatest0_a = greatest0[a];
      for (Float16 b = a; b < kInfinity; b++) {
        Float16 c;
        if (!Plus(a, b, &c)) {
          continue;
        }
        Fixed& value = greatest1[c];
        value = std::max(value, greatest0_a + greatest0[b]);
      }
    }

    std::vector<double> ratios;
    ratios.reserve(kInfinity - 1);
    for (Float16 a = 1; a < kInfinity; a++) {
      ratios.Push_back(greatest1[a] / static_cast<double>(FixedFromFloat16(a)));
    }
    std::printf("depth %d, ratio = %.17g\n", depth,
                *std::max_element(ratios.begin(), ratios.end()));
    greatest0.swap(greatest1);
  }
}

これを実行し、完了したらアップデートを投稿します。

11
David Eisenstat

非常に多くの項を必要とするため、事実上不可能(ゼロが許可されている場合)または実際には不可能(オーバーフローのためにゼロが許可されていない場合)です。ウィキペディアは、ニコラス・ハイアムによるいくつかの エラー範囲 を要約しています。すべての項が非負であるため、条件数は1であり、n項の相対誤差は| Eとして制限されます。|/| S| ≤εログ2 n /(1-εログ2 n)、ここでεはマシンのイプシロンです。 2倍オフにするためには、| Eが必要です。| ≥| S|、これはεログの場合にのみ可能です2 n≥1/2、これはn≥2と同等1 /(2ε) = 21024 float16の場合。

9
David Eisenstat

残りの問題は、合計が非常に鋭いので、合計(*)にゼロを許可すると、ペアワイズ合計で2の相対エラーが発生するかどうかです。

単純な答えは「はい」です。次のように、cum-sumの悪いシーケンスに指数のゼロを埋め込むことにより(ここで、a1、a2、a3、...は、通常の合計では問題になります):

_a1,
a2,
a3, 0,
a4, 0, 0, 0,
a5, 0, 0, 0, 0, 0, 0, 0,
a6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
...
_

ペアワイズ合計に対して同じ丸め誤差で同じ合計が生成され、nの代わりに2**(n-1)項のみが必要になります。したがって、_10**4_項は通常の総和に対して4の因数を生成できるため、2**(10**4-1)項はペアワイズ総和に対して4の因数を与えることができます。

*:David Eistenstatの回答によると、ゼロを許可しないと、問題が発生する前に合計がオーバーフローします。 (ペアワイズ総和は最後まで再帰すると仮定します。)

0
Hans Olsson