在萨基马斯,我们在机票中遇到
RuntimeWarning: invalid value encountered in multiply
同时将numpy.float32
数字与非numpy数据相乘; 即numpy应该不会默默地执行此乘法,并且如果使用gcc进行构建,或者如果不是np.float32
而是np.float
或np.float128
,则确实如此。
更准确地说,是从Python调用中获得警告
type(numpy.float32('1.5')).__mul__(numpy.float32('1.5'), x)
其中x
是Sagemath单变量多项式,系数为Sagemath的RealField
类型。 (只有这种特定类型的数据才会触发此操作)。
也就是说,这种无意义的警告可能会在萨基玛斯之外发出; 我们可以在OSX 11.12上使用其库存cc(clang 3.8的某些派生产品),在Linux上使用clang 4.0以及在FreeBSD 11.0上使用clang 4.0或clang 3.7复制它。
尽管我们需要一些技巧在numpy代码中实际实现此__mul__
一些技巧,以查看将哪些函数应用于x ...,但潜在地,我们应该能够在Sagemath之外重现此方法。
我们也在numpy 1.11和1.12上看到了这一点。
与numpy.float32('1.5').__mul__(x)
以及__add__
和__sub__
。
这种类型的错误是包含nan
或infinity
的数组的典型错误。 np.array(x)
返回什么?
@ eric-wieser:返回array(x, dtype=object)
,没有警告。
np.multiply(np.float32('1.5'), x)
发出相同的警告?
numpy.multiply(numpy.float32('1.5'), x)
发出相同的警告。
那么type(x).__rmul__(numpy.float32('1.5'), x)
呢?
另外,如果您可以运行warnings.filterwarnings('error')
,那么您将获得完整的堆栈跟踪
type(x).__rmul__(numpy.float32('1.5'), x)
TypeError: descriptor '__rmul__' requires a 'sage.structure.element.Element' object but received a 'numpy.float32'
x.__rmul__(numpy.float32('1.5'))
可以顺利通过。
似乎我忘记了rmul
工作方式。 我的意思是type(x).__rmul__(x, numpy.float32('1.5'))
,但是我想这和x.__rmul__
,除非x
真的很奇怪
这还会失败吗? np.multiply(np.array(1.5, dtype=object), x)
(这次请输入filterwarnings
)
type(x).__rmul__(x,numpy.float32('1.5'))
会通过而不会发出警告。
而且,顺便说一句,设置warnings.filterwarnings('error')
不会给我任何有趣的东西,
---------------------------------------------------------------------------
RuntimeWarning Traceback (most recent call last)
<ipython-input-50-b3ece847d318> in <module>()
sage: np.multiply(np.array(1.5, dtype=object), x)
---------------------------------------------------------------------------
RuntimeWarning Traceback (most recent call last)
<ipython-input-52-706823a0b5a2> in <module>()
----> 1 np.multiply(np.array(RealNumber('1.5'), dtype=object), x)
RuntimeWarning: invalid value encountered in multiply
嗯,圣人做了一些我没想到的事情。 我猜与float('1.5')
行为相同吗?
好的,这就是我想发生的事情:
numpy
正确地使用了ufunc中的对象循环,最终仅调用PyNumber_Multiply
sage
,某些东西正在FPU中设置错误标志(鼠尾草错误?)numpy
正在从ufunc退出时进行其正常的fpu标志检查(对象循环中的错误?),并找到sage
留下的错误sage: float(1.5).__mul__(x)
NotImplemented
sage: np.float(1.5).__mul__(x)
NotImplemented
sage: np.float32(1.5).__mul__(x)
/usr/home/dima/Sage/sage/src/bin/sage-ipython:1: RuntimeWarning: invalid value encountered in multiply
#!/usr/bin/env python
1.50000000000000*x
sage: np.float64(1.5).__mul__(x)
/usr/home/dima/Sage/sage/src/bin/sage-ipython:1: RuntimeWarning: invalid value encountered in multiply
#!/usr/bin/env python
1.50000000000000*x
值得注意的是np.float is float
出于兼容性原因
为什么np.float32(1.5).__mul__(x)
返回NotImplemented
?
因为它知道它可以通过对象循环将其作为np.multiply
处理,然后在该循环内使用float * x
再次尝试。 不幸的是,围绕该循环的包装器正在拾取由Sage设置的FPU标志。
如果仔细观察,您会发现x.__rmul__
仍在堆栈中更深处被调用
sage: np.float32(1.5)*x
/usr/home/dima/Sage/sage/src/bin/sage-ipython:1: RuntimeWarning: invalid value encountered in multiply
#!/usr/bin/env python
1.50000000000000*x
sage: np.float128(1.5)*x
1.50000000000000*x
sage: np.float64(1.5)*x
/usr/home/dima/Sage/sage/src/bin/sage-ipython:1: RuntimeWarning: invalid value encountered in multiply
#!/usr/bin/env python
1.50000000000000*x
所以看起来好像np.float128
可以,但是
sage: np.float128(1.5).__mul__(x)
/usr/home/dima/Sage/sage/src/bin/sage-ipython:1: RuntimeWarning: invalid value encountered in multiply
#!/usr/bin/env python
1.50000000000000*x
真奇怪也许是一个编译器错误(因为我们从未在gcc上看到它),但是在哪里?
看起来,由于某种原因,FPU标志是在clang上设置的,但在Sage代码中没有设置gcc。 Numpy会为此惹恼,但我高度怀疑是首先将其设置为责备。
不幸的是, numpy
并没有提供任何明确要求FPU标志的方法-这对于将鼠尾草中的问题一分为二非常有用。
我假设这会导致相同的警告(我认为需要numpy 1.12这样做)
mul = np.vectorize(x.__rmul__)
mul(float('1.5'))
不太一样,但是很接近:
/usr/home/dima/Sage/sage/local/lib/python2.7/site-packages/numpy/lib/function_base.py:2652: RuntimeWarning: invalid value encountered in __rmul__ (vectorized)
outputs = ufunc(*inputs)
array(1.50000000000000*x, dtype=object)
好的,这似乎表明np.float32
或np.float64
没有什么特别的,它是生成警告的更通用的numpy机制。
我不知道编译器作者会认为这是一个错误。 警告的工作方式是,处理器会跟踪一些不可思议的状态标志,只要相应的事件发生,它们就会自动置位。 Numpy在开始计算之前先清除它们,然后在最后再次检查它们。 因此,在这些点之间的某个地方,由clang生成的程序集正在进行涉及NaN的一些计算。 但是很难追踪(因为实际的标志设置完全在硬件中完成),并且大多数时候人们并不担心他们的代码如何影响fpu标志。 (众所周知,libm实现是否设置了这些标志也不一致。)确切的结果在很大程度上取决于所生成的确切的asm,因此仅在特定配置中看到它而不在其他配置中看到它就不足为奇了。
是的,这证实了我的怀疑,并为您提供了一种调试方法。 此代码
def check_fpu(f):
@functools.wraps(f)
def wrapped(*args, **kwargs):
excluded = list(range(len(args))) + list(kwargs.keys())
fvec = np.vectorize(f, excluded=excluded)
return fvec(*args, **kwargs)
return wrapped
应用于python函数后,您可以将警告隔离到该代码块中。
我的意思是,这简直很奇怪。 编译器通常不会无故发明然后丢弃NaN。
如果您想对其进行跟踪,那么您可能应该查看在sage中实现这些多项式乘法的代码–奇怪的标志设置可能一直在发生,并且numpy唯一的参与就是使其可见。
还有一个很好的论点,即numpy甚至不应该尝试检查对象循环上的这些标志。 (或者整数循环就可以了,但是这很棘手,因为我们报告整数溢出的方式有点毛病,并且使用了fpu标志。)这是我唯一想到的numpy在这里可以做的事情。
check_fpu()
有错字,应该在fvec = np.vectorize(f, excluded=exclude)
那里。
而且,我们在python2上: import functools32 as functools
。
functools.wraps
不需要python 3,对吗?
如果在setattr()
调用中使用python2的functools,则会出现错误
AttributeError: 'method-wrapper' object has no attribute '__module__'
是的,我想这是在实现FPU标志的RealField
中实现系数算术的任何多精度库。 在每种不同的情况下,是否都使用与numpy相同的编译器来编译基础库? 还是仅使用不同的编译器重建numpy?
是的,我想这是在RealField中实现系数算术的任何多精度库
这就是MPFR。
我们尝试将Sagemath移植到clang + gfortran(主要在OSX和FreeBSD上,其中clang是主要编译器的平台),以便在OSX上构建和运行它更加容易和快捷(FreeBSD更加像是一种获得类似环境而无需安装工具的工具)。 OSX和Apple硬件的麻烦)。
我在这里报告的所有比较都是针对使用clang / clang +++ gfortran而不是gcc / g +++ gfortran的完整版本。
包装程序似乎告诉我们x.__rmul__
设置了FPU标志
check_fpu(x.__rmul__)(np.float32('1.5'))
打印警告,而x.__rmul__(np.float32('1.5'))
不打印警告。
确实-我的假设是x.__rmul__
是用python编写的,并且其源代码可以一分为二,以找到专门设置该标志的位
x.__rmul__
在Cython中,但是仍然是一小段代码需要研究。
如果有一种简单的方法可以将警告更改为错误,则将获得回溯(Cython会为错误生成回溯,但不会为警告生成回溯)。
@jdemeyer IMHO numpy警告在代码路径中发布得更晚,即它是对FPU标志的显式检查而不是中断集的结果。
numpy确实提供了一个将警告更改为错误的接口,但是您所获得的只是回到主iPython解释器循环,而没有任何回溯。
@jdemeyer会之间用Cython代码sig_on()
/ sig_off()
从cysignals抛出一个异常的FPU标志长大的吗? 还是取决于标志?
如果引发SIGFPE
则cysignals会引发异常,如果引发FPU标志,则可能会发生这种情况,具体取决于FPU配置。 但是默认情况下不是。
类似的警告: RuntimeWarning: invalid value encountered in greater
是
来自np.float64(5)>e
。 这里e
是Sagemath常数,指定自然对数的基数2.71828 ...,因此在将其评估为True
方式上,必须“转换”(当然,“ e”知道”的数值近似值,即为数字e.n()
)。
该近似值是上面已经提到的RealField
类型(因此此警告可能密切相关)。
再次,问题是: numpy
如何评估np.float64(5)>e
?
或等效地, np.float64(5).__gt__(e)
弹出相同的警告,因此也可以从此处开始。
注意type(e)
是sage.symbolic.constants_c.E
; 基本上是一些(几乎)虚拟类
包装Sagemath的符号表达式( SR
)。
np.float64(5).__gt__(e.n())
或np.float64(5)>e.n()
没有警告。
如果将e
替换pi
(显然是pi.n()==3.1.415...
),则本质上是相同的(相同的警告/没有警告模式)。
pi
类型SR
,即sage.symbolic.expression.Expression
。
答案是相同的-numpy使用对象循环调用np.greater
。 在最底层,调用e.__lt__(5.0)
。 但是,它再次在之前和之后检查FPU标志,并发现有问题。
大多数ndarray算术/逻辑运算符( -
和divmod
除外)都委托给ufunc。 当用sage对象调用时,这将为这些ufunc调用O
(对象)循环。 这些对象循环将遍历数组(在本例中为0d),在元素上运行普通的python运算符,_但是在这样做时检查FPU标志。
因此,sage再次设置了这些标志。 也许这是错误的迹象,也许不是。
我认为这里有一个很好的论点,即numpy不应该为这些情况检查fpu标志。 @njsmith ,您认为我们应该继续删除对象类型检查吗?
实际上, e.__lt__(5.0)
是一个符号表达式:
sage: type(e.__lt__(np.float32(5.0)))
<type 'sage.symbolic.expression.Expression'>
sage: e.__lt__(np.float32(5.0))
e < 5.0
sage: bool(e.__lt__(np.float32(5.0))) # this is how it's evaluated
True
因此,我真的怀疑它最终是否会被调用,因为确实会得到True
。 另外,上面的check_fpu
包装程序不会使其显示警告,即以下内容可以正常工作。
sage: check_fpu(e.__lt__)(np.float32(5.0))
e < 5.0
fpectl的方法与np.seterr之间的区别是:
np.seterr
运行ufunc循环,然后检查是否设置了任何标志。
fpectl
做起来很神奇,以便每当发生导致设置其中一个标志的操作时,硬件就会产生一个中断,内核将其转换为传递给进程的SIGFPE,并安装一个longjmp
的SIGFPE处理程序直接从信号处理程序中移出到错误处理代码中。
fpectl
方法的一些缺点是:(a)在Windows上根本不起作用,(b)破坏了代码,这些代码在内部导致暂时设置这些标志之一,然后将其清除(此操作是合法的,我希望有libm这样做),(c) longjmp
非常脆弱; 基本上,每次您都要冒段段风险。 对于任意用户定义的ufunc,它肯定不是通用的解决方案。
考虑到所有这些,我认为numpy不会改变。
无论如何,听起来好像是原始问题已解决,所以关闭此问题–如果您要为seterr
更改辩护,可以随时打开一个新问题。
无论如何,听起来好像原来的问题已经解决了
我们确定我们不想禁用对象循环的FPU标志检查吗? 对于numpy来说,这似乎是一个非常明智的更改。
@ eric-wieser:哦,这是一个有趣的想法,是的。 也许值得为此开一个问题:-)。 不过,“正确的事情”非常复杂–理想情况下,我们不应该对对象dtype(用户dtypes)进行特殊包装,并且整数循环也不应该使用它(这对于某些需要检查的体系结构是真正的优化/清除FPU标志的速度非常慢),但是整数循环确实需要一种方法来显式表示整数错误,这是当前通过显式设置FPU标志来实现的,...我不确定这种情况很容易发生。挂水果?
还是我误会了,贤哲只发现了问题,他们仍然需要进行一些小小的改动才能真正解决它?
@njsmith :我不明白您为什么说它在Windows上不起作用。 (不过,这在C99之前的时代是正确的)。 只要您的C编译器符合C99标准,就可以使用现代FPU处理函数(fenv)。 除了fenv之外,它所需要的只是setjmp / longjmp(同样是标准C功能)。
我也很好奇,听说有一个libm在正常操作过程中导致FE异常之一。
(除非被归类为错误)。
@dimpase :您还需要SIGFPE支持,这在C99中未指定。 (好吧,C99说应该有一个SIGFPE,但这是被零除的-它没有指定任何将它连接到浮点异常的方法。)虽然如此,但是我似乎记错了,尽管Windows MSVCRT不支持信号,因此它使用结构化异常处理来模拟SIGFPE,并提供了非标准的_control_fp
函数以针对特定的fp异常启用它,因此Windows支持实际上并不是障碍。 OTOH没关系,因为longjmp
绝对没有很好的理由就不会发生:-)
FWIW,如果一个libm引起了FE异常,然后又将其清除了,我看不出他们为什么会认为这是一个bug。 我不确定是否存在任何此类实现,但这是合理的,如果这样做,那么我们发现的方式是b / c有人告诉我们numpy在X平台上已损坏,唯一的解决方法是还原更改你建议了。
您能否回答我在上一条评论末尾提出的问题?
@njsmith :如果一个libm(或任何其他用户代码)需要引起FE异常并对其进行处理,它将设置自己的FE异常处理程序,保存前一个异常处理程序,并在退出时恢复前一个异常处理程序。
因此,如果底层代码按规则运行,这不是问题。
关于MS对此的支持,自Visual C(++)2013左右以来,
它特别适合与setjmp / longjmp一起使用(希望它在幕后做得多么准确,我不必太担心)。
关于numpy的RuntimeWarning:
fpectl
是实现后者的快速方法。 使用外部工具(例如,通过debbuggers允许您在执行每条指令后检查代码以进行检查)更困难,更慢且更容易出错-例如,错误仅在优化的二进制文件中弹出并不罕见,并且只要您尝试在可很好反驳的二进制文件中找到它,它就会消失。关于Sage中的此问题-仍在解决(希望它仅限于MPFR中的某些问题)。
抱歉,这是一圈转,我需要继续讲其他事情,因此,除非fenv / sigfpe问题上出现新的问题,否则这将是我关于该主题的最后一条信息。 (我仍然对鼠尾草虫是否需要做一些numpy感兴趣)。
如果某个libm(或任何其他用户代码)需要引起FE异常并对其进行处理,则它将设置自己的FE异常处理程序,保存前一个,
您建议采取的操作通常不会导致信号处理程序启动,并且将处理器配置为会导致信号处理程序启动的非标准模式。 代码执行此操作并期望它根本不会触发信号处理程序是完全合理的。
关于MS对此的支持,他们从Visual C(++)2013左右发布了fenv.h。
它专门用于setjmp / longjmp
我不明白你在说什么。 Afaict,fenv.h中的标准功能仅对实现numpy样式的功能有用,MS坚持该标准。 我在那里根本看不到可以与setjmp / longjmp一起使用的任何功能。
用户代码可以通过FP标志快速而轻松地播放,因此这些警告最多应该是可选的。
小心清除通过中间计算设置的标志与快速或松散地进行演奏完全相反。 同样,警告是可选的。
深入了解他们来自哪里的方法对于他们的有用至关重要。 (改进)fpectl是实现后者的快速方法。
从字面上看,您是大约十年来第一个需要SIGFPE调试此类问题的人,然后再次查看鼠尾草的错误注释,看起来您实际上并没有真正发挥作用? 不应导致核心转储。 (看起来cysignals正在覆盖fpectl代码,因此它甚至无法运行。)
如果再次出现这种情况,则需要做一个C调用来启用SIGFPE,然后使用调试器获取堆栈跟踪。 您无需调试即可获取堆栈跟踪。 您需要做的就是不去除符号。 嘿,现在我们知道万一这又发生了。
我知道调试起来确实很令人沮丧,但是当您甚至无法清楚说明这将完成什么工作时,坚持认为其他项目需要更改或维护基本基础结构并没有帮助。 (我实际上不知道您如何认为在此处更改内容的numpy甚至可以帮助您更快地找到这种错误- longjmp
的整个想法是破坏您想要的更精确的信息。)
最后,事实证明,它归结为Clang C编译器中一个长期存在的错误。 基本上,在double
的一定范围内,分配如下
unsigned long a;
double b;
...
a = b;
筹集FE_INVALID
(有时FE_INEXACT
); 顺便说一句,这也影响了其他类型的浮点数据。 出色的MPFR(MPFR必须将双精度复制到任意精度的浮点数中),人们提供了一种解决方法,以解决此问题。
顺便说一句,一个甚至更久远的相关问题(自2010年以来,有12个重复的闭合)clang bug 8100说,没有希望使用clang的fenv.h
来使fpectl
正常工作。 在这方面,Clang并不完全符合C99,并且对此非常害羞。
不确定numpy
是否要对此做任何事情; 也许说RuntimeWarning
可能仅仅是由于编译器错误(引用“ clang bug 8100”,这是一个非常明显的例子)可能会有所帮助。
错误8100不相关; 这是用于C99编译指示禁用浮点优化的,并且没有主流编译器支持这些功能。 numpy似乎(大部分)仍在工作:-)
错误8100的实质是clang不关心FP操作是否正确编译。 尽管律师可能会不同意。 :-)
好,已经提到的错误17686确实是相关的。