Skip to content

Commit

Permalink
rewrite julia_to_gap (#1029)
Browse files Browse the repository at this point in the history
  • Loading branch information
ThomasBreuer authored Sep 24, 2024
1 parent e60382d commit 64fb95a
Show file tree
Hide file tree
Showing 25 changed files with 548 additions and 311 deletions.
8 changes: 8 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changes in GAP.jl

- Rewrite `julia_to_gap`, in order to
- make the installation of new conversion methods from Julia to GAP
simpler and safer and
- restrict the necessity to create dictionaries to situations where
recursive conversions make sense.
For that, the function `GAP.GapObj_internal`, the macro `GAP.@install`,
and the type `GapCacheDict` were introduced.

## Version 0.11.4 (released 2024-09-19)

- Support AbstractAlgebra 0.43
Expand Down
5 changes: 3 additions & 2 deletions docs/src/conversion.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ For a few types of objects, such conversions are unavoidable,
see [Automatic GAP-to-Julia and Julia-to-GAP Conversions](@ref).
In all other situations,
the conversions between GAP objects and corresponding Julia objects
can be performed using [`gap_to_julia`](@ref) and [`julia_to_gap`](@ref),
can be performed using [`gap_to_julia`](@ref) and
[`GapObj(x, cache::GapCacheDict = nothing; recursive::Bool = false)`](@ref),
see [Explicit GAP-to-Julia and Julia-to-GAP Conversions](@ref), respectively.

For convenience, also constructor methods are provided,
Expand Down Expand Up @@ -46,7 +47,7 @@ The exceptions are as follows.

```@docs
gap_to_julia
julia_to_gap
GapObj(x, cache::GapCacheDict = nothing; recursive::Bool = false)
```

## Constructor Methods for GAP-to-Julia Conversions
Expand Down
4 changes: 2 additions & 2 deletions docs/src/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ julia> Vector{Vector{Int}}(cf)
Next let us investigate the operation of the group on the 48 points.

```jldoctest rubik
julia> orbs = GAP.Globals.Orbits(cube, GAP.GapObj(1:48))
julia> orbs = GAP.Globals.Orbits(cube, GapObj(1:48))
GAP: [ [ 1, 3, 17, 14, 8, 38, 9, 41, 19, 48, 22, 6, 30, 33, 43, 11, 46, 40, 24, 27, 25, 35, 16, 32 ], [ 2, 5, 12, 7, 36, 10, 47, 4, 28, 45, 34, 13, 29, 44, 20, 42, 26, 21, 37, 15, 31, 18, 23, 39 ] ]
julia> length(orbs)
Expand Down Expand Up @@ -315,7 +315,7 @@ For this purpose we introduce a free group and a homomorphism of it
onto the cube group.

```jldoctest rubik
julia> f = GAP.Globals.FreeGroup(GAP.GapObj(["t", "l", "f", "r", "e", "b"], recursive = true))
julia> f = GAP.Globals.FreeGroup(GapObj(["t", "l", "f", "r", "e", "b"], recursive = true))
GAP: <free group on the generators [ t, l, f, r, e, b ]>
julia> fhom = GAP.Globals.GroupHomomorphismByImages(f, cube)
Expand Down
5 changes: 3 additions & 2 deletions docs/src/other.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ DocTestSetup = :(using GAP)
@gapwrap
@gapattribute
@wrap
@install
```

## Convenience adapters
Expand Down Expand Up @@ -94,10 +95,10 @@ regarding the mutability of the result
but here we have to choose one behaviour for the Julia function.

```jldoctest
julia> l = GAP.julia_to_gap( [ 1, 3, 7, 15 ] )
julia> l = GapObj( [ 1, 3, 7, 15 ] )
GAP: [ 1, 3, 7, 15 ]
julia> m = GAP.julia_to_gap( [ 1 2; 3 4 ] )
julia> m = GapObj( [ 1 2; 3 4 ] )
GAP: [ [ 1, 2 ], [ 3, 4 ] ]
julia> length( l )
Expand Down
4 changes: 2 additions & 2 deletions pkg/JuliaInterface/gap/JuliaInterface.gd
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,8 @@ DeclareAttribute( "JuliaPointer", IsJuliaWrapper );
#! <Julia module GAP>
#! gap> IsJuliaModule( Julia.GAP );
#! true
#! gap> Julia.GAP.julia_to_gap;
#! <Julia: julia_to_gap>
#! gap> Julia.GAP.gap_to_julia;
#! <Julia: gap_to_julia>
#! @EndExampleSession
DeclareCategory( "IsJuliaModule", IsJuliaWrapper and IsRecord );

Expand Down
12 changes: 6 additions & 6 deletions pkg/JuliaInterface/gap/convert.gd
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
#! On the &Julia; side, there is usually no need for a wrapper,
#! as (thanks to the shared garbage collector)
#! most &GAP; objects are valid &Julia; objects of type
#! <C>GAP.GapObj</C>.
#! <C>GapObj</C>.
#! The exception to that rule are immediate &GAP; objects,
#! more on that in the next section.
#! </Item>
Expand Down Expand Up @@ -84,7 +84,7 @@
#! <Ref Chap="Integers" BookName="ref"/> in the &GAP; Reference Manual.
#! Since these are not valid pointers, &Julia; cannot treat them like other
#! &GAP; objects, which are simply &Julia; objects of type
#! <C>GAP.GapObj</C>.
#! <C>GapObj</C>.
#! Instead, a conversion is unavoidable, at least when immediate objects
#! are passed as stand-alone arguments to a function.
#! <P/>
Expand Down Expand Up @@ -135,7 +135,7 @@
#! &Julia; function wrapper to &Julia; function,
#! </Item>
#! <Item>
#! other &GAP; objects to <C>GAP.GapObj</C>.
#! other &GAP; objects to <C>GapObj</C>.
#! </Item>
#! </List>
#!
Expand All @@ -156,7 +156,7 @@
#! &Julia; <C>false</C> to &GAP; <K>false</K>,
#! </Item>
#! <Item>
#! <C>GAP.GapObj</C> to <C>Obj</C>,
#! <C>GapObj</C> to <C>Obj</C>,
#! </Item>
#! <Item>
#! other &Julia; objects to &Julia; object wrapper.
Expand All @@ -169,7 +169,7 @@
#! <Ref Func="GAPToJulia"/> and
#! <Ref Constr="JuliaToGAP" Label="for IsObject, IsObject"/>.
#! In &Julia;, conversion is done via <C>gap_to_julia</C> and
#! <C>julia_to_gap</C>.
#! <C>GapObj</C>.
#!
#! <E>Conversion from &GAP; to &Julia;</E>
#!
Expand Down Expand Up @@ -289,7 +289,7 @@
#! of nested objects.
#! Various methods for this constructor then take care of input validation
#! and the actual conversion, either by delegating to the &Julia; function
#! <C>julia_to_gap</C>
#! <C>GapObj</C>
#! (which takes just one or two arguments and chooses the &GAP; filters of
#! its result depending on the &Julia; type),
#! or by automatic conversion.
Expand Down
12 changes: 6 additions & 6 deletions pkg/JuliaInterface/gap/convert.gi
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
InstallMethod(JuliaToGAP, ["IsInt", "IsJuliaObject"],
function(filter, obj)
if Julia.isa(obj, Julia.Base.Integer) then
return Julia.GAP.julia_to_gap(obj);
return Julia.GAP.GapObj(obj);
fi;
Error("<obj> must be a Julia integer");
end);
Expand All @@ -22,7 +22,7 @@ end);
InstallMethod(JuliaToGAP, ["IsRat", "IsJuliaObject"],
function(filter, obj)
if Julia.isa(obj, Julia.Base.Integer) or Julia.isa(obj, Julia.Base.Rational) then
return Julia.GAP.julia_to_gap(obj);
return Julia.GAP.GapObj(obj);
fi;
Error("<obj> must be a Julia integer or rational");
end);
Expand All @@ -38,7 +38,7 @@ end);
InstallMethod(JuliaToGAP, ["IsFloat", "IsJuliaObject"],
function(filter, obj)
if Julia.isa(obj, Julia.Base.AbstractFloat) then
return Julia.GAP.julia_to_gap(obj);
return Julia.GAP.GapObj(obj);
fi;
Error("<obj> must be a Julia float");
end);
Expand All @@ -54,10 +54,10 @@ end);
InstallMethod(JuliaToGAP, ["IsChar", "IsJuliaObject"],
function(filter, obj)
if Julia.isa(obj, Julia.Base.Char) then
return Julia.GAP.julia_to_gap(obj);
return Julia.GAP.GapObj(obj);
elif Julia.isa(obj, Julia.Base.Int8) or
Julia.isa(obj, Julia.Base.UInt8) then
return CharInt( Julia.GAP.julia_to_gap(obj) );
return CharInt( Julia.GAP.GapObj(obj) );
fi;

Error("<obj> must be a Julia Char or Int8 or UInt8");
Expand Down Expand Up @@ -119,7 +119,7 @@ InstallMethod(JuliaToGAP, ["IsString", "IsJuliaObject"],
function(filter, obj)
if Julia.isa(obj, Julia.Base.AbstractString) or
Julia.isa(obj, Julia.Base.Symbol) then
return Julia.GAP.julia_to_gap(obj);
return Julia.GAP.GapObj(obj);
fi;
Error("<obj> must be a Julia string or symbol");
end);
Expand Down
6 changes: 3 additions & 3 deletions pkg/JuliaInterface/gap/juliahelp.g
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@
#! &Julia; functions belong to &Julia; modules.
#! Many &Julia; functions can be accessed only relative to their modules,
#! and then also the help requests work only for the qualified names.
#! For example, <C>?Julia:GAP.julia_to_gap</C> yields the description
#! of the &Julia; function <C>julia_to_gap</C> that is defined in the
#! For example, <C>?Julia:GAP.wrap_rng</C> yields the description
#! of the &Julia; function <C>wrap_rng</C> that is defined in the
#! &Julia; module <C>GAP</C>,
#! whereas no match is found for the input <C>?Julia:julia_to_gap</C>.
#! whereas no match is found for the input <C>?Julia:wrap_rng</C>.
#! </Item>
#! </List>
#! @EndChunk
Expand Down
6 changes: 3 additions & 3 deletions pkg/JuliaInterface/tst/convert.tst
Original file line number Diff line number Diff line change
Expand Up @@ -189,11 +189,11 @@ gap> Julia.GAP.Obj( list );
[ 1, 2, 3 ]

## ranges
gap> Julia.GAP.julia_to_gap( JuliaEvalString( "1:3" ) );
gap> Julia.GAP.GapObj( JuliaEvalString( "1:3" ) );
[ 1 .. 3 ]
gap> Julia.GAP.julia_to_gap( JuliaEvalString( "1:2:5" ) );
gap> Julia.GAP.GapObj( JuliaEvalString( "1:2:5" ) );
[ 1, 3 .. 5 ]
gap> Julia.GAP.julia_to_gap( JuliaEvalString( "3:2" ) );
gap> Julia.GAP.GapObj( JuliaEvalString( "3:2" ) );
[ ]
gap> Julia.GAP.Obj( JuliaEvalString( "1:3" ) );
[ 1 .. 3 ]
Expand Down
2 changes: 1 addition & 1 deletion pkg/JuliaInterface/tst/help.tst
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ true
gap> str:= HelpString( "Julia:GAP.wrap_rng" );; # is not exported from GAP
gap> PositionSublist( str, "wrap_rng" ) <> fail;
true
gap> str:= HelpString( "Julia:GapObj" );; # is exported from GAP
gap> str:= HelpString( "Julia:GAP.GapObj" );;
gap> PositionSublist( str, "GapObj" ) <> fail;
true
gap> str:= HelpString( "Julia:sqrt" );; # is from Julia.Base
Expand Down
4 changes: 2 additions & 2 deletions src/adapter.jl
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ Base.copy(rng::MersenneTwister) = MersenneTwister(state=MersenneTwisterState(Glo
Random.rand(rng::AbstractGAPRNG, x::Random.SamplerTrivial{<:Obj}) = Globals.Random(rng.ptr, x[])

Random.rand(rng::AbstractGAPRNG, x::Random.SamplerTrivial{<:AbstractUnitRange}) =
Globals.Random(rng.ptr, julia_to_gap(first(x[])), julia_to_gap(last(x[])))
Globals.Random(rng.ptr, GapObj(first(x[])), GapObj(last(x[])))

Random.Sampler(::Type{<:AbstractGAPRNG}, x::AbstractUnitRange, ::Random.Repetition) =
Random.SamplerTrivial(x)
Expand All @@ -464,7 +464,7 @@ for U in (Base.BitInteger64, Union{Int128,UInt128})
end

Random.Sampler(::Type{<:AbstractGAPRNG}, x::AbstractVector, ::Random.Repetition) =
Random.SamplerTrivial(julia_to_gap(x, recursive=false))
Random.SamplerTrivial(GapObj(x, recursive=false))


# The following bypasses GAP's redirection of `x^-1` to `InverseSameMutability(x)`.
Expand Down
4 changes: 2 additions & 2 deletions src/ccalls.jl
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,11 @@ be a useful escape hatch to access GAP functionality that is otherwise
impossible to difficult to reach. But in most typical scenarios it
should not be necessary to use it at all.
Instead, use `GAP.GapObj` or `GAP.Obj` for constructing GAP objects
Instead, use `GapObj` or `GAP.Obj` for constructing GAP objects
that correspond to given Julia objects,
and call GAP functions directly in the Julia session.
For example, executing `GAP.evalstr( "x:= []; Add( x, 2 )" )`
can be replaced by the Julia code `x = GAP.GapObj([]); GAP.Globals.Add(x, 2)`.
can be replaced by the Julia code `x = GapObj([]); GAP.Globals.Add(x, 2)`.
Note that the variable `x` in the former example lives in the GAP session,
i.e., it can be accessed as `GAP.Globals.x` after the call of `GAP.evalstr`,
whereas `x` in the latter example lives in the Julia session.
Expand Down
11 changes: 5 additions & 6 deletions src/constructors.jl
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
## Handle "conversion" to GAP.Obj and GAP.GapObj (may occur in recursions).
## Handle "conversion" to GAP.Obj and GapObj (may occur in recursions).
Obj(x::Obj) = x
GapObj(x::GapObj) = x

## Handle conversion of Julia objects to GAP objects
Obj(obj; recursive::Bool = false) = julia_to_gap(obj, IdDict(); recursive)::Obj
GapObj(obj; recursive::Bool = false) = julia_to_gap(obj, IdDict(); recursive)::GapObj
Obj(obj; recursive::Bool = false) = GapObj_internal(obj, nothing, Val(recursive))::Obj

Obj(obj, recursive::Bool) = julia_to_gap(obj, IdDict(); recursive)::Obj
GapObj(obj, recursive::Bool) = julia_to_gap(obj, IdDict(); recursive)::GapObj
Obj(obj, recursive::Bool) = GapObj_internal(obj, nothing, Val(recursive))::Obj
GapObj(obj, recursive::Bool) = GapObj_internal(obj, nothing, Val(recursive))::Obj

## Conversion to gap integers
GapInt(x::Integer) = julia_to_gap(x)
GapInt(x::Integer) = GapObj(x)


"""
Expand Down
2 changes: 2 additions & 0 deletions src/gap_to_julia.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ An internal type of GAP.jl used for tracking conversion results in `gap_to_julia
"""
const RecDict = IdDict{Any,Any}

const GapCacheDict = Union{Nothing,RecDict}

## Conversion from GAP to Julia
"""
gap_to_julia(type, x, recursion_dict::Union{Nothing,RecDict}=nothing; recursive::Bool=true)
Expand Down
Loading

0 comments on commit 64fb95a

Please sign in to comment.