c++ RooFit中的变量范围行为不正确

qxgroojn  于 2022-12-24  发布在  其他
关注(0)|答案(1)|浏览(180)

RooFit包允许我导入一些TTree分支,但是它将这些分支中包含的值限制在RooRealVar设置的最小值和最大值之间。

RooRealVar t1("t1", "Some variable", 0.0, 1.0);
RooDataSet data("data", "My Dataset", RooArgSet(t1), Import(*myttree));

只要TTree myttree包含一个分支t1,这一切都很好,直到你开始接近范围内的浮点值,我的特殊问题发生是因为我有一个像t1这样的变量,它Map到一个指数分布的变量,我试图拟合这个分布,但是对于t1 ~ 0.0的值,拟合失败。我的解决方案是稍微更改范围,以截断树中t1的存储值实际上为零或接近零的潜在事件(下面的代码都是在ROOT解释器中运行的,但我已经确认它在编译代码中也能正常工作):

root[0] RooRealVar t1("t1", "Some variable", 0.001, 1.0);

但是,请注意以下恼人的行为:

root[1] t1 = 0.000998;
root[2] t1.getVal();
(double) 0.0010000000
// this is correct, as 0.000998 < 0.001 so RooFit set it as the lower limit
root[3] t1 = 0.000999;
root[4] t1.getVal();
(double) 0.00099900000
// this is incorrect.

是的,在第二种情况下打印了额外的零,我也不明白,但我最关心的是无法识别0.000999 < 0.001。当我稍后在if语句中比较这些值时,我发现C++可以区分它们。这里的所有内容似乎都是双精度的,我一直在跟踪代码,看看精度错误似乎出现在哪里。如果我错了,请纠正我,但是浮点数仍然应该保持这些数字达到比较精度,这是怎么回事?,如果这是浮点错误问题,解决它的最好方法是什么?,我有几个事件的值是t1 = 0.000999874,把边界改成0.0001也没什么用,仍然有事件在这个边界上。
编辑:我想强调的是,虽然这可能是一个浮点问题,但它真的不应该是。例如,下面的代码是有效的:

root[0] RooRealVar t1("t1", "Some variable", 0.001, 1.0);
root[1] t1 = 0.000999;
root[2] t1.getVal() < 0.001;
(bool) true
amrnrhlw

amrnrhlw1#

好了,各位,我找到了答案(我讨厌这个答案)。它实际上与浮点运算几乎没有关系,老实说,我不知道为什么代码是这样写的。从确定一个值是否“在范围内”的源代码来看:

bool RooAbsRealLValue::inRange(double value, const char* rangeName, double* clippedValPtr) const
{
  // double range = getMax() - getMin() ; // ok for +/-INIFINITY
  double clippedValue(value);
  bool isInRange(true) ;
 
  const RooAbsBinning& binning = getBinning(rangeName) ;
  double min = binning.lowBound() ;
  double max = binning.highBound() ;
 
  // test this value against our upper fit limit
  if(!RooNumber::isInfinite(max) && value > (max+1e-6)) {
    clippedValue = max;
    isInRange = false ;
  }
  // test this value against our lower fit limit
  if(!RooNumber::isInfinite(min) && value < min-1e-6) {
    clippedValue = min ;
    isInRange = false ;
  }
 
  if (clippedValPtr) *clippedValPtr=clippedValue ;
 
  return isInRange ;
}

正如我们在这里所看到的,RooFit * 实际上并不 * 检查min < val < max甚至min <= val <= max,而是min - 1e-6 < value < max + 1e-6!我找不到一个地方明确地记录了这一点,但我更关心的是inRange有一个单独的实现,它接受变量名(或逗号分隔的变量名列表),并返回与先前实现不兼容的结果:

bool RooAbsRealLValue::inRange(const char* name) const
{
  const double val = getVal() ;
  const double epsilon = 1e-8 * fabs(val) ;
  if (!name || name[0] == '\0') {
    const auto minMax = getRange(nullptr);
    return minMax.first - epsilon <= val && val <= minMax.second + epsilon;
  }
 
  const auto& ranges = ROOT::Split(name, ",");
  return std::any_of(ranges.begin(), ranges.end(), [val,epsilon,this](const std::string& range){
    const auto minMax = this->getRange(range.c_str());
    return minMax.first - epsilon <= val && val <= minMax.second + epsilon;
  });
}

在这里,我们可以看到创建了一个epsilon = 1e-8 * fabs(val),而不是第一个定义中给出的任意1e-6。这个比较使用了<=,而不是<。应该注意的是,当以这种方式导入时,用于过滤树的方法使用了第一个实现(源代码在这里)。
在这个过程中的某个地方(我不完全确定具体在哪里),这些任意的比较导致了下面的矛盾行为:

root[0] RooRealVar t1("t1", "Some variable", 0.001, 1.0);
root[1] t1 = 0.001 - 1e-6;
(RooAbsArg &) RooRealVar::t1 = 0.000999  L(0.001 - 1) 
root[2] t1 = 0.001 - 1e-8 * 0.001;
(RooAbsArg &) RooRealVar::t1 = 0.001  L(0.001 - 1)
root[3] t1 = 0.00099999999;
(RooAbsArg &) RooRealVar::t1 = 0.001  L(0.001 - 1)

我会把这归类为bug。在任何情况下,0.00099900000都不应该被归类为在(0.001 - 1)的范围内,而0.00099999999不是!

相关问题