-
Notifications
You must be signed in to change notification settings - Fork 71
First attempt at a Supposition.jl-based test #736
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Thanks for the ping! Some notes - in principle fuzzing with just a pair of Since this is something that comes up from time to time, I'll probably have to think about adding something that triggers these known edge cases for integers earlier 🤔 Another idea might be to add a constructor with Another thought - is there maybe some transform you can do to create a valid Interval from an illformed one? I'm not familiar with the domain so I'm not sure that this is applicable, it's just generally a good strategy to Other than that, this looks like an example of a "Different Paths, Same Destination"-style property test, where you test that two different constructors behave the same for inputs that they should be invariant under - nice! |
Thank @bvrb for opening this PR. This is very interesting, I was not aware of Supposition.jl. Could this be done for floats as well? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks very much for the efforts! Except for my two small comments, it looks good to me.
I think it is great to have the general layout of those Suppositions.jl test figure out for the future.
If you'd be willing to add them, I do agree with Olivier that it would be nice to have the same consistency tests for floating point number :)
More advance tests can wait, and we'll need to be somewhat smart to have meaningful ones.
assume!(!any(decoration.((x,y)) .== ill)) # Exclude ill-formed intervals | ||
isequal_interval(x, y) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These two last lines could be replaced by
x === y
which correctly handles ill formed intervals.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Changed it accordingly.
@testset "Rational tests" begin | ||
# Define number generators | ||
#intgen = Data.Integers(typemin(Int)+1,typemax(Int)) # Don't allow den==typemin(Int) to avoid overflow | ||
intgen = Data.Integers(typemin(Int8)+Int8(1),typemax(Int8)) # Don't allow den==typemin(Int8) to avoid overflow |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this still let the tests run with any integer type, or only Int8?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Data.Integers
currently enforces that either you specify the type explicitly like Data.Integers{Int64}
, or the types of the two arguments have to match & are what is produced in the end. So as written, this will only produce Int8
.
There is a Data.BitIntegers
which produces any integer type (except BigInt
), but that one doesn't have a lowerbound/upperbound based constructor yet.. The semantics are a bit weird there, because I need to decide what to do if a generated value doesn't fit into the chosen output type...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Per the discussion above, I now changed this to a regular Int
, which is probably the most commonly used type to construct a Rational
anyway. This will now however not always trigger the 1//0
and -1//0
edge cases because the input space is much larger, so I instead added a few tests in the construction.jl
file, which essentially mirror the already existing tests for Inf
/-Inf
. Is this ok?
I tried playing around with Data.BitIntegers
, but then I also ran into problems when numerator and denominator were of different Signed
or Unsigned
types when creating the Rational
, and this felt more like a separate issue not necessarily to be tackled here.
- decrease number of random samples - add test for floats - switch from Int8 to Int - change equality check
Thank you all for the helpful comments and feedback! @Seelengrab This makes complete sense, and I tried to incorporate most of it. So the way I understand it, the core role of Supposition.jl is to randomly sample a large input space, where edge cases like I do believe it would be useful to have some sort of cherry picking with regards to the common edge cases, but I imagine it is rather difficult to decide what is "common" and what isn't. Although in the |
@OlivierHnt Thanks! Indeed, Supposition.jl generates random inputs for testing to cover more of the input space, and actually creates several random values at once every time CI is run - as per @Seelengrab's suggestion I have decreased the I think we initially decided to go with a test for |
Started during the Hackathon at JuliaCon Paris under the guidance of @Kolaru, here is my first attempt at adding a test using Supposition.jl (c.p. #733).
To start simple, the goal was to convert an existing test with a hardcoded numerical value to one using randomly generated values from Supposition.jl instead.
For no particular reason, we picked
IntervalArithmetic.jl/test/interval_tests/numeric.jl
Line 175 in 544c000
which tests that for a degenerate interval with equal start and endpoints of type
Rational
, both the one and two argument syntax yield the same interval.There were a few unforeseen complications, which I tried my best to find a solution for:
Int
s, and the case0//0
is explicitly thrown out as an invalid test case.1//0
and-1//0
(correspondingInf
/-Inf
), which will yield an ill-formed interval (see also isempty_interval return false on empty ill intervals #734). However, similar to the discussion in How to automatically find counterexample Seelengrab/Supposition.jl#62 (comment), it is unlikely that a0
is generated in the denominator for anInt64
, so I had to use anInt8
generator instead. As an alternative, one might instead add these cases explicitly to the regular tests.OverflowError
would appear, especially when usingInt8
. The issue can be reproduced bySo I believe that upon construction of a Rational resulting in an overall negative value, a minus sign in the denominator is transferred to the numerator instead, and the sign of the former is flipped. However, if that value happens to be
typemin(Int8)
, there will be an overflow, sincetypemax(Int8)==127
. To address this, I instead added1
to the minimum value of theInt
generator. Does that seem reasonable?1//0
and-1//0
& Ill-formed intervalsNaN
, and any Boolean comparison will yieldfalse
.ill
.Also, it might make sense to fix the random seed for reproducibility reasons, but I am not sure if this would go against the spirit of this fuzzing-based approach, which I am not too familiar with.
I apologize for the wall of text for this (at least in theory) rather simple addition, and would appreciate any feedback!