-
Notifications
You must be signed in to change notification settings - Fork 134
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
Improve conversion between OSCAR and GAP prime fields #3611
Conversation
src/GAP/iso_oscar_gap.jl
Outdated
f(x) = GAP.Obj(lift(x))*e | ||
f = function(x) | ||
y = GAP.Obj(_ffe_to_int(x))::GapInt | ||
return (y*e)::GAP.FFE |
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 FO
is Nemo.FpField(p)
with large enough p
then e
and y*e
will not have the type GAP.FFE
.
(They will be in the GAP filter IsFFE
but not in ÌsInternalRep
.)
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 for paying close attention; yes that wasn't meant to be here at all, I only had it in there for an experiment (to show to @fieker that adding a type insertion there causes an extra allocation that I don't understand) and then accidentally committed it :-(.
Removed now.
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 prescribing GAP.FFE
will in general not work.
(Some test for this situation seems to be missing ...)
fe9ccfa
to
3e473b2
Compare
I'll not touch the E.g. z = GAP.Globals.PrimitiveRoot(FG)
finv_neu = function(x::GAP.FFE) # TODO improve
return gen(FO)^GAP.Globals.LogFFE(x, z)
end Conversely, if the input is an @fieker suggested among other things that we could change finv = function(x::GAP.Obj)
v = GAPWrap.Coefficients(basis_FG, x)
v_int = [ZZRingElem(GAPWrap.IntFFE(v[i])) for i = 1:d]
return sum([v_int[i]*basis_F[i] for i = 1:d])
end by replacing the return by function (a::FqField)(b::Vector{<:IntegerUnion})
da = degree(a)
db = length(b)
da == db || error("Coercion impossible")
return a(parent(defining_polynomial(a))(b))
end (I also experimented with changing from |
I have not checked, but if we already have efficient conversions between GAP and the "old" finite fields (aka |
On Tue, Apr 16, 2024 at 12:13:53AM -0700, Tommy Hofmann wrote:
I have not checked, but if we already have efficient conversions between GAP and the "old" finite fields (aka `fpField`, `FpField`, `fqPolyRepField`, `FqPolyRepField`), then it is relatively easy to reuse this. I am happy to help, if you want to go down this road.
Part of the point is, we never had
- efficient conversion for elements
- even less for matrices
ctrl-c showed that GF(3, 4) conversion would create a GAPInt
Coeff-Vector, then convert it to a Vector{ZZRingElem}, then do a
sum(c[i]*b[i] ...) to compute the lin comb.
Quite likely, K(Int[]) would have done the job...
… --
Reply to this email directly or view it on GitHub:
#3611 (comment)
You are receiving this because you were mentioned.
Message ID: ***@***.***>
|
@fieker but note that |
src/GAP/iso_oscar_gap.jl
Outdated
function _iso_oscar_gap_field_finite_functions(FO::Union{Nemo.fpField, Nemo.FpField}, FG::GAP.GapObj) | ||
_ffe_to_int(a::FqFieldElem) = Nemo._coeff(a, 0) | ||
_ffe_to_int(a::FqPolyRepFieldElem) = coeff(a, 0) | ||
_ffe_to_int(a::fqPolyRepFieldElem) = ZZ(coeff(a, 0)) |
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.
@ThomasBreuer I assume you added the conversion to ZZRingElem
because we otherwise get a UInt
.
We should have a better function to "convert a Julia or Oscar integer to GAP integer" which does the right thing for UInt (i.e. convert it to an Int
it if fits and otherwise to a GAP larger integer). A natural way to express that would be via GapInt(x)
methods
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.
Yes, the other coeff
and _coeff
methods return ZZRingElem
, and here we get a UInt
. This is not really a problem if we are sure that anyhow GAP.Obj
will be called for the result. Thus perhaps we do not need _ffe_to_int
but _ffe_to_gapint
?
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.
Yeah, and we can use julia_to_gap
to get "efficient" conversion for UInt, too (as long as they are less than UInt
it still first converts to a BigInt. I could optimize that, too, but that's for GAP.jl
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.
Yes, julia_to_gap(ZZ(2))
is faster than GAP.Obj(ZZ(2))
. (Is this part of the problem or part of the solution?)
(The code for GAP.Obj
looks worse than it is. It calls julia_to_gap
with an often unnecessary IdDict
object, but it looks as if this gets optimized away in calls such as GAP.Obj(ZZ(2))
. Or I am misunderstanding what is going on.)
creating a poly is bad, but has no arithmetic costs. Its one stupid allocation, but not a single computation |
The polynomials are more than one allocation because the coefficients are first converted from
This calls the following method: function (a::FqField)(b::Vector{<:IntegerUnion})
da = degree(a)
db = length(b)
da == db || error("Coercion impossible")
return a(parent(defining_polynomial(a))(b))
end Let's create that polynomial then:
Well, what is the code here? This: function (R::FqPolyRing)(x::Vector{T}) where {T <: Integer}
length(x) == 0 && return zero(R)
return R(map(ZZRingElem, x))
end That said, I still think we should use the But there is more: if the Nemo field internally really uses Zech logarithms, it would be great to know so that we can make use of this. Same for other representations. |
Before: julia> hom = Oscar._iso_oscar_gap(GF(2)); julia> e = one(domain(hom)); @Btime hom(e); 2.222 μs (21 allocations: 736 bytes) julia> e = one(codomain(hom)); @Btime preimage(hom, e); 1.733 μs (17 allocations: 624 bytes) After: julia> hom = Oscar._iso_oscar_gap(GF(2)); julia> e = one(domain(hom)); @Btime hom(e); 154.096 ns (3 allocations: 48 bytes) julia> e = one(codomain(hom)); @Btime preimage(hom, e); 232.591 ns (2 allocations: 96 bytes)
b2d6cc5
to
4c990fd
Compare
e = GAPWrap.One(FG) | ||
|
||
f(x) = GAP.Obj(lift(x))*e | ||
f = function(x) | ||
y = GAP.julia_to_gap(_ffe_to_int(x))::GapInt |
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.
@ThomasBreuer For now I removed the ZZ(...)
from the one _ffe_to_int
method and just call GAP.julia_to_gap
here, that should work fine for now. On the long run my plan is to use GAP.GapInt
instead, see oscar-system/GAP.jl#984
@ThomasBreuer OK to merge for now? Then please approve, we can still improve it later |
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.
Nice, thanks.
Before:
After: