-
Notifications
You must be signed in to change notification settings - Fork 63
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
Add more exhaustive mutating op tests to ring conformance test #1816
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #1816 +/- ##
==========================================
+ Coverage 88.03% 88.12% +0.09%
==========================================
Files 119 119
Lines 29982 29982
==========================================
+ Hits 26396 26423 +27
+ Misses 3586 3559 -27 ☔ View full report in Codecov by Sentry. |
test/Rings-conformance-tests.jl
Outdated
@@ -21,6 +21,113 @@ function equality(a::T, b::T) where T <: AbstractAlgebra.NCRingElement | |||
end | |||
end | |||
|
|||
function test_mutating_op_like_zero(f::Function, f!::Function, a) |
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.
Function names are fine by me.
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.
Thank you, I like it!
test/Rings-conformance-tests.jl
Outdated
a = deepcopy(A) | ||
b = deepcopy(B) | ||
|
||
a = f!(a, b, a) |
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.
In practice this won't always work; think of divexact!(::QQMatrix, ::QQMatrix, ::ZZRingElem)
I am also not sure how useful it is in practice.
Here is my impression (I might be missing something, though): if b
and a
have the same type, this swapped test is redundant; while if tf they have different types, they might be harmful; but if the caller truly wants to test both orders (e.g. both mul!(::ZZRingElem, ::ZZRingElem, ::Integer)
and mul!(::ZZRingElem, ::Integer, ::ZZRingElem)
they can just call this function twice?
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.
it is not redundant, as it tests a different combination of aliasing between the arguments of f!
I'd rather add another function for *_like_divexact
What remains here is to decide which functions get tested in which interface test. To be sorted are
I would say |
Sounds about right to me. Except perhaps for |
e945836
to
6cae6fa
Compare
test/Rings-conformance-tests.jl
Outdated
if typeof(A) == typeof(B) | ||
a = deepcopy(A) | ||
b = deepcopy(B) | ||
a = f!(a, b, a) | ||
@test equality(a, f(B, A)) | ||
@test b == B | ||
end |
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.
if typeof(A) == typeof(B) | |
a = deepcopy(A) | |
b = deepcopy(B) | |
a = f!(a, b, a) | |
@test equality(a, f(B, A)) | |
@test b == B | |
end | |
if typeof(A) == typeof(B) | |
a = deepcopy(A) | |
b = deepcopy(B) | |
a = f!(a, b, a) | |
@test equality(a, f(B, A)) | |
@test b == B | |
end |
I don't think this is OK, just because the types are equal doesn't mean you can change the order. Think of divexact(4,2)
vs. divexact(2,4)
.
Instead I would suggest to check f!(b, a, b)
. And if you do that, then perhaps there is no need for a separate test_mutating_op_like_divexact
after all?
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.
Thinking some more along this vein, perhaps the arguments to test_mutating_op_like_add
(and test_mutating_op_like_divexact
if we retain it) should include just two values a, b
. The third value really is only needed for storage purposes and presumably we can get that via deepcopy? (although, I am also worrying about relying on deepcopy
too much here, but that's a separate concern... anyway zero(parent(a))
might be a better source, dunno)
Next, I was wondering whether we should arrange things so that e.g. b
is always the last argument, by using these input combos:
- zero(parent(a)), a, b
- a, a, b
- b, a, b (only if a, b have same parent)
- a, b, b (only if a, b have same parent)
- b, b, b (only if a, b have same parent)
- a, b
- b, b (only if a, b have same parent)
The idea being that this way the "divisor" always stays on the right side. And we are testing the parent of a
(so if b
has a different parent` we don't mix).
But then we loose the ability to test e.g. add!(::ZZRingElem, ::Integer, ::ZZRingElem)
by passing an Integer
for a
and a ZZRingElem
for b
, because we implicitly assume the ring being tested is parent(a)
. We could recover this if we passed the "target ring" R
(= the parent of a
), and then used these input combos (one may wish to also add a check that least one of a
and b
has R
as its parent):
- zero(R), a, b
- a, b
- a, a, b (only if R == parent(a))
- b, a, b (only if R == parent(b))
- a, b, b (only if R == parent(a) == parent(b))
- b, b, b (only if R == parent(a) == parent(b))
- b, b (only if R == parent(a) == parent(b))
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.
I like this and try to implement all of this now. One little thing: I would not only use zero(R)
as the temporary result place, but something else as well, e.g. because for 0 we cannot distinguish mul!
and addmull!
6cae6fa
to
c30cfd4
Compare
8ffec6b
to
f64601b
Compare
I now mostly implemented you suggestions from above, with the difference that I don't do any parent checks and take no "target ring".
|
f64601b
to
3424889
Compare
I am wondering. Isn't the parent check kind of important? And if we don't do parent checks, are we really testing the ring interface? |
Of course functions like |
Question about And for the time being, I remove |
3f8a786
to
ea55749
Compare
My point was that it makes sense to test that the result of Edit: I am fine to keep it as is, but maybe add a comment that the tests are also for things that don't implement the |
This is not how it currently works. See e.g. the following Nemo example: julia> F1 = cyclotomic_field(4)[1]
Number field with defining polynomial _$^2 + 1
over rational field
julia> F2 = cyclotomic_field(5)[1]
Number field with defining polynomial _$^4 + _$^3 + _$^2 + _$ + 1
over rational field
julia> F3 = cyclotomic_field(7)[1]
Number field with defining polynomial _$^6 + _$^5 + _$^4 + _$^3 + _$^2 + _$ + 1
over rational field
julia> a = gen(F1)
z_4
julia> b = gen(F2)
z_5
julia> c = gen(F3)
z_7
julia> z = add!(a,b,c)
18*z_4 + 207967168
julia> parent(z)
Number field with defining polynomial _$^2 + 1
over rational field
julia> parent(b+c)
ERROR: no common overstructure for the arguments found
Stacktrace:
[1] error(s::String)
@ Base ./error.jl:35
[2] force_op(::Function, ::Val{true}, ::AbsSimpleNumFieldElem, ::Vararg{AbsSimpleNumFieldElem})
@ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/ohL1e/src/AbstractAlgebra.jl:123
[3] force_op(::Function, ::AbsSimpleNumFieldElem, ::AbsSimpleNumFieldElem)
@ AbstractAlgebra ~/.julia/packages/AbstractAlgebra/ohL1e/src/AbstractAlgebra.jl:129
[4] +(a::AbsSimpleNumFieldElem, b::AbsSimpleNumFieldElem)
@ Nemo ~/code/julia/Nemo.jl/src/antic/nf_elem.jl:292
[5] top-level scope
@ REPL[10]:1 So not only doesn't adding something like your suggested test to the interface will require a lot of work in Nemo right now to make the interface tests work there again. |
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.
Fine by me. Maybe add one comment as suggested.
IMHO we should just get this in ASAP and then we can still iterate over it.
E.g. one thing I'd like to see added are tests for the ad-hoc variants of the unsafe ops. Which then raises the question which cases to test; and whether we can make it so that if Nemo
is loaded, also ad-hoc operations involving ZZRingElem
can be tested. (One idea here would be to have a global
const adhoc_ring_parents = [ parent(1), parent(UInt(1)), parent(UInt8(1)) ]
and then Nemo could just do
push!(AbstractAlgebra.adhoc_ring_parents, ZZ)
Anyway, that's an idea for perhaps a future PR, not this one.
end | ||
|
||
function test_mutating_op_like_add(f::Function, f!::Function, A, B) | ||
for z in [zero(A), deepcopy(A), deepcopy(B)] |
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.
Maybe add a comment why this loops is here? Same in other similar places (via copy&paste)
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.
I'll do that after lunch
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.
Done
Yes, if you put garbage into the |
I already bumped the version, so that we can tag a release immediately after merging this, so we can test Nemocas/Nemo.jl#1869 with this available. |
Resolves #1814.
I open to suggestions for the function names. What I already iterated through:
test_mutating_op_3_2
for theadd!
test as that may take 3 or 2 arguments. But this was confusing me very fast, and doesn't seems to be extensible toaddmul!
in a straightforward way.cc @fingolfin
open TODOs:
test_*_interface
function