<p>numpy.isclose vs math.isclose</p>

Creado en 5 dic. 2017  ·  78Comentarios  ·  Fuente: numpy/numpy

numpy.isclose (https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.isclose.html):

abs(a - b) <= (atol + rtol * abs(b))

math.isclose (https://docs.python.org/3/library/math.html#math.isclose):

abs(a - b) <= max(rtol * max(abs(a), abs(b)), atol)

Tenga en cuenta que la ecuación de Numpy no es simétrica y correlaciona los parámetros atol y rtol , ambos son cosas malas (IMO).

Aquí hay una situación en la que Numpy marca "incorrectamente" dos números como iguales:

a = 0.142253
b = 0.142219
rtol = 1e-4
atol = 2e-5

# true because atol interferes with the rtol measurement
abs(a - b) <= (atol + rtol * abs(b))
Out[24]: True

# correct result, rtol fails
abs(a - b) <= max(rtol * max(abs(a), abs(b)), atol)
Out[29]: False

Aquí hay otro, este problema de simetría de caso:

a = 0.142253
b = 0.142219
rtol = 1e-4
atol = 1.9776e-05

# relative to b
abs(a - b) <= (atol + rtol * abs(b))
Out[73]: False

#relative to a
abs(a - b) <= (atol + rtol * abs(a))
Out[74]: True

# math one has no problems with this
abs(a - b) <= max(rtol * max(abs(a), abs(b)), atol)
Out[75]: False

La versión matemática de Python parece ser a prueba de balas, ¿debería Numpy comenzar a usar esto? ¿Hay algún beneficio de usar la versión Numpy?

57 - Close?

Comentario más útil

@njsmith : gracias por traerme.

Un poco de historia: cuando propuse isclose para stdlib, ciertamente consideramos numpy como técnica anterior. Si fuera solo yo, puede que haya usado un enfoque compatible, por el bien de la compatibilidad :-).

Pero el resto de la comunidad pensó que era más importante hacer lo que era "correcto" para Python, por lo que se produjo una larga discusión ... Traté de capturar la mayor parte del punto en el PEP, si quieres ir a buscar.

Aquí está la referencia a numpy:

https://www.python.org/dev/peps/pep-0485/#numpy -isclose

Puede ver que se hicieron los mismos puntos que en esta discusión.

Al final, salieron tres puntos clave:

1) un enfoque simétrico resultaría en la menor sorpresa.

2) la tolerancia absoluta predeterminada probablemente debería ser cero, para no hacer suposiciones sobre el orden de magnitud de los argumentos.

3) la diferencia entre las pruebas "débil" y "fuerte" era irrelevante cuando se usaba con pequeñas tolerancias, como es el caso de uso esperado.

Al final, creo que se nos ocurrió la "mejor" solución para las matemáticas.

Pero, ¿es lo suficientemente "mejor" para romper la compatibilidad con versiones anteriores? No lo creo.

Desafortunadamente, gran parte de numpy (y python) tenía muchas características agregadas porque eran útiles, pero sin mucha de la discusión actual, estas cosas tienen, por lo que tenemos muchos diseños subóptimos. Esto es esperado (y necesario) para una biblioteca joven, y solo tenemos que vivir con eso ahora.

@njsmith tiene razón: creo que muy pocos usos de np.isclose () tienen las tolerancias establecidas por un análisis de errores de FP riguroso, sino que prueban un error con np.isclose () en sí.

Sin embargo, creo que el problema más grande es el valor predeterminado atol , que asume que sus argumentos son de orden 1, lo que podría ser una suposición MUY incorrecta, y dado que a menudo daría lugar a pruebas aprobadas que no deberían , es posible que los usuarios no se den cuenta.

In [85]: np.isclose(9.0e-9, 1.0e-9)
Out[85]: True

¡Ay!

y sí, es el atol causa esto:

In [86]: np.isclose(9.0e-9, 1.0e-9, atol=0.0)
Out[86]: False

Así que probablemente sea una buena idea tener un camino a seguir.

Me gusta la idea del argumento de palabras clave: parece mucho más sencillo que tratar de meterse con __future__ y cosas por el estilo.

Y luego podríamos decidir si queríamos comenzar a emitir advertencias de desaprobación y, en última instancia, cambiar las muchas versiones predeterminadas en sentido descendente ...

Todos 78 comentarios

Demasiado tarde para cambiar algo en mi humilde opinión.

Esta es una de las funciones más utilizadas en todos los números (a través de assert_allclose ). La única ruta para esto sería elegir otro nombre.

Confieso que la implementación actual parece un error. Tenga en cuenta que usar max(abs(a), abs(b)) lugar de abs(b) no romperá ninguna prueba existente, simplemente relaja la condición en el caso de que abs(a) > abs(b) .

Como mínimo, esto necesita una advertencia en la cadena de documentos de que no coincide con el

¿Qué tal agregar esto?

from numpy.__future__ import isclose

Por cierto, eso puede ser una idea para el control de versiones de números aleatorios ...

Para aclarar, la importación en realidad no importaría is close, pero habilitaría la nueva semántica. Sería recogido en un módulo.

La idea es que podemos permitir que las personas usen la función correcta y, al mismo tiempo, no necesitamos romper ningún código existente.

, la importación no importaría isclose pero habilitaría la nueva semántica

No creo que sea posible hacer que esto no sea una importación. Simplemente elija un nombre más largo, como mathlike_ufunc s, que también podría cubrir remainder .

Tenga en cuenta que from __future__ import print_function realidad produce un print_function global

En realidad, el estilo de importación from __future__ import * podría no ser apropiado aquí; en Python, afecta a _syntax_ en un nivel por archivo, y hacer algo similar en numpy sería complicado

Las excepciones del iterador no fueron un cambio de sintaxis. Solo necesitamos un pequeño módulo C para inspeccionar el espacio de nombres del módulo de llamada. Incluso podemos almacenar en caché los módulos para que solo se ralentice el tiempo de importación, y solo de manera trivial.

Tienes razón, y true_division es obviamente análogo a isclose.

En realidad, esta es una buena manera de evitar los problemas en # 9444, como una alternativa para usar declaraciones with para administrar funciones obsoletas.

Vamos a CC @ ChrisBarker-NOAA como el creador de math.isclose .

Este es un problema bastante menor en mi opinión. Generalmente atol y rtol se eligen jugando con ellos hasta que las pruebas pasen, y el objetivo es detectar errores que son un orden de magnitud mayor que las tolerancias. Tal vez tenga sentido relajar la parte rtol como sugirió @charris , pero realmente no creo que valga la pena sacar trucos de introspección de pila para este pequeño ajuste. Y todavía tendríamos el problema de que isclose menudo es llamado indirectamente por cosas como assert_allclose o varios ayudantes de prueba de terceros.

Pregunté en StackOverflow sobre el uso de importaciones de estilo __future__ : https://stackoverflow.com/questions/29905278/using-future-style-imports-for-module-specific-features-in-python

TLDR: es posible, pero no fácil ni limpio.

La introspección de la pila no es solo para esto, sino que se propone como una política general para cambiar los valores de retorno de las funciones. La pregunta es: en principio, ¿debería cambiarse esto? Creo que si la respuesta es sí, el período de desaprobación debe ser de al menos unos años dado el uso generalizado. Python nunca incluyó un módulo anterior, pero esa es una opción para intentar mejorar la estabilidad de la API, si hay demanda.

El otro método sería simplemente agregar esto como una opción isclose(...,symmetric=True) o assert_allclose(symmetric=True) donde el valor predeterminado sería False , la situación actual.

Estoy de acuerdo en que este es un problema menor cuando no le da un significado a los valores rtol y atol , simplemente ajústelos para pasar las pruebas unitarias, como lo menciona @njsmith.

Sin embargo, a veces le gustaría decir que el error está dentro, por ejemplo, del 1% ( rtol=0.01 ).
En este caso, el error del peor caso con respecto a la medición de rtol es del 100% ( rtol * abs(b) se acerca a atol , luego atol + rtol * abs(b) ~= 2 * rtol * abs(b) ).

Lo que significa que algunos valores pueden pasar con un error de ~ 2%:

atol = 1e-8 #default
rtol = 0.01 # 1%

b = 1e-6
a = 1.0199e-6 # ~ 2% larger comapared to b
abs(a - b) <= (atol + rtol * abs(b))
True

Implementar la idea de @charris empeoraría un poco este caso en particular, ya que relaja aún más la comparación, pero aún así vale la pena, ya que elimina el problema de simetría y, de hecho, es compatible con versiones anteriores.

En mi opinión, sería mejor si Numpy usara la función matemática, pero entiendo que el cambio puede ser demasiado perturbador y posiblemente no importante para la mayoría de los usuarios. Sería útil hacer la opción de cambiar entre el núcleo isclose .

@njsmith : gracias por traerme.

Un poco de historia: cuando propuse isclose para stdlib, ciertamente consideramos numpy como técnica anterior. Si fuera solo yo, puede que haya usado un enfoque compatible, por el bien de la compatibilidad :-).

Pero el resto de la comunidad pensó que era más importante hacer lo que era "correcto" para Python, por lo que se produjo una larga discusión ... Traté de capturar la mayor parte del punto en el PEP, si quieres ir a buscar.

Aquí está la referencia a numpy:

https://www.python.org/dev/peps/pep-0485/#numpy -isclose

Puede ver que se hicieron los mismos puntos que en esta discusión.

Al final, salieron tres puntos clave:

1) un enfoque simétrico resultaría en la menor sorpresa.

2) la tolerancia absoluta predeterminada probablemente debería ser cero, para no hacer suposiciones sobre el orden de magnitud de los argumentos.

3) la diferencia entre las pruebas "débil" y "fuerte" era irrelevante cuando se usaba con pequeñas tolerancias, como es el caso de uso esperado.

Al final, creo que se nos ocurrió la "mejor" solución para las matemáticas.

Pero, ¿es lo suficientemente "mejor" para romper la compatibilidad con versiones anteriores? No lo creo.

Desafortunadamente, gran parte de numpy (y python) tenía muchas características agregadas porque eran útiles, pero sin mucha de la discusión actual, estas cosas tienen, por lo que tenemos muchos diseños subóptimos. Esto es esperado (y necesario) para una biblioteca joven, y solo tenemos que vivir con eso ahora.

@njsmith tiene razón: creo que muy pocos usos de np.isclose () tienen las tolerancias establecidas por un análisis de errores de FP riguroso, sino que prueban un error con np.isclose () en sí.

Sin embargo, creo que el problema más grande es el valor predeterminado atol , que asume que sus argumentos son de orden 1, lo que podría ser una suposición MUY incorrecta, y dado que a menudo daría lugar a pruebas aprobadas que no deberían , es posible que los usuarios no se den cuenta.

In [85]: np.isclose(9.0e-9, 1.0e-9)
Out[85]: True

¡Ay!

y sí, es el atol causa esto:

In [86]: np.isclose(9.0e-9, 1.0e-9, atol=0.0)
Out[86]: False

Así que probablemente sea una buena idea tener un camino a seguir.

Me gusta la idea del argumento de palabras clave: parece mucho más sencillo que tratar de meterse con __future__ y cosas por el estilo.

Y luego podríamos decidir si queríamos comenzar a emitir advertencias de desaprobación y, en última instancia, cambiar las muchas versiones predeterminadas en sentido descendente ...

Me gustaría proponer la sugerencia de @bashtage :

"" "
El otro método sería simplemente agregar esto como una opción isclose (..., symmetric = True) o assert_allclose (simétrico = True) donde el valor predeterminado sería False, la situación actual.
"" "

Creo que es una mejor opción que un nuevo nombre de función y podría abrir el camino hacia una futura depreciación y un cambio del predeterminado (o no).

Creo que además de la prueba simétrica (fuerte), atol debería estar por defecto en cero.

Dado eso, tal vez necesitemos un nombre mejor para el parámetro que symmetric , aunque no está mal ...

Ah, y podría ser bueno asegurarse de que isclose() no se llame en otro lugar en numpy además de assert allclose() .

La forma más sencilla de hacerlo es probablemente poner una advertencia de obsolescencia y
luego retírelo antes de fusionar. Podríamos seguir adelante y poner el
advertencia de desaprobación que dice cambiar el código existente a
simétrico = Falso; no hay razón para decir que el valor predeterminado debe ser
cambiado pronto, incluso si la advertencia está allí.

¿Necesitamos agregarlo a assert_allclose también, o simplemente hacemos el cambio en silencio? Hacer que las personas actualicen cada una de sus pruebas para silenciar una advertencia no es realmente diferente a hacer que actualicen sus pruebas para corregir la reducción de tolerancia.

@xoviat @ eric-wieser, no puede haber ningún cambio incompatible hacia atrás en assert_allclose ni una advertencia de desaprobación, demasiado perturbador. A menos que me falte el lugar donde desea colocar una advertencia de desaprobación.

Me temo que esto todavía suena como mucho dolor para infinitesimal
ganancia, para mí.

Que atol sea distinto de cero por defecto no es un accidente. Es necesario para conseguir
comportamiento predeterminado sensible para cualquier prueba donde el resultado esperado incluye
ceros exactos.

No se requieren cambios en ningún assert_allclose, ya que se actualizaría a
internamente para utilizar el antiguo comportamiento.

¡Los cambios silenciosos de la OMI son desagradables! La advertencia de desaprobación es solo para que
las personas eventualmente no necesitan escribir la bandera en un mensaje interactivo para
obtén el nuevo comportamiento, nada más

¡Los cambios silenciosos de la OMI son desagradables!

Convenido. Sin embargo, no debería haber cambios. Las advertencias de obsolescencia en funciones ampliamente utilizadas también obligan a los usuarios (al menos a los que realmente realizan mantenimiento) a realizar cambios.

¿Qué hay de solucionar el problema de no simetría como sugirió @charris ? Esto también liberaría algo de espacio de documentación, que podría llenarse con una advertencia sobre las cosas malas que pueden suceder cuando atol != 0.0 .

No podemos solucionar ese problema sin informar a las personas sobre los cambios
comportamiento. La única forma en que sé cómo hacerlo es con una advertencia.

Lo que sería realmente bueno es una herramienta de refactorización automatizada para inyectar banderas
en código antiguo. Entonces 'mantenimiento' solo estaría ejecutando un script en su
código; 5 minutos de trabajo.

Lo sentimos, esto se puede arreglar, solo con una nueva bandera.

¿Qué hay de solucionar el problema de no simetría como sugirió @charris ?

Esa sugerencia (https://github.com/numpy/numpy/issues/10161#issuecomment-349384830) parece factible.

No podemos solucionar ese problema sin informar a la gente sobre el cambio de comportamiento.

No lo estamos arreglando. Esto es lo que tiene que ver con el costo / beneficio de cualquier cambio que se acaba de discutir en el tema de versiones semánticas. Este es definitivamente un caso en el que no realizaremos ningún cambio ni emitiremos ninguna advertencia de una función de prueba ampliamente utilizada. Tenga en cuenta que el comportamiento actual no es un error (aunque podría decirse que es una opción subóptima).

Para ser claros, estaba proponiendo un cambio en isclose pero no assert_allclose.
Al observar las fuentes, ese cambio será un tercero como disruptivo.
Sin embargo, es probable que el beneficio sea todavía demasiado pequeño. No creo que nadie
Objetos para agregar una bandera, ¿correcto?

Sin embargo, no creo que nadie se oponga a agregar una bandera, ¿correcto?

No estoy seguro, depende de los detalles. La sugerencia de @charris puede no requerir una bandera, y cualquier bandera que introduzca esto es un poco cuestionable para las matrices:

In [1]: import math

In [2]: math.isclose(0, 1e-200)
Out[2]: False

Me opongo a agregar banderas sin motivaciones concretas y casos de uso. Prueba de fuego útil: si un desarrollador novato le preguntara por qué existe esta bandera, ¿cómo lo explicaría?

como lo explicas

  1. Utilice la bandera symmetric=True si desea vectorizar llamadas a math.isclose
  2. Tenía que ser una bandera para no romper el código existente.

Estoy más buscando situaciones en las que se esté resolviendo algún problema real.

Probablemente el único problema real que se resuelve aquí es hacer que los principiantes
Piense más en la precisión del punto flotante. Quiero decir, el ejemplo de @rgommers
parece que estaría mal, pero ¿qué pasa si se trata de
¿números? En mi opinión, la implementación de math.isclose es mejor, pero ni siquiera
creo que hay consenso al respecto. Desafortunadamente, no hay realmente un
solución única para todos para determinar si los números están "cerca". Pero
basándome en las respuestas de otros (¡que no están mal!), realmente no
cualquier cambio en la API en el futuro. Supongo que la única acción a tomar es
probablemente una actualización de documentación entonces (las famosas últimas palabras, dado que yo
anteriormente pensó que una bandera estaría bien)?

Seguramente una nota que compare con math.isclose y documente cómo obtener un comportamiento vectorizado que sea idéntico al establecer rtol y atol, si se quiere, estaría bien.

Además, si entiendo correctamente, @charris propuso una solución para permitir
está cerca de ser menos tolerante de lo que es actualmente, lo que no se rompería
cualquier prueba. Sigo pensando que sería una buena idea emitir una advertencia (el
advertencia debe emitirse una vez) si hay una situación en la que está cerca
considere que los números están "cercanos" cuando antes no lo estaban. Eso es muchomejor que simplemente cambiar el comportamiento de la función en silencio y no dejar
cualquiera lo sabe cuando les afecta.

Creo que se necesita una aclaración aquí sobre lo que estamos discutiendo. Hay dos cosas que hacen que math.isclose difieran:

  • Un valor predeterminado diferente para atol
  • Una definición diferente de rtol

No creo que podamos hacer nada sobre el primer problema, aparte de documentarlo como diferente de `math.isclose.

El segundo problema, creo que se soluciona mejor agregando un argumento symmetric que por defecto es False . Ahora podemos escribir en nuestros documentos _ " np.isclose(x, y, symmetric=True, atol=0) es una versión vectorizada de math.isclose(x, y) " _, que me parece que es lo que estamos tratando de resolver.

A partir de aquí, tenemos tres opciones:

  1. Documente el argumento adicional y no haga nada más
  2. Dejar de llamar a isclose sin el argumento, lo que obliga a los usuarios a escribir isclose(..., symmetric=False) para obtener el comportamiento anterior sin una advertencia (y similar para allclose ). Sospecho que esto no afectará demasiado al código, pero el resultado es menos legible sin grandes ganancias. assert_close se cambiaría para llamar a isclose(..., symmetric=False) internamente, por lo que los usuarios no se verían afectados
  3. Como el anterior, pero también requiere el argumento symmetric para assert_close . Esto sería una rotación masiva aguas abajo
  4. Cambia silenciosamente el comportamiento

De estos, creo que la opción 1 es inobjetable, pero el resto no suena a cosas que valgan la pena.

Editar: 2 podría ser aceptable si la advertencia solo se emite si el comportamiento cambia, lo que sería mucho menos ruidoso.

@ eric-wieser Hay una tercera diferencia: la forma en que se combinan atol y rtol . ( math.isclose usa max , numpy.isclose usa + ). Esto significa que a menos que atol o rtol sea ​​cero, no hay ninguna forma general de hacer que una llamada math.isclose coincida con una llamada numpy.isclose .

Sin embargo, todavía no creo que valga la pena agregar API visibles para el usuario.

Estaba a favor de la opción dos. Sigo estando a favor de esa opción, con
la estipulación adicional de que numpy proporcionaría un sistema automatizado
herramienta de refactorización (agregada a entry_points) que podría ejecutar en su
proyectos existentes para solucionarlos. Según lo que otros dijeron, parece
esta opción no sería favorecida por otros.

No estoy y nunca he estado a favor de las opciones tres o cuatro. En
Además, no estoy a favor de cambiar el comportamiento de las funciones hasta que aparezca una advertencia.
se ha emitido durante al menos cuatro lanzamientos importantes.

Suponiendo que otros no estén de acuerdo con la opción dos (que tienen), estaría
a favor de la opción uno. Pero otros (particularmente @njsmith) no están a favor
de cualquiera de las opciones que dio aquí. Al menos esa es mi percepción.

@njsmith Eso no es correcto; puede cambiar el comportamiento de la función con la bandera.

Estoy doblando esa tercera diferencia en un handwavey _ "rtol is different" _

"" "
Estoy más buscando situaciones en las que se esté resolviendo algún problema real.
"" "
Apostaré dólares a donas (lo cual haré porque no tengo idea de lo que eso significa ...) que hay pruebas que están pasando y que no deberían porque atol hace que la prueba sea mucho menos sensible que debe ser.

Parece que hay tres "problemas" con la implementación actual:

1) no es simétrico

  • Creo que eso es una lástima, pero en realidad no es gran cosa, y casi no hay diferencia cuando los valores realmente están cerca :-) Creo que literalmente no hay diferencia si rtol <1e-8 (al menos si atol es 0.0)

2) el atol afecta el resultado incluso cuando no se compara con cero (eso es inevitable), pero cambia efectivamente la tolerancia en aproximadamente un factor de dos, o incluso más si el atol es grande, lo que podría ser si se trabaja con un gran orden de valores de magnitud.

3) el atol es distinto de cero por defecto; de hecho, creo que este es el mayor problema (particularmente con el algoritmo actual de agregar ambos), ya que puede conducir muy fácilmente a que se aprueben pruebas que no deberían, y con demasiada frecuencia estamos un poco perezoso: escribimos la prueba con la tolerancia predeterminada, y si pasa, creemos que hemos terminado. (cuando me di cuenta de que así era como funcionaba, volví al código y, y encontré un par de esos ...

Alguien en este hilo dijo algo sobre "habría algo retorcido si:

está cerca (1e-200, 0.0)

devuelto False de forma predeterminada. No estoy de acuerdo, sí, eso sería sorprendente, pero obligaría al usuario a pensar en lo que está sucediendo, mientras que la implementación actual da como resultado (por ejemplo):

En [8]: np.isclose (1e-20, 1e-10)
Fuera [8]: Verdadero

¿De Verdad? uno es DIEZ ÓRDENES de MAGNITUD más grande que el otro y vuelve Verdadero ????

Mi punto es que tener un atol distinto de cero da resultados quizás menos sorprendentes en el caso común de comparar con cero, pero resultados MUCHO más peligrosos e incorrectos cuando se trabaja con números pequeños (pequeño realmente es cualquier cosa menor que el orden de magnitud 1.

Y si está trabajando con números grandes, digamos más grandes que 1e8, el atol predeterminado también es inapropiado.

Y junto con (2), significa que el atol predeterminado también puede estropear las pruebas de tolerancia relativa de formas sorprendentes.

Entonces: no, no es un error, y no está "roto", pero es bastante subóptimo, por lo que sería bueno tener una forma de avanzar hacia una mejor implementación.

Me gustó el enfoque de la bandera, pero creo que estoy cambiando de opinión. El problema es que, a menos que lo desaprovechemos y cambiemos la bandera predeterminada en algún momento, casi todos usarán el algoritmo "antiguo". prácticamente para siempre. Y ha habido muchos buenos argumentos por los que probablemente no podríamos desaprobarlo.

Entonces tal vez una nueva función, con un nuevo nombre esté en orden. Podríamos agregar a los documentos alentar a las personas a usar el nuevo, y _ tal vez_ agregar advertencias en algún momento cuando la gente use el nuevo, pero nunca romperíamos el código de nadie.

¿Alguien tiene una idea de un buen nombre para una nueva función ????

¿Quizás np.math.isclose y amigos? No lo sé.

Eso resuelve que np.remainder sea ​​diferente, y también le permite vectorizar fácilmente el código usando from numpy import math

Entonces estaría +1 en un módulo np.math

La introducción de nuevas funciones con un comportamiento solo ligeramente diferente de otras funciones que han trabajado así durante una década es, en general, una muy mala idea. En este caso, realmente no hay mucho problema que no se pueda resolver adecuadamente con documentación. Entonces -1 en una nueva función. Y definitivamente -1 en un submódulo completamente nuevo.

¿De Verdad? uno es DIEZ ÓRDENES de MAGNITUD más grande que el otro y vuelve Verdadero ????

Si su expectativa es Verdadera o Falsa depende totalmente del contexto. Si eso es para números que provienen de una distribución continua en [0, 1), entonces sí, probablemente esperará True. Un usuario realmente debería comprender las tolerancias absolutas / relativas y lo que realmente hace una función, y para eso están los documentos.

symmetric_isclose() es un poco largo, pero no es ofensivo para mis ojos.: bikeshed: emoji>

El 10 de diciembre de 2017, a las 8:09 p.m., Ralf Gommers [email protected] escribió:

Introduciendo nuevas funciones con un comportamiento ligeramente diferente al de otros
funciones que han funcionado así durante una década es, en general, una muy mala
idea.

Tampoco me gusta, pero romper el código de las personas con un cambio es peor.
¿Tienes una idea que no sea simple para no mejorar Numpy?

En este caso, realmente no hay mucho problema que no pueda ser adecuadamente
resuelto con documentación.

No estoy de acuerdo, los incumplimientos importan mucho.

Y no creo que haya ninguna forma de ajustar los parámetros para darte una
comparación simétrica.

Y definitivamente -1 en un submódulo completamente nuevo.

Yo también. Si hay otras cosas en el módulo de matemáticas sin numpy
equivalentes que serían útiles, podrían agregarse al espacio de nombres de numpy
como todo lo demás.

¿De Verdad? uno es DIEZ ÓRDENES de MAGNITUD más grande que otro y vuelve
¿¿¿¿Cierto????

Si su expectativa es Verdadera o Falsa depende totalmente del contexto.

Exactamente, es por eso que NO HAY un valor predeterminado "razonable" para atol.

Si es para números que provienen de una distribución continua en [0, 1)
entonces sí, probablemente esperará True.

Pero isclose se anuncia como una comparación relativa, y aquí el
la relatividad está siendo completamente eliminada sea la comparación absoluta,
sin que el usuario tenga que haberlo pensado.

Ese es mi punto: el valor predeterminado SOLO es apropiado si espera su
valores del orden de magnitud 1. Un caso común, claro, pero no
universal.

Esto se reduce a lo que es peor: un falso negativo o un falso positivo.
Y en el caso de uso común de las pruebas, un falso positivo es mucho peor.

(es decir, una prueba aprobada que no debería)

Un usuario realmente debería comprender las tolerancias absolutas / relativas y qué
la función realmente lo hace,

Absolutamente, pero una función también debe tener valores predeterminados razonables y una
algoritmo robusto que es fácil de entender.

Tiendo a mejorar la documentación y dejar la función en paz. La falta de simetría se puede explicar como "isclose (a, b) significa que a está cerca de b, pero debido a la precisión absoluta variable del punto flotante, no siempre es el caso de que b esté cerca de a. En las pruebas, b debe ser el resultado esperado y a debe ser el resultado real ". Tenga en cuenta que esto tiene sentido para las pruebas, el caso de la función simétrica es en realidad un poco más complicado de justificar. El error relativo, que implica la división, no es simétrico.

Tampoco me gusta, pero romper el código de las personas con un cambio es peor. ¿Tienes una idea que no sea simple para no mejorar Numpy?

Ya está emitiendo un juicio de valor aquí de que agregar su nueva función es mejor que ninguna función nueva. No es en mi humilde opinión. Tres opciones:

  1. Un cambio radical: no es aceptable, puede dejar de discutirlo.
  2. Solo agrego mejores documentos
  3. Agregar una función

2 es en general una mejor opción que 3, por lo tanto, eso es lo que hace que numpy sea "mejor". También estás ignorando que esto de atol / rtol no se limita a una sola función, así que, ¿qué sigue? ¿Un assert_allclose nuevo y ligeramente "mejor"? Esto hace que el caso de solo doc sea aún más claro.

Este es un defecto bastante serio, básicamente el código que prueba su código tiene errores ... no es bueno. ¿Enviarías algo a la luna que se pruebe con numpy.isclose y atol predeterminado? Me lo pensaría dos veces ... y es por eso que tenemos que hacer que estos errores se destaquen en la documentación.
Agregar una función de alias simplemente saturará la base de código a menos que se fuerce a los usuarios (no sucede).

Estoy de acuerdo en que el asunto de la simetría es menor, pero aún así deberíamos arreglarlo. Dejarlo podría distraer a los usuarios de las trampas reales.

@rgommers escribió:
"" "
Ya está emitiendo un juicio de valor aquí de que agregar su nueva función es mejor que ninguna función nueva. No es en mi humilde opinión.
"" "
Bueno, pasé MUCHO tiempo pensando y debatiendo sobre math.isclose , y comenzamos mirando la implementación numpy entre otros. entonces, sí, creo que ese enfoque es mejor. Y pensé de esta discusión que eso era prácticamente un consenso.

Y obtener un mejor algoritmo / interfaz en numpy lo hace mejor, sí.

Tal vez quiera decir que tener tanto la función antigua como la nueva, mejor, en numpy NO es mejor que simplemente dejar la función antigua (quizás mejor documentada) allí. Claro, ese es un punto totalmente válido, pero estaba tratando de tener esa discusión, y el comentario anterior de que "Introducir nuevas funciones con un comportamiento solo ligeramente diferente de otras funciones que han funcionado así durante una década es en general una idea muy pobre". pareció cerrar la discusión - mi punto es que si la nueva forma es "suficientemente mejor", entonces valdría la pena. Lo que claramente no tenemos un consenso es si esta opción en particular es "suficientemente mejor", no si es "mejor".

Y, por cierto, yo personalmente no me he convencido de que valga la pena un cambio, pero quiero tener la discusión.

La clave aquí es un par de suposiciones de mi parte. No sé si alguna vez podremos saber con certeza si son correctos, pero:

1) el mayor uso de np.isclose () es para realizar pruebas: ¿están sus respuestas lo suficientemente cerca de lo que espera? - esto es a través de np.assert_all_close, o más directamente, en pruebas de pytest, o ....

2) La mayoría de las personas, la mayoría de las veces, no hacen nada como un análisis riguroso de errores de punto flotante para determinar qué tan buenas se espera que sean las respuestas. Más bien, prueban un valor, y si falla, miran los resultados y deciden si realmente es un error, o si necesitan ajustar las tolerancias de la prueba.

  • esto significa que no importa mucho si el atol y rtol se fusionan, y si la prueba es simétrica.

3) muchas personas, la mayor parte del tiempo, comienzan el proceso en (2) con tolerancias predeterminadas y solo buscan ajustar la tolerancia si falla una prueba.

  • ESTO significa que tener un atol predeterminado es bastante peligroso: pasar pruebas que no deberían es algo realmente malo.

4) Las personas no leen documentos (más allá de la etapa inicial de "cómo llamo a esto"), al menos no hasta que encuentran un comportamiento confuso, y luego pueden entrar y tratar de entender cómo funciona algo realmente para aclarar la confusión. . Pero vea (3): si una prueba no falla, no saben que deben consultar los documentos para comprender por qué.

Todo esto me lleva a la conclusión de que numpy sería "mejor" con una prueba de proximidad FP más similar a las matemáticas.

Y por qué mejor es una gran idea, pero no lo suficiente.

Quizás estoy totalmente equivocado, y la mayoría de las personas leen cuidadosamente los documentos y seleccionan cuidadosamente tanto rtol como atol para su problema la mayor parte del tiempo, pero sé que yo, ni la media docena de personas de mi equipo, lo hice hasta que me convertí en consciente de estos problemas.

: bikeshed: (maldición, eso no funcionó, no hay emoji ingenioso)

tal vez relatively_close , o rel_close ?

Una arruga "divertida" adicional: assert_allclose en realidad usa atol = 0 por
defecto. Hay otro hilo en alguna parte debatiendo si podemos arreglar
esa inconsistencia. (En mi teléfono no puedo encontrarlo fácilmente).

El 11 de diciembre de 2017 a las 14:58, "Chris Barker" [email protected] escribió:

@rgommers https://github.com/rgommers escribió:
"" "
Ya está emitiendo un juicio de valor aquí que agrega su nueva función
es mejor que ninguna función nueva. No es en mi humilde opinión.
"" "
Bueno, pasé MUCHO tiempo pensando y debatiendo sobre
math.isclose, y comenzamos mirando la implementación numpy
entre otros. entonces, sí, creo que ese enfoque es mejor. Y yo pensé
de esta discusión que eso fue prácticamente un consenso.

Y obtener un mejor algoritmo / interfaz en numpy lo hace mejor, sí.

Quizás quiere decir que tener tanto lo antiguo como lo nuevo, mejor, funcionan en
numpy NO es mejor que simplemente dejar lo viejo (quizás sea mejor
documentado) funcionan allí. Claro, ese es un punto totalmente válido, pero estaba
tratando de tener esa discusión, y el comentario anterior que "Presentando
nuevas funciones con un comportamiento ligeramente diferente al de otras funciones
que han funcionado así durante una década es en general una muy mala idea "
pareció cerrar la discusión; mi punto es que si la nueva forma es
"bastante mejor" de lo que valdría la pena. Lo que claramente no tenemos
el consenso es si esta opción en particular es "suficientemente mejor", no
si es "mejor".

Y por cierto, personalmente no me he convencido de que valga la pena
cambiar, pero quiero tener la discusión.

La clave aquí es un par de suposiciones de mi parte. No se que podamos
saber con certeza si son correctos, pero:

1.

el mayor uso de np.isclose () es para realizar pruebas, son sus respuestas
lo suficientemente cerca de lo que esperas? - esto es a través de np.assert_all_close, o
más directamente, en pruebas de pytest, o ....
2.

La mayoría de la gente, la mayor parte del tiempo, no hace nada como riguroso
análisis de error de punto flotante para determinar qué tan buenas son las respuestas
Se espera que sea. Más bien, prueban un valor, y si falla, miran el
resultados y decidir si realmente es un error, o si es necesario ajustar la
tolerancia (es) de la prueba.

  • esto significa que no importa mucho si el atol y
    rtol se fusionan y si la prueba es simétrica.

  • muchas personas, la mayor parte del tiempo, comienzan el proceso en (2) con
    tolerancias predeterminadas, y solo busca ajustar la tolerancia si falla una prueba.

  • ESTO significa que tener un atol predeterminado es bastante peligroso: pruebas
    pasar que no debería es algo realmente malo.

  • La gente no lee documentos (más allá del "cómo llamo a esto" inicial
    etapa) - al menos no hasta que encuentren un comportamiento confuso, y luego
    podría entrar y tratar de entender cómo funciona algo realmente para aclarar
    la confusión. Pero vea (3): si una prueba no falla, no saben
    mira los documentos para entender por qué.

Todo esto me lleva a la conclusión de que numpy sería "mejor" con un
más prueba de proximidad FP similar a la matemática.

Y por qué mejor es una gran idea, pero no lo suficiente.

Quizás estoy totalmente equivocado, y la mayoría de la gente lee cuidadosamente los documentos y
seleccione tanto rtol como atol cuidadosamente para su problema la mayor parte del tiempo -
pero sé que yo, ni la media docena de personas de mi equipo, lo hice hasta que
tomó conciencia de estos problemas.

: bikeshed: (maldición, eso no funcionó, no hay emoji ingenioso)

¿quizás relativamente_close, o rel_close?

-
Estás recibiendo esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/numpy/numpy/issues/10161#issuecomment-350886540 , o silenciar
la amenaza
https://github.com/notifications/unsubscribe-auth/AAlOaNquy47fsOkvBlxcSa-Mxyhgdimlks5s_bORgaJpZM4Q2J-P
.

¿Te refieres al # 3183?

assert_allclose en realidad usa atol = 0 por defecto.

Interesante, eso me hace sentir mejor.

Esto está trayendo de vuelta un recuerdo vago de discusiones pasadas sobre las diferencias entre los dos. ¿Era esa la única?

Hola,
Mis bibliotecas thermo, fluids y ht hacen un uso extensivo de assert_allclose de numpy, por lo que comparto algunas ideas aquí ya que está relacionado. Este hilo hace que la diferencia de comportamiento parezca muy alarmante, y un poco como las personas deberían esperar encontrar errores en su código debido a la falta de simetría y / o la diferencia en la combinación de atol y rtol, así como el atol predeterminado (no presente en assert_allclose , Lo sé). Así que quería ver si mis bibliotecas tenían algún error y pirateé una implementación muy burda de assert_allclose que luego cambié mis pruebas para usar en su lugar temporalmente.

Creo que mi uso de assert_allclose es representativo: comparo valores seleccionados individualmente y hago pruebas en las que confundí cosas y llamo a assert_allclose en el resultado de las funciones de forma paramétrica. Comparo entrantes y flotadores de todo tipo de magnitudes. He pensado relativamente poco en elegir un rtol o atol, y rara vez he añadido un atol o cambiando rtol por un valor predeterminado menos estricto. Las bibliotecas llaman a assert_allclose 13159, 1178 y 4243 veces respectivamente.

Así que hice el cambio y realicé mis pruebas. Estoy muy feliz de decir que no encontré ningún error nuevo ni fallas en las pruebas ; las únicas fallas de prueba que pude encontrar fueron con mi implementación de assert_allclose.

Sé que hay otros que serían menos afortunados y se encontrarían con un problema si se cambia algo en assert_allclose o isclose. Personalmente, me sentiría más cómodo escribiendo pruebas si tuviera un modo assert_allclose que replicara el comportamiento de math.allclose, ya sea a través de otra función o symmetric flag. Pero sé que es mucho trabajo para alguien y tampoco ha habido relaciones públicas todavía. ¡Me alegra tener la comodidad de haber verificado mi código en busca de este defecto aunque sea una vez!

4880 es en el que estaba pensando.

El 11 de diciembre de 2017, a las 5:01 p.m., Nathaniel J. Smith [email protected]
escribió:

4880 https://github.com/numpy/numpy/pull/4880 es el que estaba pensando

de.

Gracias, tenga en cuenta que el ejemplo de statsmodel hace bien mi punto sobre el
valor predeterminado de atol: cualquier cosa que no sea 0.0 es peligrosa.

¿El hecho de que esto surja cada dos años indica que deberíamos
finalmente hacer algo al respecto?

Quizás sí. Es una verruga pequeña, pero a veces esas pequeñas molestias agregan
con el tiempo.

Y aunque estoy satisfecho de que assert_allclose tenga un valor predeterminado de atol en cero
(apoyando mi punto de que es una opción aceptable, incluso si no
de acuerdo en que es la mejor opción) muchos de nosotros, y más todo el tiempo, no somos
usando unittest, y por lo tanto puede usar np.all_close directamente en las pruebas.

Y sí Ralf, si hacemos un cambio, querremos cambiar los tres
funciones estrechamente relacionadas :-(. Pero eso nos da la oportunidad de hacerlas
más consistente, ¿una ventaja?

Si bien la transición de py2-3 no ha ido muy bien, hay algo que
dicho por tener una versión de "está bien limpiar las verrugas" en algún momento :-)

Hay otra opción, para mover is_close a la versión matemática (de hecho mejor) de atol=0 : cambie el valor predeterminado a None e intente con atol=0 ; si todos True simplemente regresan; si algunos False , inténtelo de nuevo con atol estándar y si los resultados cambian emitirá una advertencia de desaprobación (y devolverá el resultado "antiguo").

Lamentablemente, uno no puede hacer simetría y atol al mismo tiempo, ya que van en direcciones diferentes (es decir, siempre habría que ejecutar ambos casos, lo que parece un poco excesivo). Pero no me gusta el atol predeterminado de

No estoy a favor de cambiar ninguna función a menos que haya una función que
puede usar que tiene el comportamiento anterior. Además, debería haber una
herramienta de refactorización automatizada para realizar este cambio si se hace. Sin embargo eso no es
incluso en la mesa ahora.

La transición de py3k no es algo que queramos volver a tener y
no es un modelo a seguir. Reunir las limpiezas en un gran lanzamiento no es bueno
acercarse a la OMI.

Si bien la transición de py2-3 no ha ido muy bien, hay algo que decir acerca de tener una versión de "está bien limpiar las verrugas" en algún momento :-)

Solo para abordar esta idea general de que los números de versión especiales pueden hacer que los cambios importantes estén bien: la regla general es que está bien limpiar las verrugas, si existe un plan de transición claro que evite niveles inaceptables de dolor y los beneficios sean suficientes para justificar la costos. El punto clave aquí es que el juicio debe ser en términos del efecto sobre los usuarios, no en si "seguimos las reglas". (Si quieres ser sofisticado, somos consecuencialistas , no deontólogos .) Así que la única razón para declarar que una versión específica es "esta es la que rompe cosas" es si hace las cosas sustancialmente más fáciles de manejar para los usuarios. En mi opinión, los beneficios de acumular cambios importantes juntos son generalmente menores si es que existen (el código roto es código roto) e incluso si existen, es muy raro que inclinen el análisis de costo / beneficio de "no" a " si".

atol no es cero por defecto; de hecho, creo que este es el mayor problema (particularmente con el algoritmo actual de agregar ambos), ya que puede llevar fácilmente a que se aprueben pruebas que no deberían, y con demasiada frecuencia estamos un poco perezoso: escribimos la prueba con la tolerancia predeterminada, y si pasa, creemos que hemos terminado. (cuando me di cuenta de que así era como funcionaba, volví al código y, y encontré un par de esos ...

Tenga en cuenta que el ejemplo de statsmodel hace bien mi punto sobre el valor predeterminado de atol: cualquier cosa que no sea 0.0 es peligrosa. [...] muchos de nosotros, y más todo el tiempo, no estamos usando unittest y, por lo tanto, podemos usar np.all_close directamente en las pruebas.

"Deberíamos descifrar el código de usuario para aumentar la coherencia con algún otro paquete" no puntúa muy bien en la escala de costo / beneficio. "Hay inconsistencias dentro de Numpy que no solo son confusas, sino que confunden de una manera que conduce directamente a errores silenciosos en el código de usuario, y podemos solucionar eso" es mucho más convincente. Todavía no me he formado una opinión, pero si quieres progresar aquí, entonces esto es lo que estaría presionando.

Una nota:

"" "He pensado relativamente poco en elegir un rtol o atol,
rara vez agregando un atol
-recorte-

$ llame a assert_allclose 13159, 1178 y 4243 veces respectivamente.

-recorte-

No encontré ningún error nuevo ni fallos de prueba ;

"" "

Buenas noticias, aunque noto que assert_allclose tiene atol predeterminado en cero. Y
esta es la razón por :-)

@njsmith escribió:

"Debemos romper el código de usuario para aumentar la coherencia con algún otro paquete"

No creo que nadie en este hilo esté defendiendo eso, estoy seguro de que no. La única consistencia que alguien defiende es la consistencia entre las funciones numéricas relacionadas.

"Hay inconsistencias dentro de numpy que no solo son confusas, sino que confunden de una manera que conduce directamente a errores silenciosos en el código de usuario, y podemos arreglar eso"

Eso es lo que yo, al menos, estoy defendiendo. Y creo que la mayoría de los demás.

El problema es que no podemos solucionarlo sin:

Rompiendo la compatibilidad con versiones anteriores, que no creo que nadie piense que esto sea lo suficientemente serio como para hacerlo, incluso con un ciclo de desaprobación.

O

Hacer una nueva bandera o función.

Creo que una nueva función sería una forma más limpia de hacerlo. El código antiguo podría permanecer sin cambios todo el tiempo que quiera, el nuevo código podría usar las nuevas funciones y una búsqueda y reemplazo (o algunas importaciones como llamadas) podría facilitar el cambio de un archivo a la vez.

(Supongo que incluso podrías parchear numpy en tu código de prueba ...)

Nosotros ya tenemos:

  • allclose
  • assert_allclose
  • assert_almost_equal
  • assert_approx_equal
  • assert_array_almost_equal
  • assert_array_almost_equal_nulp
  • assert_array_max_ulp

No creo que agregar más opciones a esta lista vaya a hacer una gran diferencia para los usuarios reales.

Bueno, pasé MUCHO tiempo pensando y debatiendo sobre math.isclose, y comenzamos mirando la implementación numpy entre otras. entonces, sí, creo que ese enfoque es mejor.

Yo también, y he sido uno de los principales mantenedores de numpy.testing ; este no es un problema nuevo. Insistir con letras mayúsculas en que tienes razón no significa que sea así.

Tal vez quiera decir que tener tanto la función antigua como la nueva, mejor, en numpy NO es mejor que simplemente dejar la función antigua (quizás mejor documentada) allí. Claro, ese es un punto totalmente válido, pero estaba tratando de tener esa discusión,

De hecho, debería quedar claro que eso es lo que quise decir.

"Introducir nuevas funciones con un comportamiento ligeramente diferente al de otras funciones que han funcionado así durante una década es en general una idea muy pobre" pareció cerrar la discusión.

No, señala un problema real al agregar nuevas funciones que con demasiada frecuencia se ignoran o no se les da suficiente peso. En este caso, parece bastante claro para muchos desarrolladores principales: parece que está recibiendo la opinión de que este barco ha zarpado de @njsmith , @pv , @charris y yo.

Me disculpo de antemano por repetir esto, pero assert_allclose es el comportamiento correcto (o al menos lo suficientemente cerca). Sin embargo, isclose puede causar problemas a las personas porque asume una tolerancia absoluta, como han señalado otros. Estoy considerando enviar un PR para poner un gran cuadro rojo en la página de documentación np.isclose para advertir a la gente sobre este comportamiento. ¿Como suena eso?

Me disculpo de antemano por repetir esto, pero assert_allclose es el
comportamiento correcto (o al menos lo suficientemente cerca). Sin embargo, si está cerca
gente en problemas porque asume una tolerancia absoluta como otros han
célebre. Estoy considerando enviar un PR para poner un gran recuadro rojo en el
np.isclose la página de documentación para advertir a las personas sobre este comportamiento. Cómo
¿ese sonido?

+1

Hay consenso en que los documentos deberían ser mejores.

Gracias,

-CHB

-
Estás recibiendo esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/numpy/numpy/issues/10161#issuecomment-351182296 , o silenciar
la amenaza
https://github.com/notifications/unsubscribe-auth/AA38YDw4sdhRWYmeeyr4met1RCwWsjMQks5s_uBmgaJpZM4Q2J-P
.

Apoyo la mejora de la documentación, pero creo que el cambio propuesto no es tan útil como podría ser.

.. advertencia :: El valor predeterminado atol no es apropiado para números que
son mucho menos que uno.

En primer lugar, esta advertencia ni siquiera es cierta, en general. El valor predeterminado atol _es_ apropiado cuando desea tratar números menores que 1e-8 como iguales a cero. Esto no es posible con una tolerancia relativa, por lo que existe una tolerancia absoluta. Por el contrario, el valor predeterminado atol _ no_ es apropiado cuando desea tratar números menores que 1e-8 como significativos. No asumamos que una talla sirve para todos.

Por lo tanto, @xoviat , con el más profundo respeto, me opongo firmemente a su declaración subjetiva:

... assert_allclose _es_ el comportamiento correcto (o al menos lo suficientemente cerca).

Creo que el problema con los documentos actuales es que hace un muy buen trabajo al describir _ qué_ atol es, pero no describe _ por qué_ está ahí y, por lo tanto, es posible que los usuarios no entiendan cuándo cambiar el valor predeterminado .

Propongo algo en la línea de:

"Establecer un valor pequeño distinto de cero de atol permite tratar los números que están muy cerca de cero como si fueran efectivamente iguales a cero. Por lo tanto, el valor predeterminado de atol puede no ser apropiado para su uso Elija atol manera que los números mayores que atol sean considerados significativos (distintos de cero), y los números menores que atol sean considerados insignificantes (igual que cero) ".

Por último, agregaría la misma nota a las cadenas de documentos de isclose y allclose .

Esta es mi sugerencia. Tómelo o déjelo.
Salud.

Deje comentarios de revisión sobre el RP. En particular, su propuesta es demasiado detallada en mi opinión.

Lo siento todo si estaba siendo demasiado molesto, realmente no creía que hubiera un consenso todavía.

@xoviat : gracias por trabajar en los documentos.

Dado que esto volverá a surgir, voy a (probablemente) poner fin a esta discusión con una versión mini de una NEP diseñada para ser rechazada: resumir las propuestas y las razones del rechazo. Para que conste, dado que los números anteriores simplemente se agotaron.

Perdón por mencionar el problema de py2 / 3: si y cómo limpiar las verrugas en numpy es una discusión para otro lugar y momento.

El problema con isclose()

Este problema (y otros a los que se hace referencia) es el resultado de observaciones de que numpy.isclose() y los amigos usan un algoritmo menos que óptimo, tiene un valor predeterminado contencioso para atol y las diversas funciones relacionadas tienen diferentes valores predeterminados. En general, esto genera confusión para los usuarios y, en el peor de los casos, pruebas falsas positivas cuando los usuarios no piensan en establecer el valor atol .

En particular, la implementación en stdlib: math.isclose() , proporciona un algoritmo y valores predeterminados diferentes y posiblemente mejores: es una prueba simétrica, no mezcla rtol y atol, y el atol predeterminado es 0.0

Hay casi consenso en que la situación no es ideal, pero no hay consenso en que sea lo suficientemente mala como para hacer algo al respecto.

Opciones consideradas:

Cambio de algoritmos y / o valores predeterminados:

Rechazado universalmente debido a problemas de compatibilidad con versiones anteriores, incluso con un período de obsolescencia y advertencias: estas funciones se utilizan ampliamente en las pruebas, por lo que sería una gran molestia al menos.

Agregar un parámetro adicional con una bandera para seleccionar un algoritmo diferente

Esto no rompería ningún código existente, pero persistiría para siempre con una API fea y confusa.

Agregar una directiva de tipo __future__

TLDR: es posible, pero no fácil ni limpio. Nadie parecía querer seguir con esto.

Creando otra función

Esta parecía ser la única opción que ganó tracción, pero no fue apoyada por los desarrolladores principales que participaron en la discusión.

La forma más limpia de "arreglar" esto sería agregar un np.rel_close() [u otro nombre] con un nuevo algoritmo y valores predeterminados, probablemente los que se usan en math.isclose . La nueva función se documentaría como la "recomendada" para utilizar en el futuro código. Sería posible agregar advertencias de desaprobación al anterior en el futuro, pero nadie parecía pensar que el ruido valdría la pena en este momento.

Se podría construir una herramienta de refactorización para hacer el reemplazo, pero ¿quién va a hacer eso para este uso?

Esto daría como resultado probablemente dos funciones muy similares en el futuro previsible, y "Introducir nuevas funciones con un comportamiento solo ligeramente diferente de otras funciones que han trabajado así durante una década es en general una idea muy pobre".

Conclusión:

No vale la pena por la pequeña ganancia, pero se necesitan mejores documentos, y eso se hace aquí: # 10214

Todavía me queda una pregunta:

@njsmith escribió:

"" "
Nosotros ya tenemos:

allclose
assert_allclose
assert_almost_equal
assert_approx_equal
assert_array_almost_equal
assert_array_almost_equal_nulp
assert_array_max_ulp

No creo que agregar más opciones a esta lista vaya a hacer una gran diferencia para los usuarios reales.
"" "

¿Quiere decir que agregar uno nuevo sería una buena idea?

También señalaría:

la mayoría de estos son afirmaciones, y la proliferación de afirmaciones es un efecto secundario de la arquitectura unittest.

A medida que muchos de nosotros cambiamos a otras arquitecturas de prueba (por ejemplo, pytest), la necesidad de afirmaciones desaparece.

Entonces, qué hacer con numpy.testing es una pregunta separada de qué hacer con numpy core.

A medida que muchos de nosotros cambiamos a otras arquitecturas de prueba (por ejemplo, pytest), la necesidad de afirmaciones desaparece.

En realidad, esto es un peligro en sí mismo. Las pruebas pueden cambiarse de assert_allclose(...) a assert np.allclose(...) , y silenciosamente se volverán menos estrictas, lo cual es malo.

Todavía uso assert_allclose en el entorno pytest porque da un mensaje de falla útil:

    def test_shs():
        a = [0.1, 0.2, 0.3, 0.4]
        b = [0.2, 0.3, 0.3, 0.4]

>       np.testing.assert_allclose(a,b)
E       AssertionError: 
E       Not equal to tolerance rtol=1e-07, atol=0
E       
E       (mismatch 50.0%)
E        x: array([ 0.1,  0.2,  0.3,  0.4])
E        y: array([ 0.2,  0.3,  0.3,  0.4])

vs usar assert np.allclose()

    def test_shs():
        a = [0.1, 0.2, 0.3, 0.4]
        b = [0.2, 0.3, 0.3, 0.4]
>       assert np.allclose(a, b)
E       assert False
E        +  where False = <function allclose at 0x7f20b13c9840>([0.1, 0.2, 0.3, 0.4], [0.2, 0.3, 0.3, 0.4])
E        +    where <function allclose at 0x7f20b13c9840> = np.allclose

Podría ser posible solucionarlo implementando pytest_assertrepr_compare , pero no estoy seguro de cómo aplicar eso a las llamadas a funciones, y no puedo averiguar dónde lo invoca pytest.

@ eric-wieser: escribió:

"En realidad, esto es un peligro en sí mismo. Las pruebas pueden cambiarse de assert_allclose (...) a assert np.allclose (...), y silenciosamente se volverán menos estrictas, lo cual es algo malo".

Exactamente lo que quiero decir: es una "mala idea" suponer que todos van a usar las afirmaciones para realizar pruebas y, por lo tanto, no preocuparse por si los valores predeterminados de isclose () y allclose () son apropiados para las pruebas, en un ideal mundo que ciertamente deberían ser.

¿Quiere decir que agregar uno nuevo sería una buena idea?

No, quise decir que dado que ya tenemos toda una colección de formas ligeramente diferentes de expresar pruebas casi iguales, desafortunadamente la mayoría de los usuarios no van a notar o entender otra adición.

También señalaría:
la mayoría de estos son afirmaciones, y la proliferación de afirmaciones es un efecto secundario de la arquitectura unittest.
A medida que muchos de nosotros cambiamos a otras arquitecturas de prueba (por ejemplo, pytest), la necesidad de afirmaciones desaparece.

Sucede que se escriben como afirma, pero AFAICT cada una de esas funciones en realidad codifica una definición diferente de "casi igual". (Creo. Algunas de las distinciones son tan oscuras que no puedo decir si son reales o no).

Cambio de algoritmos y / o valores predeterminados:
Rechazado universalmente debido a problemas de compatibilidad con versiones anteriores, incluso con un período de obsolescencia y advertencias: estas funciones se utilizan ampliamente en las pruebas, por lo que sería una gran molestia al menos.

No lo diría exactamente así. Para mí, este es el único enfoque que potencialmente tendría beneficios suficientes para justificar el costo. No estoy diciendo que lo harían , y no puedo hablar por los otros desarrolladores principales; Me gustaría ver algunos datos, y sin datos, errar del lado del conservadurismo parece ser la elección correcta. Pero si alguien produjera datos, al menos los miraría :-).

Por ejemplo, si alguien se presentara y dijera "Probé el cambio propuesto en 3 grandes proyectos y generó 12 fallas adicionales de 10,000 pruebas, y de esas 12, 8 de ellas eran errores silenciosos reales que habían sido ocultos por el antiguo malos valores predeterminados "... eso sería bastante convincente. Especialmente porque este es un caso en el que tenemos la capacidad técnica para realizar advertencias con un objetivo limitado. Supongo que los números reales no serían tan favorables, pero hasta que alguien verifique, quién sabe.

El lunes 18 de diciembre de 2017 a las 3:57 p.m., Nathaniel J. Smith <
[email protected]> escribió:

No, quise decir que, dado que ya tenemos una colección completa de
formas ligeramente diferentes de expresar pruebas casi iguales, desafortunadamente
la mayoría de los usuarios no se darán cuenta ni comprenderán otra adición.

Bueno, hay una gran diferencia: cada uno de ellos se agregó porque lo hizo
algo diferente , y con los caprichos del punto flotante, esos
las diferencias pueden ser importantes.

Una nueva función de cercanía relativa haría esencialmente lo mismo, pero
hacerlo mejor". Y el objetivo sería recomendar que se utilice el nuevo
en lugar de lo viejo (y quizás desaprobar lo viejo eventualmente).

Honestamente, no estoy seguro de si eso es un argumento a favor o en contra de la idea.
aunque.

Por ejemplo, si alguien se presentara y dijera "Probé el cambio propuesto en 3 grandes

proyectos, y dio lugar a 12 fallas adicionales de 10,000 pruebas, y de esas
12, 8 de ellos eran errores silenciosos reales que habían sido ocultos por el viejo mal
valores predeterminados "... eso sería bastante convincente. Sobre todo porque se trata de una
caso en el que tengamos la capacidad técnica para realizar advertencias específicas.
Supongo que los números reales no serían tan favorables, pero hasta
alguien lo comprueba, quién sabe.

hmm, mi base de código tiene alrededor de 1500 pruebas, no 10,000, pero le daré una
Disparo. Si nada más, puedo encontrar un error o una prueba deficiente o dos. U obtener más
¡seguridades!

Oigan todos,

Quería intervenir aquí después de que un colega encontró este problema.

_Descargo de responsabilidad: Realmente no pensé mucho en la lógica antes de gritar "fuego en el pasillo" y tirar esto por encima de la cerca. Si he hecho un hash vergonzoso de ello, sea amable, por favor.

Para mí, todo esto suena mucho como el clásico problema de arquitectura / diseño del “huevo y la gallina”: semántica frente a implementación. No todos los errores existen dentro del código, a veces existen dentro de la semántica. Esto no es muy diferente (si es que lo hace) de un algoritmo que se implementa sin fallas solo para descubrir que el algoritmo es defectuoso: el error no está en el código, está en el algoritmo, pero de cualquier manera sigue siendo un error. Básicamente, esta discusión suena un poco a la broma clásica "no es un error, es una característica". Por supuesto, una "elección" es solo eso, pero en mi humilde opinión, esto normalmente indica el final de la razón fundamental: si la semántica se elige como está, tal como se implementa, entonces que así sea, la implementación es genial, fin de la discusión.

Entonces, ¿cuál es la semántica "deseada" y / o "esperada" de isclose() . Pensando en esto, me quedo convergiendo inexorablemente en la semántica definida por el usuario, es decir, el usuario necesita poder definir la semántica definición de "cerrar".

Defaults

Con respecto a los valores predeterminados, esto finalmente no rompe ninguna semántica. Sin embargo, una mala elección _es_ peligrosa. Si el valor predeterminado distinto de cero se eligió _sólo_ para dar un comportamiento razonable para cuando abs(a - b) == 0 , esto definitivamente suena como la solución incorrecta. Este estado en particular estaría mejor en una caja especial, ya que es una caja especial semánticamente. No hay "margen de maniobra" en la definición de "cerca" cuando la diferencia es cero, es la piedra angular autodefinida, es decir, no están _close_, son _exact_, donde "close" es una desviación relativa de la exacta . (_Nota: _La condicional de carcasa especial puede (o no) afectar el rendimiento).

+ frente a max

La mezcla o "sombreado" de rel_tol y a_tol debido a la implementación usando una suma y no max _no_ rompe la semántica de la función anterior. La definición del usuario de "cerrar" está limitada de una manera no trivial, ya que destruye, y por lo tanto rompe, la semántica de "relativo" y "absoluto". Considerando solo la semántica anterior, este aspecto _es_ un error. Encuentro @gasparka El ejemplo de apertura es un argumento irrefutable de este punto.

Conmutatividad (es decir, simetría)

En realidad, solo hay dos operadores aquí: - en a-b , y <= en |a-b| <= f(a, b, atol, rtol) . Si bien la resta es anticomutativa, |a - b| no lo es, es conmutativa, por definición. Sin embargo, esto por sí solo puede no ser suficiente para establecer la conmutación de f(a, b, atol, rtol) . Sin embargo, aquí hay un argumento fuerte y débil.

  • El débil es el más simple - encapsulación de separación de funciones o falta de - mientras que aritméticamente la conmutatividad de |a - b| puede no imponer la de f(a, b, atol, rtol) , programáticamente, f(a, b, atol, rtol) no está realmente en pregunta pero isClose(a, b, atol, rtol) . Aquí, el cálculo |a - b| se realiza internamente a la función, es decir, isClose() := |a-b| <= f(a, b, atol, rtol) . Como esto es interno y se pasan a y b , para que |a-b| sea ​​conmutativo isClose() también debe serlo. Si no es así, la conmutatividad de |a-b| pierde todo su significado.
  • Argumento sólido: la operación de comparación no es estricta, es decir, es <= no < , por lo tanto, para la parte de igualdad de la comparación debe satisfacer |a-b| = f(a, b, atol, rtol) que implica f(a, b, atol, rtol) _ debe_ también ser conmutativo (¿creo?). No hacerlo significa que la igualdad anterior es _nunca_ verdadera (no hay valores de a & b que satisfagan esto) o que la desigualdad estricta, < , ha en realidad se ha definido semánticamente, ¿verdad?

Lo que haces (o no haces) sobre todo o parte de esto es una pregunta completamente separada.

¿Fue útil esta página
0 / 5 - 0 calificaciones