-
Notifications
You must be signed in to change notification settings - Fork 4
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
Don't recompute congruence substitutions #240
base: master
Are you sure you want to change the base?
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #240 +/- ##
=======================================
Coverage 95.81% 95.82%
=======================================
Files 12 12
Lines 836 838 +2
=======================================
+ Hits 801 803 +2
Misses 35 35 ☔ View full report in Codecov by Sentry. |
Which Julia and OSCAR versions do you use? Did you run using GenericCharacterTables
function scalar_test(g)
for c1 in g
for c2 in g
scalar_product(c1, c2)
end
end
end
g=generic_character_table("SL3.1")
@time scalar_test(g)
@time scalar_test(g) With master:
With your PR:
So this is indeed a little bit slower and allocates a bit more, but overall much less than what you have. |
Oh, no... I still had Oscar Current master:
With the PR:
|
else | ||
q = gen(R, 1) | ||
substitute = congruence[2] * q + congruence[1] | ||
substitute_inv = (q - congruence[1]) * 1//congruence[2] |
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 discussed orally, computing this here in advance may be too costly. I would recommend changing the ring to a mutable struct
(most of our ring types are anyway, to allow them to store attributes), and then leave the two fields undefined in the constructor.
Then add an accessor function (or two, depending on taste) to get their content, with suitable isdefined(R, :substitute)
checks
else | ||
q = gen(R, 1) | ||
substitute = congruence[2] * q + congruence[1] | ||
substitute_inv = (q - congruence[1]) * 1//congruence[2] |
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.
Also, why isn't this
substitute_inv = (q - congruence[1]) * 1//congruence[2] | |
substitute_inv = (q - congruence[1]) / congruence[2] |
numerator(g), [1], [substitute] | ||
)//evaluate(denominator(g), [1], [substitute]) | ||
numerator(g), [1], [R.substitute] | ||
)//evaluate(denominator(g), [1], [R.substitute]) |
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 the fraction was reduced (i.e. numerator and denomiator are coprime) before the substitution, then it still is after, correct? But the //
method can't know that and thus may end up trying to reduce? (I don't know if it actually does that, I just wonder).
In any case, you next need numerator and denominator anyway. So perhaps something like this could be used in this loop instead:
den_g = denominator(g)
num_g = numerator(g)
if R.congruence === nothing
den_g = evaluate(den_g, [1], [substitute])
num_g = evaluate(num_g, [1], [substitute])
end
a, r = divrem(num_g, den_g)
push!(L, (c, den_g, r, a))
This would arguably also make the code a little bit nicer?
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 the fraction was reduced (i.e. numerator and denomiator are coprime) before the substitution, then it still is after, correct?
No, not necessarily. This is kind of the whole point of this substitution. Take for example the fraction (q+1)//2
. If q
is now congruent to 1
modulo 2
then R.substitute
is equal to 2*q+1
. After evaluation the fraction is (2*q+2)//2
which is not reduced anymore. The idea is then that the fraction gets reduced to q+1
which has a normal form of 0
. This detects that exp(2*pi*i*(q+1)//2)
is equal to 1
for all q
congruent to 1
modulo 2
.
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 of course, gotcha.
So please add a comment explaining just that? :-)
substitute = R.congruence[2] * q + R.congruence[1] | ||
substitute_inv = (q - R.congruence[1]) * 1//R.congruence[2] | ||
end | ||
|
||
# reduce numerators modulo denominators | ||
L = NTuple{4,UPoly}[] | ||
for (g, c) in f |
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.
Style / taste question, but: I like to avoid nested control flow, esp. multi-level, to improve code clarity. So here I'd do
for (g, c) in f | |
for (g, c) in f | |
is_zero(c) && continue |
and then drop the if !iszero(c)
and thereby reduce the indention level inside the loop
As discussed earlier we shouldn't recompute the substitutions used in the simplification of
GenericCyclo
. Apparently I've made it slower and increased the allocations when computing scalar products of tables with congruence as in #238 .Before the patch with
SL3.1
:25.557065 seconds (98.68 M allocations: 8.370 GiB, 11.97% gc time)
And after the patch with
SL3.1
:36.824178 seconds (111.61 M allocations: 10.982 GiB, 9.44% gc time)
@fingolfin Do you see the problem here?