-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Fix and test lcm([1//2, 1//2]) == 1//1 #56423
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -191,6 +191,13 @@ end | |||||||||||
|
||||||||||||
@test lcm(T[2, 4, 6]) ⟷ T(12) | ||||||||||||
end | ||||||||||||
|
||||||||||||
# Issue #55379 | ||||||||||||
@test lcm([1//2; 1//2]) === lcm([1//2, 1//2]) === lcm(1//2, 1//2) === 1//2 | ||||||||||||
@test gcd(Int[]) === 0 | ||||||||||||
@test lcm(Int[]) === 1 | ||||||||||||
@test gcd(Rational{Int}[]) === 0//1 | ||||||||||||
Comment on lines
+197
to
+199
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These three tests are unnecessarily strict.
Suggested change
Relaxing them should help avoid spurious test failures in the future. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If someone changes the return type of I'm adding them because I noticed that #56113 would break all three of these new tests but doesn't break any existing tests; I want to make sure that behavior change is only made if folks are aware that we are changing existing behavior. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changing the return type isn't breaking, as long as the value is correct and the type subtypes the correct abstract type. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Making a mathematical operation on Changing |
||||||||||||
@test lcm(Rational{Int}[]) === 1//1 | ||||||||||||
end | ||||||||||||
|
||||||||||||
⟷(a::Tuple{T, T, T}, b::Tuple{T, T, T}) where T <: Union{Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128} = a === 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.
IMO, this is wrong. Rational numbers are a field, and as such to not have an LCM
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.
xref #56166
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 think it is perfectly sensible to define lcm on rationals, provided we choose a definition for "multiple" that is based on integers. A definition that is mathematically sensible and in line with current behavior (except for #55379) is
For a signed ring
R
,m
inR
is an lcm ofa_1, a_2, ... a_n in R
iffm
is positive and is a multiple ofa_1
,a_2
, ... anda_n
and for all otherm'
inR
that are multiples ofa_1
,a_2
, ... anda_n
,m'
is a multiple ofm
.And we define "multiple" as
a
is a multiple ofb
iff there exists an integern
such thatb * n = a
where*
refers to multiplication in the ring of integers, not in the ring of integers mod 2^64.While there is certainly a case to be made that
lcm
should throw on rationals, now is not the time to make that case. That time was #33910.lcm
has supported rationals under the definition above since 1.4.I am not aware of any definitions of
lcm
over the integers or the rationals that would result in returning different answers; though some definitions should throw errors:Using "
m <= m'
" instead of "m'
is a multiple ofm
" produces the same results on rationals and integers.Defining "multiple" based on the existence of a ring element that takes
b
toa
instead of based on an integer that takesb
toa
is equivalent for the ring of integers. For the rationals, every positive rational is thelcm
of every set of rationals which doesn't seem like a particularly useful definition. Folks sometimes (e.g. #27039 (comment), #56166 (comment)) say thatlcm(a,b) == 1
for a field which, if we are defining multiple as "all field elements are multiples of all other field elements", is not wrong but we could just as correctly say thatlcm(a,b) == 17
for all field elementsa
andb
under that definition which means the right answer for a programming language is an error, not the number 1 nor the number 17.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 took a different route to obtain the same answer here #55379 (comment)
with the FTA based definition,
lcm(1//2, 1//2) == 1//2
. in my opinion it is perfectly clean & consistent this way.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.
Ah yes, the FTA definition is nice. AFAICT these two definitions agree in all cases.
For the empty case over the rationals the definition I listed indicates there is no LCM. Using the FTA definition extended to rationals requires computing
max
over an empty set of integers which also does not exist.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.
as counterpoint to my interpretation, it would lead to
gcd([]) = 1
as well, which is normatively worse thangcd([]) = 0
to be honest though, I think all these choices are basically just convention and I don't think it matters that much that there is some underlying purity determining the convention as long as what we end up with makes sense.
I think the current state of this PR makes good choices for each convention, and afaict it looks like the court of public opinion --- which seems to arise from the recursive definitions --- agrees https://math.stackexchange.com/questions/1755266/gcd-of-an-empty-set https://www.reddit.com/r/learnmath/comments/v9vmfm/whats_the_empty_lcm/
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.
@adienes, please take a look at #56166
The only way to end up with something that makes sense is to respect the math ("underlying purity").
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.
respectfully, I don't think "the math" makes any particularly consistent demands about what to do in these edge cases. and any choice made is essentially just convention (which can and does vary among authors)
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.
As described in the linked issue, there indeed are multiple possible choices here. That doesn't imply that anything goes, though.
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 think a more charitable interpretation of my comments (rather than "anything" goes) is "a lot of things could go, and I think the choices made here are good ones"
but anyway I guess let's just leave to to triage. I don't typically attend triage but if the discussion comes up, I would upvote the implementation in this PR.