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

Criado em 5 dez. 2017  ·  78Comentários  ·  Fonte: 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)

Observe que a equação de Numpy não é simétrica e correlaciona os parâmetros atol e rtol , ambos são coisas ruins (IMO).

Aqui está uma situação em que Numpy "incorretamente" sinaliza dois números como iguais:

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

Aqui está outro, este problema de simetria 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

A versão matemática do Python parece ser à prova de balas, o Numpy deve começar a usá-la? Há algum benefício em usar a versão Numpy?

57 - Close?

Comentários muito úteis

@njsmith : obrigado por me trazer.

Um pouco de história: quando propus isclose para o stdlib, certamente consideramos o numpy como arte anterior. Se fosse só eu, eu poderia ter usado uma abordagem compatível, por uma questão de compatibilidade :-).

Mas o resto da comunidade pensou que era mais importante fazer o que era "certo" para Python, então uma longa discussão se seguiu ... Tentei capturar a maior parte do ponto no PEP, se você quiser dar uma olhada.

Aqui está a referência a numpy:

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

Você pode ver que os mesmos pontos foram levantados nesta discussão.

No final, três pontos principais surgiram:

1) uma abordagem simétrica resultaria no mínimo de surpresa.

2) a tolerância absoluta padrão deve ser provavelmente zero, de modo a não fazer suposições sobre a ordem de magnitude dos argumentos.

3) a diferença entre os testes "fracos" e "fortes" era irrelevante quando usados ​​com tolerâncias pequenas, como é o caso de uso esperado.

No final, acho que encontramos a "melhor" solução para matemática.

Mas é "melhor" o suficiente para quebrar a compatibilidade com versões anteriores? Acho que não.

Infelizmente, muito do numpy (e python) teve muitos recursos adicionados porque eram úteis, mas sem muito da discussão atual essas coisas aparecem, então temos muitos designs abaixo do ideal. Isso é esperado (e necessário) para uma biblioteca jovem, e só temos que conviver com isso agora.

@njsmith está certo - acho que muito poucos usos de np.isclose () têm as tolerâncias definidas por uma análise de erro rigorosa de FP, mas sim, tente um erro, com o próprio np.isclose ().

No entanto, acho que o maior problema é o padrão atol - ele assume que seus argumentos são de ordem 1 - o que pode ser uma suposição MUITO errada, e uma vez que muitas vezes resultaria em testes que não deveriam , os usuários podem não perceber.

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

ai!

e sim, é atol causando isso:

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

Portanto, provavelmente é uma boa ideia ter um caminho a seguir.

Eu gosto da ideia do argumento de palavra-chave - parece muito mais simples do que tentar mexer com __future__ e coisas do gênero.

E poderíamos então decidir se queríamos começar a emitir avisos de descontinuação e, finalmente, alterar o padrão de muitas versões posteriores ...

Todos 78 comentários

Tarde demais para mudar qualquer coisa.

Esta é uma das funções mais amplamente utilizadas em todos os numpy (até assert_allclose ). O único caminho para isso seria escolher outro nome.

Confesso que a implementação atual parece um bug. Observe que usar max(abs(a), abs(b)) vez de abs(b) não quebrará nenhum teste existente, apenas relaxa a condição no caso de abs(a) > abs(b) .

No mínimo, isso precisa de um aviso no docstring de que não corresponde ao integrado

Que tal adicionar isso?

from numpy.__future__ import isclose

A propósito, isso pode ser uma ideia para o controle de versão de números aleatórios ...

Para esclarecimento, a importação não importaria realmente isclose, mas habilitaria a nova semântica. Seria reduzido a um módulo.

A ideia é que podemos permitir que as pessoas usem a função correta e, ao mesmo tempo, não precisamos quebrar nenhum código existente.

, a importação não importaria realmente isclose, mas habilitaria a nova semântica

Não acho que seja possível fazer isso não importar. Basta escolher um nome mais longo, como mathlike_ufunc s, que também pode abranger remainder .

Observe que from __future__ import print_function realmente produz um print_function global

Na verdade, o estilo de importação from __future__ import * pode não ser apropriado aqui - em python, ele afeta _sintaxe_ em um nível por arquivo, e fazer algo semelhante em numpy seria complicado

As exceções do iterador não eram uma mudança de sintaxe. Precisamos apenas de um pequeno módulo C para inspecionar o namespace do módulo de chamada. Podemos até armazenar os módulos em cache, de forma que apenas o tempo de importação seja lento, e apenas trivialmente.

Você está certo - e true_division é obviamente análogo a isclose.

Esta é na verdade uma boa maneira de evitar os problemas em # 9444, como uma alternativa para usar instruções with para gerenciar recursos obsoletos.

Vamos CC @ ChrisBarker-NOAA como o criador de math.isclose .

Este é um problema bem menor, IMO. Geralmente atol e rtol são escolhidos mexendo neles até que os testes passem, e o objetivo é detectar erros que são uma ordem de magnitude maior do que as tolerâncias. Talvez faça sentido relaxar a parte rtol como @charris sugeriu, mas eu realmente não acho que vale a pena isclose costuma ser chamado indiretamente por coisas como assert_allclose ou vários auxiliares de teste terceirizados.

Eu perguntei no StackOverflow sobre o uso de importações de estilo __future__ : https://stackoverflow.com/questions/29905278/using-future-style-imports-for-module-specific-features-in-python

TLDR: é possível, mas não é fácil nem limpo.

A introspecção da pilha não é apenas para isso, mas é proposta como uma política geral para alterar os valores de retorno das funções. A questão é: em princípio, isso deve ser alterado? Acho que, se a resposta for sim, o período de suspensão de uso precisa ser de pelo menos alguns anos, devido ao uso generalizado. Python nunca incluiu um módulo anterior, mas essa é uma opção para tentar melhorar a estabilidade da API, se houver demanda.

O outro método seria apenas adicionar isso como uma opção isclose(...,symmetric=True) ou assert_allclose(symmetric=True) onde o padrão seria False , a situação atual.

Eu concordo que este é um problema menor quando você não coloca um significado para os valores rtol e atol , apenas ajustá-los para passar nos testes de unidade, conforme mencionado por @njsmith.

No entanto, às vezes você gostaria de dizer que o erro está dentro, por exemplo, de 1% ( rtol=0.01 ).
Neste caso, o erro do pior caso em relação à medição de rtol é 100% ( rtol * abs(b) chega perto de atol , então atol + rtol * abs(b) ~= 2 * rtol * abs(b) ).

O que significa que alguns valores podem passar com cerca de 2% de erro:

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 a ideia de @charris tornaria este caso particular um pouco pior, pois relaxa ainda mais a comparação, mas ainda vale a pena, pois elimina o problema de simetria e, de fato, é compatível com versões anteriores.

IMO, seria melhor se o Numpy usasse a função matemática, mas eu entendo que a alteração pode ser muito perturbadora e possivelmente não importante para a maioria dos usuários. Tornar a opção de alternar entre o isclose core seria útil.

@njsmith : obrigado por me trazer.

Um pouco de história: quando propus isclose para o stdlib, certamente consideramos o numpy como arte anterior. Se fosse só eu, eu poderia ter usado uma abordagem compatível, por uma questão de compatibilidade :-).

Mas o resto da comunidade pensou que era mais importante fazer o que era "certo" para Python, então uma longa discussão se seguiu ... Tentei capturar a maior parte do ponto no PEP, se você quiser dar uma olhada.

Aqui está a referência a numpy:

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

Você pode ver que os mesmos pontos foram levantados nesta discussão.

No final, três pontos principais surgiram:

1) uma abordagem simétrica resultaria no mínimo de surpresa.

2) a tolerância absoluta padrão deve ser provavelmente zero, de modo a não fazer suposições sobre a ordem de magnitude dos argumentos.

3) a diferença entre os testes "fracos" e "fortes" era irrelevante quando usados ​​com tolerâncias pequenas, como é o caso de uso esperado.

No final, acho que encontramos a "melhor" solução para matemática.

Mas é "melhor" o suficiente para quebrar a compatibilidade com versões anteriores? Acho que não.

Infelizmente, muito do numpy (e python) teve muitos recursos adicionados porque eram úteis, mas sem muito da discussão atual essas coisas aparecem, então temos muitos designs abaixo do ideal. Isso é esperado (e necessário) para uma biblioteca jovem, e só temos que conviver com isso agora.

@njsmith está certo - acho que muito poucos usos de np.isclose () têm as tolerâncias definidas por uma análise de erro rigorosa de FP, mas sim, tente um erro, com o próprio np.isclose ().

No entanto, acho que o maior problema é o padrão atol - ele assume que seus argumentos são de ordem 1 - o que pode ser uma suposição MUITO errada, e uma vez que muitas vezes resultaria em testes que não deveriam , os usuários podem não perceber.

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

ai!

e sim, é atol causando isso:

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

Portanto, provavelmente é uma boa ideia ter um caminho a seguir.

Eu gosto da ideia do argumento de palavra-chave - parece muito mais simples do que tentar mexer com __future__ e coisas do gênero.

E poderíamos então decidir se queríamos começar a emitir avisos de descontinuação e, finalmente, alterar o padrão de muitas versões posteriores ...

Eu gostaria de propor a sugestão de @bashtage :

"" "
O outro método seria apenas adicionar isso como uma opção isclose (..., symmetric = True) ou assert_allclose (symmetric = True) onde o padrão seria False, a situação atual.
"" "

Acho que é uma opção melhor do que um novo nome de função e pode levar a uma futura depreciação e alteração do padrão (ou não).

Eu acho que além do teste simétrico (forte), atol deve ser padronizado como zero também.

Dado isso, talvez precisemos de um nome melhor para o parâmetro do que symmetric , embora não seja ruim ...

Ah - e pode ser bom verificar se isclose() não é chamado em nenhum outro lugar em numpy além de assert allclose() .

A maneira mais fácil de fazer isso é provavelmente colocar um aviso de suspensão de uso em e
em seguida, remova-o antes de mesclar. Podemos ir em frente e colocar o
aviso de depreciação que diz para alterar o código existente para
simétrico = falso; não há razão para dizer que o padrão precisa ser
alterado em breve, mesmo que o aviso esteja lá.

Precisamos adicioná-lo a assert_allclose também ou apenas fazemos a alteração silenciosamente? Fazer as pessoas atualizarem cada um de seus testes para silenciar um aviso não é realmente diferente de fazê-las atualizar seus testes para corrigir a redução de tolerância

@xoviat @ eric-wieser não pode haver nenhuma alteração incompatível para trás em assert_allclose nem um aviso de depreciação dele, muito perturbador. A menos que esteja faltando onde você deseja colocar um aviso de suspensão de uso.

Receio que isso ainda pareça muito doloroso para infinitesimal
ganho, para mim.

Ter o atol diferente de zero por padrão não é um acidente. É necessário obter
comportamento padrão sensato para qualquer teste onde o resultado esperado inclui
zeros exatos.

Nenhuma mudança em qualquer assert_allclose é necessária, pois seria atualizado para
internamente para usar o comportamento antigo.

As mudanças silenciosas da IMO são desagradáveis! O aviso de suspensão de uso é apenas para que
eventualmente, as pessoas não precisam digitar a bandeira em um prompt interativo para
obter o novo comportamento, nada mais

As mudanças silenciosas da IMO são desagradáveis!

Acordado. No entanto, deve haver zero alterações. Avisos de descontinuação em funções amplamente utilizadas também forçam os usuários (pelo menos aqueles que realmente fazem a manutenção) a fazer alterações.

Que tal corrigir o problema de não simetria como @charris sugeriu? Isso também liberaria algum espaço de documentação, que poderia ser preenchido com um aviso sobre as coisas ruins que podem acontecer quando atol != 0.0 .

Não podemos corrigir esse problema sem avisar as pessoas sobre a mudança
comportamento. A única maneira que sei fazer isso é com um aviso.

O que seria realmente bom é uma ferramenta de refatoração automatizada para injetar sinalizadores
em código antigo. Então, 'manutenção' seria apenas executar um script em seu
código; 5 minutos de trabalho.

Desculpe, isso pode ser corrigido, apenas com um novo sinalizador.

Que tal corrigir o problema de não simetria como @charris sugeriu?

Essa sugestão (https://github.com/numpy/numpy/issues/10161#issuecomment-349384830) parece viável.

Não podemos corrigir esse problema sem informar as pessoas sobre a mudança de comportamento.

Não estamos consertando isso. Essa é a questão de custo / benefício de qualquer mudança que está sendo discutida na questão do versionamento semântico. Este é definitivamente um caso em que não faremos nenhuma alteração ou emitiremos qualquer aviso de uma função de teste amplamente utilizada. Observe que o comportamento atual não é um bug (embora indiscutivelmente uma escolha abaixo do ideal).

Para ficar claro, eu estava propondo uma alteração para isclose, mas não assert_allclose.
Olhando para as fontes, essa mudança será um terço como perturbadora.
No entanto, o benefício provavelmente ainda é muito pequeno. Eu não acho que ninguém
objetos para adicionar uma bandeira, certo?

Acho que ninguém se opõe à adição de uma bandeira, correto?

Não tenho certeza, depende dos detalhes. A sugestão de @charris pode não exigir um sinalizador, e qualquer sinalizador que introduza isso é um pouco questionável para matrizes:

In [1]: import math

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

Oponho-me a adicionar sinalizadores sem motivações e casos de uso concretos. Teste de tornassol útil: se um desenvolvedor novato perguntasse por que essa bandeira existe, como você explicaria?

como você explicaria?

  1. Use a sinalização symmetric=True se quiser vetorizar chamadas para math.isclose
  2. Tinha que ser um sinalizador para não quebrar o código existente.

Estou mais procurando situações em que haja algum problema real sendo resolvido.

Provavelmente, o único problema real sendo resolvido aqui é fazer com que iniciantes
pense mais sobre a precisão do ponto flutuante. Quer dizer, o exemplo de @rgommers
parece que seria errado, mas e se você estiver lidando com algo muito pequeno
números? IMO a implementação math.isclose é melhor, mas eu nem mesmo
acho que há consenso sobre isso. Infelizmente, não há realmente um
solução única para todos para determinar se os números estão "próximos". Mas
com base nas respostas de outras pessoas (que não estão erradas!), eu realmente não
quaisquer mudanças na API daqui para frente. Eu acho que a única ação a tomar é
provavelmente uma atualização de documentação então (últimas palavras famosas, visto que eu
pensava que uma bandeira estaria bem)?

Certamente, uma nota comparando com math.isclose e documentando como obter um comportamento vetorial que é idêntico ao definir rtol e atol, se alguém quiser, está ok.

Além disso, se bem entendi, @charris propôs uma solução para permitir
está perto de ser menos tolerante do que é atualmente, o que não quebraria
quaisquer testes. Ainda acho que seria uma boa ideia emitir um aviso (o
aviso deve ser emitido uma vez) se houver uma situação onde isclose seria
considere os números como "próximos" quando antes não eram. Isso é muitomelhor do que simplesmente mudar o comportamento da função silenciosamente e não deixar
qualquer um sabe quando isso os afeta.

Acho que alguns esclarecimentos são necessários aqui sobre o que estamos discutindo. Existem duas coisas que fazem math.isclose diferir:

  • Um padrão diferente para atol
  • Uma definição diferente de rtol

Não acho que possamos fazer nada sobre o primeiro problema, a não ser documentá-lo como sendo diferente de `math.isclose.

O segundo problema, eu acho que é melhor resolvido adicionando um argumento symmetric cujo padrão é False . Agora podemos escrever em nossos documentos _ " np.isclose(x, y, symmetric=True, atol=0) é uma versão vetorizada de math.isclose(x, y) " _, que para mim parece ser o que estamos tentando resolver.

A partir daqui, temos três opções:

  1. Documente o argumento extra e não faça mais nada
  2. Rejeite chamar isclose sem o argumento, forçando os usuários a escrever isclose(..., symmetric=False) para obter o comportamento antigo sem um aviso (e similar para allclose ). Eu suspeito que isso não vai atingir muitos códigos, mas o resultado é menos legível e sem grandes ganhos. assert_close seria alterado para chamar isclose(..., symmetric=False) internamente, portanto, os usuários dele não seriam afetados
  3. Como o acima, mas também requer o argumento symmetric para assert_close . Isso seria uma grande rotatividade a jusante
  4. Silenciosamente mude o comportamento

Destes, acho a opção 1 inquestionável, mas o resto não parece coisas que valham a pena interromper.

Edit: 2 pode ser aceitável se o aviso for emitido apenas se o comportamento for alterado, o que seria muito menos ruidoso.

@ eric-wieser Há uma terceira diferença: a maneira atol e rtol são combinados. ( math.isclose usa max , numpy.isclose usa + ). Isso significa que, a menos que atol ou rtol seja zero, não há nenhuma maneira geral de fazer uma chamada de math.isclose corresponder a uma chamada de numpy.isclose .

Ainda não acho que vale a pena adicionar APIs visíveis ao usuário.

Eu era a favor da opção dois. Ainda sou a favor dessa opção, com
a estipulação adicional de que numpy forneceria um sistema automatizado
ferramenta de refatoração (adicionada a entry_points) que você pode simplesmente executar em seu
projetos existentes para corrigi-los. Com base no que outros disseram, parece
esta opção não seria favorecida por outros.

Não sou e nunca fui a favor das opções três ou quatro. Dentro
Além disso, não sou a favor de mudar o comportamento da função até um aviso
foi emitido para pelo menos quatro lançamentos principais.

Supondo que outros discordem da opção dois (que eles têm), eu estaria
a favor da opção um. Mas outros (especialmente @njsmith) não são a favor
de qualquer uma das opções que você deu aqui. Pelo menos essa é minha percepção.

@njsmith Isso não está correto; você pode alterar o comportamento da função com o sinalizador.

Estou dobrando essa terceira diferença em um _ "rtol é diferente" _

"" "
Estou mais procurando situações em que haja algum problema real sendo resolvido.
"" "
Aposto dólares em donuts (o que vou fazer porque não tenho ideia do que isso significa ..) que existem testes por aí que estão passando e não deveriam, porque o atol está tornando o teste muito menos sensível que deve ser.

Parece que há três "problemas" com a implementação atual:

1) não é simétrico

  • Eu acho que isso é muito ruim, mas realmente não é grande coisa, e quase não faz diferença quando os valores realmente estão próximos :-) Eu _ acho_ que literalmente não faz diferença se rtol <1e-8 (pelo menos se atol for 0,0)

2) o atol afeta o resultado mesmo quando não é comparado a zero (isso é inevitável) - mas efetivamente altera a tolerância em cerca de um fator de dois - ou ainda mais se o atol for grande, o que poderia ser se estivesse trabalhando com grande ordem de valores de magnitude.

3) atol é diferente de zero ser o padrão - eu realmente acho que este é o maior problema (particularmente com o algoritmo atual de adição de ambos), pois pode facilmente levar à aprovação de testes que não deveriam - e muitas vezes somos um pouco preguiçoso - escrevemos o teste com a tolerância padrão, e se ele passar, achamos que terminou. (quando percebi que era assim que funcionava, voltei ao código y, e ENCONTREI alguns deles - opa!

Alguém neste tópico disse algo sobre "haveria algo estranho se:

isclose (1e-200, 0,0)

retornou False por padrão. Eu discordo - sim, isso seria surpreendente, mas forçaria o usuário a pensar sobre o que está acontecendo, enquanto a implementação atual resulta em (por exemplo):

Em [8]: np.isclose (1e-20, 1e-10)
Fora [8]: Verdadeiro

mesmo? um é DEZ ORDENS de MAGNITUDE maior que o outro e volta Verdadeiro ????

Meu ponto é que ter atol diferente de zero dá resultados talvez menos surpreendentes no caso comum de comparação com zero, mas resultados MUITO mais perigosos e errados ao trabalhar com números pequenos (pequeno realmente sendo qualquer coisa menor que a ordem de magnitude 1.

E se você estiver trabalhando com números grandes, digamos maiores que 1e8, então o atol padrão também é inapropriado.

E junto com (2), isso significa que o atol padrão também pode bagunçar os testes de tolerância relativa de maneiras surpreendentes.

Então: não, não é um bug, e não está "quebrado", mas está muito abaixo do ideal, então seria bom ter um caminho a seguir para uma implementação melhor.

Eu gostei da abordagem do sinalizador, mas acho que estou mudando de ideia - o problema é que, a menos que o descontinuemos e alteremos o sinalizador padrão em algum momento, quase todos usaremos o algoritmo "antigo" praticamente para sempre. E tem havido muitos bons argumentos pelos quais provavelmente não poderíamos descontinuá-lo.

Então, talvez uma nova função, com um novo nome, seja necessária. Poderíamos adicionar à documentação encorajando as pessoas a usarem o novo e _talvez_ adicionar avisos em algum ponto quando as pessoas usarem o novo, mas nunca quebraríamos o código de ninguém.

Alguém tem uma ideia de um bom nome para uma nova função ????

Talvez np.math.isclose e amigos? Eu não sei.

Isso resolve np.remainder sendo diferente também, e também permite que você vetorize facilmente o código usando from numpy import math

Então, eu seria +1 em um módulo np.math

A introdução de novas funções com comportamento apenas ligeiramente diferente de outras funções que funcionaram dessa forma por uma década é, em geral, uma ideia muito pobre. Nesse caso, realmente não há muito problema que não possa ser resolvido adequadamente com a documentação. Portanto, -1 em uma nova função. E definitivamente -1 em um submódulo totalmente novo.

mesmo? um é DEZ ORDENS de MAGNITUDE maior que o outro e volta Verdadeiro ????

Se sua expectativa é verdadeira ou falsa, depende totalmente do contexto. Se for para números que vêm de uma distribuição contínua em [0, 1), então sim, você provavelmente espera True. Um usuário realmente deve entender as tolerâncias absolutas / relativas e o que uma função realmente faz, e é para isso que servem os documentos.

symmetric_isclose() é um pouco prolixo, mas não ofensivo aos meus olhos.: bicicleta: emoji>

Em 10 de dezembro de 2017, às 20h09, Ralf Gommers [email protected] escreveu:

Apresentando novas funções com comportamento apenas ligeiramente diferente de outros
funções que funcionaram assim por uma década são, em geral, uma
idéia.

Eu também não gosto - mas quebrar o código das pessoas com uma mudança é pior.
Você tem alguma ideia além de simplesmente não tornar o numpy melhor?

Neste caso, realmente não há muito problema que não possa ser adequadamente
resolvido com documentação.

Eu discordo - os padrões são importantes - muito.

E eu não acho que haja alguma maneira de ajustar os parâmetros para lhe dar um
comparação simétrica.

E definitivamente -1 em um submódulo totalmente novo.

Eu também. Se houver outras coisas no módulo de matemática sem numpy
equivalentes que seriam úteis, eles podem ser adicionados ao namespace de numpy
como tudo mais.

mesmo? um é DEZ ORDENS de MAGNITUDE maior do que o outro e volta
Verdadeiro????

Se sua expectativa é verdadeira ou falsa, depende totalmente do contexto.

Exatamente - é por isso que NÃO EXISTE um padrão “razoável” para o atol.

Se for para números que vêm de uma distribuição contínua em [0, 1)
então sim, você provavelmente espera True.

Mas isclose é anunciado como uma comparação relativa - e aqui o
a relativa está sendo completamente apagada, seja a comparação absoluta,
sem que o usuário tenha que ter pensado nisso.

Esse é todo o meu ponto - o padrão é apropriado SOMENTE se você espera que seu
os valores são da ordem de magnitude 1. Um caso comum, claro, mas não
universal.

Isso se resume ao que é pior - um falso negativo ou um falso positivo.
E no caso de uso comum de teste, um falso positivo é muito pior.

(ou seja, um teste de aprovação que não deveria)

Um usuário realmente deve entender as tolerâncias absolutas / relativas e o que
função realmente faz,

Absolutamente - mas uma função também deve ter padrões razoáveis ​​e um
algoritmo robusto que é fácil de entender.

Estou tendendo a melhorar a documentação e deixar a função de lado. A falta de simetria pode ser explicada como "isclose (a, b) significa que a está próximo de b, mas devido à precisão absoluta variável do ponto flutuante, nem sempre é o caso que b está próximo de a. No teste, b deve ser o resultado esperado e a deve ser o resultado real. " Observe que isso faz sentido para o teste, o caso de função simétrica é na verdade um pouco mais complicado de justificar. O erro relativo, envolvendo divisão como ocorre, não é simétrico.

Eu também não gosto - mas quebrar o código das pessoas com uma mudança é pior. Você tem alguma ideia além de simplesmente não tornar o numpy melhor?

Você já está fazendo um julgamento de valor aqui, dizendo que adicionar sua nova função é melhor do que nenhuma função nova. Não é imho. Três opções:

  1. Uma alteração significativa - não aceitável, você pode parar de discuti-la.
  2. Apenas adicionando documentos melhores
  3. Adicionando uma função

Em geral, 2 é uma escolha melhor do que 3, portanto, é isso que torna numpy "melhor". Você também está ignorando que essa coisa de atol / rtol não está limitada a uma única função, então o que vem a seguir - um novo e ligeiramente "melhor" assert_allclose ? Isso torna o caso apenas para documentos ainda mais claro.

Este é um defeito bastante sério, basicamente o código que testa seu código está bugado ... não é bom. Você enviaria qualquer coisa para a lua que fosse testada com numpy.isclose e atol padrão? Eu pensaria duas vezes ... e é por isso que precisamos fazer essas armadilhas se destacarem na documentação.
Adicionar uma função de alias irá apenas bagunçar a base de código, a menos que seja forçado aos usuários (o que não acontece).

Eu concordo que o problema de simetria é menor, mas ainda devemos consertá-lo. Deixá-lo pode distrair os usuários de armadilhas reais.

@rgommers escreveu:
"" "
Você já está fazendo um julgamento de valor aqui, dizendo que adicionar sua nova função é melhor do que nenhuma função nova. Não é imho.
"" "
Bem, eu passei muito tempo pensando e debatendo sobre math.isclose , e começamos olhando para a implementação numpy, entre outras. então, sim, acho que essa abordagem é melhor. E eu pensei, a partir dessa discussão, que era praticamente um consenso.

E obter um algoritmo / interface melhor no numpy o torna melhor, sim.

Talvez você queira dizer que ter a função antiga e a nova, melhor, em numpy NÃO é melhor do que simplesmente deixar a função antiga (talvez melhor documentada) lá. Claro, esse é um ponto totalmente válido, mas eu estava tentando ter essa discussão, e o comentário anterior de que "Introduzir novas funções com comportamento apenas ligeiramente diferente de outras funções que funcionaram assim por uma década é em geral uma ideia muito ruim" pareceu encerrar a discussão - meu ponto é que se a nova forma é "melhor o suficiente", então valeria a pena. O que claramente não temos um consenso é se essa opção particular é "melhor o suficiente", não se é "melhor".

E, a propósito, eu pessoalmente não me convenci de que vale a pena mudar, mas quero discutir.

Aqui estão algumas suposições da minha parte. Não sei se poderemos saber com certeza se eles estão corretos, mas:

1) o maior uso de np.isclose () é para teste - suas respostas são próximas o suficiente do que você espera? - isso é por meio de np.assert_all_close, ou mais diretamente, em testes de pytest ou ....

2) A maioria das pessoas, na maioria das vezes, não faz nada como uma análise rigorosa de erros de ponto flutuante para determinar o quão boas as respostas devem ser. Em vez disso, eles tentam um valor e, se falhar, examinam os resultados e decidem se realmente é um erro ou se é necessário ajustar as tolerâncias do teste.

  • isso significa que não importa muito se o atol e o rtol são mesclados e se o teste é simétrico.

3) muitas pessoas, na maioria das vezes, iniciam o processo em (2) com tolerâncias padrão, e só procuram ajustar a tolerância se um teste falhar.

  • ISTO significa que ter um atol padrão é muito perigoso - passar em testes que não deveriam é uma coisa realmente ruim.

4) As pessoas não leem documentos (além do estágio inicial "como eu chamo isso") - pelo menos não até que encontrem um comportamento confuso e, então, podem entrar e tentar entender como algo realmente funciona para esclarecer a confusão . Mas veja (3) - se um teste não falhar, eles não sabem ver os documentos para entender o porquê.

Tudo isso me leva à conclusão de que numpy seria "melhor" com um teste de proximidade de FP mais matemático.

E por que melhor documentar uma ótima ideia, mas não o suficiente.

Talvez eu esteja totalmente errado, e a maioria das pessoas lê cuidadosamente os documentos e seleciona rtol e atol cuidadosamente para seus problemas na maioria das vezes - mas eu sei que eu, nem a meia dúzia de pessoas em minha equipe, fiz isso até me tornar ciente desses problemas.

: bikeshed: (droga, isso não funcionou - nenhum emoji bacana)

talvez relatively_close ou rel_close ?

Um aspecto "divertido" adicional: assert_allclose realmente usa atol = 0 por
padrão. Há outra discussão inteira em algum lugar debatendo se podemos consertar
essa inconsistência. (No meu telefone, não consigo encontrá-lo facilmente.)

Em 11 de dezembro de 2017, 14:58, "Chris Barker" [email protected] escreveu:

@rgommers https://github.com/rgommers escreveu:
"" "
Você já está fazendo um julgamento de valor aqui que adicionar sua nova função
é melhor do que nenhuma nova função. Não é imho.
"" "
Bem, eu passei muito tempo pensando e debatendo sobre
math.isclose, e começamos observando a implementação numpy
entre outros. então, sim, acho que essa abordagem é melhor. E eu pensei
a partir dessa discussão, isso foi basicamente um consenso.

E obter um algoritmo / interface melhor no numpy o torna melhor, sim.

Talvez você queira dizer que ter tanto o antigo quanto o novo, melhor, funcionam em
entorpecido NÃO é melhor do que simplesmente deixar o velho (talvez melhor
documentado) funcionar lá. Claro, esse é um ponto totalmente válido, mas eu estava
tentando ter essa discussão, e o comentário anterior que "Apresentando
novas funções com comportamento apenas ligeiramente diferente de outras funções
que funcionam assim há uma década é, em geral, uma ideia muito pobre "
parecia encerrar a discussão - meu ponto é que, se a nova forma é
"melhor o suficiente" do que valeria a pena. O que claramente não temos
consenso é se esta opção particular é "melhor o suficiente", não
se é "melhor".

E, a propósito, eu pessoalmente não me convenci de que vale a pena
mudar, mas eu quero ter a discussão.

Aqui estão algumas suposições da minha parte. Não sei se podemos
sempre saiba com certeza se eles estão corretos, mas:

1

o maior uso de np.isclose () é para teste - são suas respostas
perto o suficiente do que você espera? - isso é via np.assert_all_close, ou
mais diretamente, em testes de teste, ou ....
2

A maioria das pessoas, na maioria das vezes, não faz nada parecido com o rigoroso
análise de erro de ponto flutuante para determinar o quão boas são as respostas
esperado que seja. Em vez disso, eles tentam um valor e, se falhar, olham para o
resultados e decidir se é realmente um erro, ou se é necessário ajustar o
tolerâncias do teste.

  • isso significa que não importa muito se o atol e
    rtol é mesclado e se o teste é simétrico.

  • muitas pessoas, na maior parte do tempo, iniciam o processo em (2) com
    tolerâncias padrão e apenas tente ajustar a tolerância caso um teste falhe.

  • ISTO significa que ter um atol padrão é muito perigoso - testes
    passar que não deveria é uma coisa muito ruim.

  • As pessoas não leem documentos (além do "como faço para chamar isso" inicial
    estágio) - pelo menos não até que encontrem um comportamento confuso, e então eles
    pode entrar e tentar entender como algo realmente funciona para esclarecer
    a confusão. Mas veja (3) - se um teste não falhar, eles não sabem
    vá dar uma olhada na documentação para entender o porquê.

Tudo isso me leva à conclusão de que numpy seria "melhor" com um
mais teste de proximidade FP semelhante a math.isclose.

E por que melhor documentar uma ótima ideia, mas não o suficiente.

Talvez eu esteja totalmente errado e a maioria das pessoas leia atentamente os documentos e
selecione o rtol e o atol cuidadosamente para o problema deles na maioria das vezes -
mas eu sei que eu, nem a meia dúzia de pessoas da minha equipe, fiz isso até que
tornou-se ciente desses problemas.

: bikeshed: (droga, isso não funcionou - nenhum emoji bacana)

talvez relativamente_close ou rel_close?

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/numpy/numpy/issues/10161#issuecomment-350886540 ou mudo
o segmento
https://github.com/notifications/unsubscribe-auth/AAlOaNquy47fsOkvBlxcSa-Mxyhgdimlks5s_bORgaJpZM4Q2J-P
.

Você quer dizer # 3183?

assert_allclose realmente usa atol = 0 por padrão.

Interessante - isso me faz sentir melhor.

Isso está trazendo de volta uma vaga memória de discussões anteriores sobre as diferenças entre os dois - foi a única?

Oi,
Minhas bibliotecas thermo, fluids e ht fazem uso extensivo do assert_allclose do numpy, portanto, estou compartilhando algumas ideias aqui, já que estão relacionadas. Este tópico faz a diferença comportamental parecer muito alarmante, e um pouco como as pessoas deveriam esperar encontrar bugs em seu código por causa da falta de simetria e / ou da diferença na combinação de atol e rtol, bem como o atol padrão (não presente em assert_allclose , Eu sei). Então, eu queria ver se minhas bibliotecas tinham algum bug e hackear uma implementação muito grosseira de assert_allclose que mudei meus testes para usar temporariamente.

Acho que meu uso de assert_allclose é representativo - eu comparo valores selecionados individualmente e faço testes em que confundo coisas e chamo assert_allclose no resultado de funções parametricamente. Eu comparo ints e floats de todos os tipos de magnitudes. Eu pensei relativamente pouco na escolha de um rtol ou atol, apenas raramente adicionando um atol ou alterando o rtol para um padrão menos estrito. As bibliotecas chamam assert_allclose 13159, 1178 e 4243 vezes, respectivamente.

Então eu fiz a mudança e executei meus testes de teste. Estou muito feliz em dizer que não encontrei nenhum novo bug ou falha de teste ; as únicas falhas de teste que consegui encontrar foram com minha implementação de assert_allclose.

Eu sei que existem outros que seriam menos afortunados e enfrentariam um problema se algo em assert_allclose ou isclose fosse alterado. Eu pessoalmente me sentiria mais confortável escrevendo testes se tivesse um modo assert_allclose que reproduzisse o comportamento de math.allclose, seja por meio de outra função ou do sinalizador symmetric . Mas eu sei que é muito trabalho para alguém fazer e ainda não houve PRs. Fico feliz em ter o conforto de ter verificado meu código para esta falha mesmo que uma vez!

4880 é o que eu estava pensando.

Em 11 de dezembro de 2017, às 17:01, Nathaniel J. Smith [email protected]
escrevi:

4880 https://github.com/numpy/numpy/pull/4880 é o que eu estava pensando

do.

Obrigado, observe que o exemplo do modelo de estatísticas mostra bem o meu ponto de vista sobre o
padrão de atol - qualquer coisa diferente de 0,0 é perigoso.

O fato de isso acontecer a cada dois anos indica que devemos
finalmente fazer algo sobre isso?

Talvez sim. É uma pequena verruga, mas às vezes esses pequenos incômodos adicionam
com o tempo.

E embora eu esteja grato que assert_allclose tenha o padrão atol zero
(apoiando meu ponto de que é uma opção aceitável, mesmo se você não
concordar que é a melhor opção) muitos de nós - e mais o tempo todo - não somos
usando unittest e, portanto, pode usar np.all_close diretamente em testes.

E sim Ralf, se fizermos uma mudança, vamos querer mudar todos os três
funções intimamente relacionadas :-(. Mas isso nos dá a chance de torná-los
mais consistente, um bônus?

Embora a transição py2-3 não tenha corrido muito bem, há algo para ser
dito por ter uma versão “está tudo bem em limpar as verrugas” em algum ponto :-)

Há outra opção, para realmente mover is_close para a versão matemática (de fato melhor) de atol=0 : mude o padrão para None e tente com atol=0 ; se todos True apenas retornar; se algum False , tente novamente com o padrão atol e se os resultados mudarem, emita um aviso de depreciação (e retorne o resultado "antigo").

Infelizmente, não se pode fazer simetria e atol ao mesmo tempo, pois eles vão em direções diferentes (ou seja, seria sempre necessário executar os dois casos, o que parece um pouco demais). Mas eu não gosto muito mais do padrão atol que da falta de simetria ...

Não sou a favor de alterar nenhuma função, a menos que haja uma função que
você pode usar aquele comportamento antigo. Além disso, deve haver um
ferramenta de refatoração automatizada para fazer essa alteração, se feito. No entanto, isso não é
mesmo na mesa agora.

A transição py3k não é algo que queremos ter novamente e
não é um modelo a seguir. Agregar limpezas em um grande lançamento não é uma boa
abordagem IMO.

Embora a transição py2-3 não tenha corrido muito bem, há algo a ser dito sobre ter uma versão “está tudo bem em limpar as verrugas” em algum ponto :-)

Só para abordar esta idéia geral de que os números de versão especial pode fazer quebra alterações OK: a regra geral é que é ok para limpar as verrugas, se houver um plano de transição claro que evita níveis inaceitáveis de dor e os benefícios são suficientes para justificar a custos. O ponto-chave aqui é que o julgamento deve ser em termos do efeito sobre os usuários, não sobre se "seguimos as regras". (Se você quiser ser sofisticado, somos consequencialistas , não deontologistas .) Portanto, a única razão para declarar uma versão específica como "essa é a que quebra as coisas" é se torna as coisas substancialmente mais fáceis de manusear para os usuários. IMO, os benefícios de agrupar as alterações de interrupção são geralmente menores, se é que existem - código quebrado é código quebrado - e mesmo se existirem, é muito raro que eles desviem a análise de custo / benefício de "não" para " sim".

atol é diferente de zero ser o padrão - eu realmente acho que este é o maior problema (particularmente com o algoritmo atual de adição de ambos), pois pode facilmente levar à aprovação de testes que não deveriam - e muitas vezes estamos um pouco preguiçoso - escrevemos o teste com a tolerância padrão e, se for aprovado, achamos que terminou. (quando percebi que era assim que funcionava, voltei ao código y, e ENCONTREI alguns deles - opa!

Observe que o exemplo do modelo de estatísticas mostra bem o meu ponto sobre o padrão de atol - qualquer coisa diferente de 0,0 é perigosa. [...] muitos de nós - e cada vez mais - não estamos usando unittest e, portanto, podemos usar np.all_close diretamente em testes.

"Devemos quebrar o código do usuário para aumentar a consistência com algum outro pacote" não tem uma pontuação muito boa na escala de custo / benefício. "Existem inconsistências dentro do numpy que não são apenas confusas, mas confusas de uma forma que leva diretamente a bugs silenciosos no código do usuário, e podemos consertar isso" é muito mais atraente. Eu ainda não formei uma opinião, mas se você quiser progredir aqui, então é isso que eu estaria pressionando.

Uma nota:

“” ”Eu coloquei relativamente pouco pensamento na escolha de um rtol ou atol,
apenas raramente adicionando um atol
-recorte-

$ call assert_allclose 13159, 1178 e 4243 vezes, respectivamente.

-recorte-

Não encontrei novos bugs ou falhas de teste ;

“” ”

Boas notícias, embora eu note que assert_allclose tem o padrão de atol zero. E
isso é por que :-)

@njsmith escreveu:

"Devemos quebrar o código do usuário para aumentar a consistência com algum outro pacote"

Não acho que ninguém neste tópico esteja defendendo isso - tenho certeza que não. A única consistência que alguém está defendendo é a consistência entre as funções numpy relacionadas.

"Existem inconsistências dentro do numpy que não são apenas confusas, mas confusas de uma forma que leva diretamente a bugs silenciosos no código do usuário, e podemos consertar isso"

ISSO é o que eu, pelo menos estou defendendo. E acho que a maioria dos outros.

O problema é que não podemos consertá-lo sem:

Quebrando a compatibilidade com versões anteriores, o que eu não acho que ninguém pense que isso seja sério o suficiente para fazer - mesmo com um ciclo de depreciação.

Ou

Criar um novo sinalizador ou função.

Acho que uma nova função seria uma maneira mais limpa de fazer isso. O código antigo pode permanecer inalterado pelo tempo que desejar, o novo código pode usar as novas funções e uma pesquisa e substituição (ou algumas importações como chamadas) podem facilitar a troca de um arquivo por vez.

(Suponho que você poderia até mesmo fazer um monkey patch numpy em seu código de teste ...)

Nós já temos:

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

Não acho que adicionar mais opções a esta lista vá realmente fazer muita diferença para usuários reais.

Bem, eu gastei muito tempo pensando e debatendo sobre math.isclose, e começamos examinando a implementação entorpecida, entre outras. então, sim, acho que essa abordagem é melhor.

Eu também, e fui um dos principais mantenedores de numpy.testing - este não é um problema novo. Insistir com letras maiúsculas que você está certo não significa que seja verdade.

Talvez você queira dizer que ter a função antiga e a nova, melhor, em numpy NÃO é melhor do que simplesmente deixar a função antiga (talvez melhor documentada) lá. Claro, esse é um ponto totalmente válido, mas eu estava tentando ter essa discussão,

Na verdade, deve ficar claro que é isso que eu quis dizer.

"A introdução de novas funções com comportamento apenas ligeiramente diferente de outras funções que funcionaram dessa forma por uma década é, em geral, uma ideia muito pobre" pareceu encerrar a discussão

Não, ele aponta um problema real com a adição de novas funções que muitas vezes são ignoradas ou não recebem peso suficiente. Neste caso, parece muito claro para muitos desenvolvedores principais - parece que você está entendendo que este navio partiu de @njsmith , @pv , @charris e eu.

Peço desculpas antecipadamente por refazer isso, mas assert_allclose é o comportamento correto (ou pelo menos perto disso). No entanto, isclose pode causar problemas às pessoas porque assume uma tolerância absoluta, como outros notaram. Estou pensando em enviar um PR para colocar uma grande caixa vermelha na página de documentação np.isclose para alertar as pessoas sobre esse comportamento. Como isso soa?

Peço desculpas antecipadamente por refazer isso, mas assert_allclose é o
comportamento correto (ou pelo menos próximo o suficiente disso). No entanto, isclose pode obter
pessoas em apuros porque pressupõe uma tolerância absoluta como os outros
notado. Estou pensando em enviar um PR para colocar uma grande caixa vermelha no
np.isclose página de documentação para alertar as pessoas sobre este comportamento. Como vai
Aquele som?

+1

Há consenso de que os docs devem ser melhores.

Obrigado,

-CHB

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/numpy/numpy/issues/10161#issuecomment-351182296 ou mudo
o segmento
https://github.com/notifications/unsubscribe-auth/AA38YDw4sdhRWYmeeyr4met1RCwWsjMQks5s_uBmgaJpZM4Q2J-P
.

Apoio a melhoria da documentação, mas acho que a alteração proposta não é tão útil quanto poderia ser.

.. aviso :: O padrão atol não é apropriado para números que
são muito menos do que um.

Em primeiro lugar, este aviso nem mesmo é verdadeiro, em geral. O padrão atol _é_ apropriado quando você deseja tratar números menores que 1e-8 como iguais a zero. Isso não é possível com uma tolerância relativa, por isso existe uma tolerância absoluta. Por outro lado, o padrão atol _não_ é apropriado quando você deseja tratar números menores que 1e-8 como significativos. Não vamos supor que um tamanho serve para todos.

Portanto, @xoviat , com o mais profundo respeito, me oponho veementemente à sua declaração subjetiva:

... assert_allclose _é_ o comportamento correto (ou pelo menos próximo o suficiente dele).

Acho que o problema com os documentos atuais é que eles descrevem muito bem _o que_ atol é, mas não descreve _por que_ está lá e, portanto, os usuários podem não entender quando alterar o valor padrão .

Proponho algo como:

"Definir um pequeno valor diferente de zero de atol permite tratar números que estão muito próximos de zero como sendo efetivamente iguais a zero. Portanto, o valor padrão de atol pode não ser apropriado para seu uso caso. Escolha atol modo que números maiores que atol sejam considerados por você como significativos (distintos de zero) e números menores que atol sejam considerados por você como insignificantes (igual a zero). "

Por último, eu adicionaria a mesma nota aos docstrings de isclose e allclose .

Esta é minha sugestão. É pegar ou largar.
Felicidades.

Por favor, deixe comentários sobre o PR. Em particular, sua proposta é muito prolixa IMO.

Me desculpe se eu estava sendo muito chato - eu realmente não achei que houvesse um consenso ainda.

@xoviat : obrigado por trabalhar nos documentos.

Já que isso vai aparecer novamente, eu irei (provavelmente) encerrar esta discussão com uma mini versão de uma NEP projetada para ser rejeitada - resumir as propostas e os motivos da rejeição. Só para constar - já que as edições anteriores simplesmente se esgotaram.

Desculpe por trazer à tona a questão py2 / 3 - se / como limpar verrugas em numpy é uma discussão para outro lugar e hora.

O problema com isclose()

Este problema (e outros referenciados) é o resultado de observações de que numpy.isclose() e amigos usam um algoritmo inferior ao ideal, tem um padrão contencioso para atol e as várias funções relacionadas têm padrões diferentes. Em geral, isso resulta em confusão para os usuários e, no pior dos casos, em testes de falso positivo quando os usuários não pensam em definir o atol .

Em particular, a implementação no stdlib: math.isclose() , fornece um algoritmo e padrões diferentes e indiscutivelmente melhores: é um teste simétrico, não mistura rtol e atol, e o atol padrão é 0,0

Há quase um consenso de que a situação não é ideal, mas nenhum consenso de que é ruim o suficiente para fazer algo a respeito.

Opções consideradas:

Alterando os algoritmos e / ou padrões:

Rejeitado universalmente devido a problemas de compatibilidade com versões anteriores, mesmo com um período de depreciação e avisos - essas funções são amplamente utilizadas em testes, portanto, seria um grande aborrecimento no mínimo.

Adicionar um parâmetro extra com um sinalizador para selecionar um algoritmo diferente

Isso não quebraria nenhum código existente, mas persistiria para sempre com uma API feia e confusa.

Adicionando uma diretiva de tipo __future__

TLDR: é possível, mas não é fácil nem limpo. Ninguém parecia querer prosseguir com isso.

Criando mais uma função

Esta parecia ser a única opção que ganhou força, mas não foi apoiada pelos desenvolvedores principais que participaram da discussão.

A maneira mais limpa de "consertar" isso seria adicionar np.rel_close() [ou outro nome] com um novo algoritmo e padrões, provavelmente aqueles usados ​​em math.isclose . A nova função seria documentada como a "recomendada" para uso em código futuro. Seria possível adicionar avisos de depreciação ao antigo no futuro - mas ninguém parecia pensar que o ruído valeria a pena neste momento.

Uma ferramenta de refatoração poderia ser construída para fazer a substituição - mas quem fará isso para este uso?

Isso resultaria em provavelmente duas funções muito semelhantes no futuro previsível, e "A introdução de novas funções com comportamento apenas ligeiramente diferente de outras funções que funcionaram dessa forma por uma década é, em geral, uma ideia muito pobre."

Conclusão:

Não vale a pena pelo pequeno ganho, mas documentos melhores estão em ordem, e isso é feito aqui: # 10214

Ainda tenho uma pergunta:

@njsmith escreveu:

"" "
Nós já temos:

allclose
assert_allclose
assert_almost_equal
assert_approx_equal
assert_array_almost_equal
assert_array_almost_equal_nulp
assert_array_max_ulp

Não acho que adicionar mais opções a esta lista vá realmente fazer muita diferença para usuários reais.
"" "

Quer dizer que adicionar um novo seria uma boa ideia?

Eu também observaria:

a maioria deles são afirmações - e a proliferação de afirmações é um efeito colateral da arquitetura de teste unitário.

Conforme muitos de nós mudamos para outras arquiteturas de teste (por exemplo, pytest), a necessidade de asserts vai embora.

Portanto, o que fazer com numpy.testing é uma questão separada do que fazer com o núcleo entorpecido.

Conforme muitos de nós mudamos para outras arquiteturas de teste (por exemplo, pytest), a necessidade de asserts vai embora.

Na verdade, isso é um perigo em si. Os testes podem ser alterados de assert_allclose(...) para assert np.allclose(...) e se tornarão silenciosamente menos rígidos, o que é uma coisa ruim.

Eu ainda uso assert_allclose no ambiente pytest porque ele fornece uma mensagem útil de falha:

    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

Pode ser possível consertar isso implementando pytest_assertrepr_compare , mas não tenho certeza de como aplicar isso a chamadas de função e não consigo descobrir onde pytest o invoca.

@ eric-wieser: escreveu:

"Na verdade, isso é um perigo em si. Os testes podem ser alterados de assert_allclose (...) para assert np.allclose (...), e se tornarão silenciosamente menos rígidos, o que é uma coisa ruim."

Exatamente meu ponto - é uma "má ideia" presumir que todo mundo vai usar as afirmações para teste e, portanto, não se preocupar se os padrões de isclose () e allclose () são apropriados para teste - em um ideal mundo que certamente deveriam ser.

Quer dizer que adicionar um novo seria uma boa ideia?

Não, eu quis dizer que, dado que já temos uma coleção de maneiras ligeiramente diferentes de expressar testes quase iguais, infelizmente a maioria dos usuários não vai notar ou entender outra adição.

Eu também observaria:
a maioria deles são afirmações - e a proliferação de afirmações é um efeito colateral da arquitetura de teste unitário.
Conforme muitos de nós mudamos para outras arquiteturas de teste (por exemplo, pytest), a necessidade de asserts vai embora.

Acontece que elas são escritas como declarações, mas AFAICT cada uma dessas funções na verdade codifica uma definição diferente de "quase igual". (Eu acho. Algumas das distinções são tão obscuras que não posso dizer se são reais ou não.)

Alterando os algoritmos e / ou padrões:
Rejeitado universalmente devido a problemas de compatibilidade com versões anteriores, mesmo com um período de depreciação e avisos - essas funções são amplamente utilizadas em testes, portanto, seria um grande aborrecimento no mínimo.

Eu não diria bem assim. Para mim, essa é a única abordagem que potencialmente teria benefícios suficientes para justificar o custo. Não estou dizendo que sim , e não posso falar pelos outros desenvolvedores principais; Eu gostaria de ver alguns dados e, sem dados, errar pelo conservadorismo parece a escolha certa. Mas se alguém produzisse dados, eu pelo menos daria uma olhada :-).

Por exemplo, se alguém aparecesse e dissesse "Eu tentei minha mudança proposta em 3 grandes projetos, e isso levou a 12 falhas extras em 10.000 testes, e desses 12, 8 deles eram bugs silenciosos reais que haviam sido ocultados pelo antigo padrões ruins "... isso seria muito convincente. Especialmente porque este é um caso em que temos a capacidade técnica de fazer avisos direcionados. Estou supondo que os números reais não seriam tão favoráveis, mas até que alguém verifique, quem sabe.

Na segunda-feira, 18 de dezembro de 2017 às 15:57, Nathaniel J. Smith <
notificaçõ[email protected]> escreveu:

Não, eu quis dizer que, dado que já temos todo um zoológico de
maneiras ligeiramente diferentes de expressar testes quase iguais, infelizmente
a maioria dos usuários não notará ou compreenderá outra adição.

Bem, há uma grande diferença - cada um deles foi adicionado porque
algo diferente , e com os caprichos do ponto flutuante, aqueles
diferenças podem ser importantes.

Uma nova função de proximidade relativa faria essencialmente a mesma coisa, mas
faça melhor". E o objetivo seria recomendar que o novo fosse usado
em vez do antigo (e talvez descontinue o antigo eventualmente).

Honestamente, não tenho certeza se isso é um argumento a favor ou contra a ideia,
Apesar.

Por exemplo, se alguém aparecesse e dissesse "Tentei minha mudança proposta em 3 grandes

projetos, e isso levou a 12 falhas extras em 10.000 testes, e desses
12, 8 deles eram insetos silenciosos reais que haviam sido ocultados pelo antigo mau
padrões "... isso seria bastante convincente. Especialmente porque este é um
caso em que temos a capacidade técnica de fazer avisos direcionados.
Acho que os números reais não seriam tão favoráveis, mas até
alguém verifica, quem sabe.

hmm, minha base de código tem cerca de 1.500 testes - não 10.000, mas darei a isso uma
tiro. Se nada mais, posso encontrar um bug ou um teste ruim ou dois. Ou consiga mais
resseguro!

Olá a todos,

Queria falar aqui depois que um colega encontrou esse problema.

_Aviso: eu realmente não dei muita importância à lógica antes de gritar “fogo no corredor” e jogar isso por cima da cerca. Se eu fiz um hash embaraçoso disso, seja gentil, por favor._

Para mim, tudo isso se parece muito com o problema clássico de arquitetura / design “galinha e ovo” - semântica versus implementação. Nem todos os bugs existem dentro do código, às vezes eles existem dentro da semântica. Isso não é muito diferente (se é que é) de um algoritmo que está sendo implementado sem falhas apenas para descobrir que o algoritmo tem falhas - o bug não está no código, está no algoritmo, mas de qualquer forma ainda é um bug. Basicamente, essa discussão soa um pouco como a piada clássica “não é um bug, é um recurso”. Claro, uma “escolha” é apenas isso, mas IMHO isso normalmente sinaliza o fim do raciocínio - se a semântica for escolhida como está, conforme implementada, então que seja, a implementação é legal, fim da discussão.

Então, quais são as semânticas "desejadas" e / ou "esperadas" de isclose() . Pensando nisso, estou inexoravelmente convergindo na semântica que está sendo definida pelo usuário, ou seja, o usuário precisa ser capaz de definir a semântica definição de “fechar”.

Padrões

Em relação aos padrões, isso não quebra nenhuma semântica. No entanto, uma má escolha _é_ perigosa. Se o padrão diferente de zero foi _apenas_ escolhido para fornecer um comportamento razoável para quando abs(a - b) == 0 então isso definitivamente soa como o soln incorreto. Este estado particular seria melhor se tivesse uma caixa especial, pois é uma caixa especial semanticamente. Não há "espaço de manobra" na definição de "fechar" quando o diff é zero, é a pedra angular autodefinida, ou seja, eles não são _close_, são _exatos_ - onde "fechar" é um desvio relativo do exato . (_Nota: _A condicional de revestimento especial pode (ou não) afetar o desempenho.)

+ vs max

A mistura ou “sombreamento” de rel_tol e a_tol devido à implementação usando uma soma e não max _does_ quebra a semântica da função acima. A definição do usuário de “fechar” é limitada de uma maneira não trivial, pois destrói e, portanto, quebra a semântica de “relativo” e “absoluto”. Considerando apenas a semântica acima, este aspecto _é_ um bug. Acho @gasparka O exemplo de abertura é um argumento irrefutável deste ponto.

Comutatividade (ou seja, simetria)

Existem realmente apenas dois operadores aqui: - em a-b e <= em |a-b| <= f(a, b, atol, rtol) . Embora a subtração seja anticomutativa, |a - b| não é, é comutativa, por definição. No entanto, isso por si só pode não ser suficiente para indicar a comutativa de f(a, b, atol, rtol) . No entanto, há um argumento forte e fraco aqui.

  • O fraco sendo o mais simples - encapsulamento de separação de funções ou falta de - enquanto aritemicamente a comutatividade de |a - b| pode não impor a de f(a, b, atol, rtol) , programaticamente, f(a, b, atol, rtol) não está realmente pergunta, mas isClose(a, b, atol, rtol) . Aqui, o cálculo |a - b| é conduzido internamente para a função, isto é, isClose() := |a-b| <= f(a, b, atol, rtol) . Como isso é interno e a e b são passados, |a-b| para ser comutativo isClose() também deve ser. Se não for, a comutatividade de |a-b| perdas significa tudo.
  • Argumento forte: A comparação op não é estrita, ou seja, é <= não < , portanto, para a parte de igualdade da comparação, você deve satisfazer |a-b| = f(a, b, atol, rtol) que implica f(a, b, atol, rtol) _deve_ também ser comutativo (eu acho?). Não fazer isso significa que a igualdade acima é _nunca_ verdadeira (não há valores de a & b que satisfaçam isso) ou que a desigualdade estrita, < , tem na verdade, foi semanticamente definido, certo?

O que você faz (ou não faz) sobre tudo ou nada disso é uma questão completamente distinta.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

navytux picture navytux  ·  4Comentários

Kreol64 picture Kreol64  ·  3Comentários

MorBilly picture MorBilly  ·  4Comentários

astrofrog picture astrofrog  ·  4Comentários

Levstyle picture Levstyle  ·  3Comentários