Goto Chapter: Top 1 2 3 Ind
 [Top of Book]  [Contents]   [Previous Chapter]   [Next Chapter] 

2 Using Julia from GAP
 2.1 Filters for JuliaInterface
 2.2 Creating Julia objects
 2.3 Access to Julia objects
 2.4 Calling Julia functions
 2.5 Utilities
 2.6 Access Julia help from a GAP session

2 Using Julia from GAP

2.1 Filters for JuliaInterface

2.1-1 IsJuliaObject
‣ IsJuliaObject( obj )( filter )

Returns: true or false

The result is true if and only if obj is a pointer to a Julia object.

gap> julia_fun:= JuliaEvalString( "sqrt" );
<Julia: sqrt>
gap> IsJuliaObject( julia_fun );
true
gap> julia_val:= julia_fun( 2 );
<Julia: 1.4142135623730951>
gap> IsJuliaObject( julia_val );
true

However not every object living on the Julia side is in this filter. For example Julia booleans and small Int values are directly translated to GAP booleans and small integers, while for Julia functions dedicated wrappers are used to improve efficiency and add features.

gap> JuliaEvalString( "x = 4" );;
gap> Julia.x;
4
gap> IsJuliaObject( Julia.x );
false
gap> IsJuliaObject( Julia.sqrt );
false

2.1-2 IsJuliaWrapper
‣ IsJuliaWrapper( obj )( filter )

Returns: true or false

If the component or positional object obj is in this filter then calling a Julia function with obj as an argument will not pass obj as an GapObj, but instead its JuliaPointer (2.1-3) value is passed, which must be a Julia object. This admits implementing high-level wrapper objects for Julia objects that behave just like the Julia objects when used as arguments in calls to Julia functions.

Objects in IsJuliaWrapper should not be in the filter IsJuliaObject (2.1-1).

2.1-3 JuliaPointer
‣ JuliaPointer( obj )( attribute )

is an attribute for GAP objects in the filter IsJuliaWrapper (2.1-2). The value must be a Julia object.

2.1-4 IsJuliaModule
‣ IsJuliaModule( obj )( filter )

Returns: true or false

This filter is set in those GAP objects that represent Julia modules. Members of a Julia module can be accessed like record components.

gap> IsJuliaModule( Julia );
true
gap> Julia.GAP;
<Julia module GAP>
gap> IsJuliaModule( Julia.GAP );
true
gap> Julia.GAP.gap_to_julia;
<Julia: gap_to_julia>

2.2 Creating Julia objects

2.2-1 JuliaEvalString
‣ JuliaEvalString( string )( function )

evaluates the string string in the current Julia session, in the Main module, and returns Julia's return value.

gap> JuliaEvalString( "x = 2^2" );  # assignment to a variable in Julia
4
gap> JuliaEvalString( "x" );        # access to this variable
4

2.2-2 JuliaIncludeFile
‣ JuliaIncludeFile( filename[, module_name] )( function )

Returns: nothing.

calls Julia's Base.include with the strings filename (an absolute filename, as returned by Filename (Reference: Filename)) and module_name (the name of a Julia module, the default is "Main"). This means that the Julia code in the file with name filename gets executed in the current Julia session, in the context of the Julia module module_name.

2.2-3 JuliaImportPackage
‣ JuliaImportPackage( pkgname )( function )

Returns: true or false.

This function triggers the execution of an import statement for the Julia package with name pkgname. It returns true if the call was successful, and false otherwise.

Note that we just want to load the package into Julia, we do not want to import variable names from the package into Julia's Main module, because the Julia variables must be referenced relative to their modules if we want to be sure to access the correct values.

Why is this function needed?

Apparently libjulia throws an error when trying to compile the package, which happens when some files from the package have been modified since compilation.

Thus GAP has to check whether the Julia package has been loaded successfully, and can then safely load and execute code that relies on this Julia package. In particular, we cannot just put the necessary import statements into the relevant .jl files, and then load these files with JuliaIncludeFile (2.2-2).

2.3 Access to Julia objects

Not all Julia syntax features are supported in GAP. For important ones, the interface provides GAP functions or helper functions written in Julia to use them in GAP. For example, CallJuliaFunctionWithCatch (2.3-3) allows one to use Julia's try/catch statements.

Here is a selection of other workarounds for Julia syntax features.

2.3-1 Julia
‣ Julia( global variable )

This global variable represents the Julia module Main, see IsJuliaModule (???).

The variables from the underlying Julia session can be accessed via Julia, as follows.

gap> Julia.sqrt;  # a Julia function
<Julia: sqrt>
gap> JuliaEvalString( "x = 1" );  # an assignment in the Julia session
1
gap> Julia.x;  # access to the value that was just assigned
1
gap> Julia.Main.x;
1

Note that not all Julia variables are directly visible in its Main module. For example, Julia variables from the interface to GAP are defined in the Julia module GAP or its submodules. It is safe to access this module as Julia.GAP.

gap> Julia.GAP;
<Julia module GAP>
gap> Julia.GAP.prompt;
<Julia: prompt>

2.3-2 JuliaTypeInfo
‣ JuliaTypeInfo( juliaobj )( function )

Returns: a string.

Returns the string that describes the Julia type of the object juliaobj.

gap> JuliaTypeInfo( Julia.GAP );
"Module"
gap> JuliaTypeInfo( Julia.sqrt(2) );
"Float64"
gap> JuliaTypeInfo( 1 );
"Int64"

2.3-3 CallJuliaFunctionWithCatch
‣ CallJuliaFunctionWithCatch( juliafunc, arguments[, kwargs] )( function )

Returns: a record.

The function calls the Julia function juliafunc with ordinary arguments in the GAP list arguments and optionally with keyword arguments given by the component names (keys) and values of the GAP record kwargs, and returns a record with the components ok and value. If no error occurred then ok has the value true, and value is the value returned by juliafunc. If an error occurred then ok has the value false, and value is the error message as a GAP string.

gap> fun:= Julia.sqrt;;
gap> CallJuliaFunctionWithCatch( fun, [ 2 ] );
rec( ok := true, value := <Julia: 1.4142135623730951> )
gap> res:= CallJuliaFunctionWithCatch( fun, [ -1 ] );;
gap> res.ok;
false
gap> res.value{ [ 1 .. Position( res.value, '(' )-1 ] };
"DomainError"
gap> inv:= Julia.inv;;
gap> m:= GAPToJulia( JuliaEvalString( "Matrix{Int}" ), [[1,2],[2,4]] );
<Julia: [1 2; 2 4]>
gap> res:= CallJuliaFunctionWithCatch( inv, [ m ] );;
gap> res.ok;
false
gap> res.value{ [ 1 .. Position( res.value, '(' )-1 ] };
"LinearAlgebra.SingularException"
gap> fun:= Julia.range;;
gap> CallJuliaFunctionWithCatch( fun, [ 2, 10 ], rec( step:= 2 ) );
rec( ok := true, value := <Julia: 2:2:10> )
gap> res:= CallJuliaFunctionWithCatch( fun, [ 2, 10 ],
>              rec( step:= GAPToJulia( "a" ) ) );;
gap> res.ok;
false
gap> res.value{ [ 1 .. Position( res.value, '(' )-1 ] };
"MethodError"

2.3-4 CallJuliaFunctionWithKeywordArguments
‣ CallJuliaFunctionWithKeywordArguments( juliafunc, arguments, kwargs )( function )

Returns: the result of the Julia function call.

The function calls the Julia function juliafunc with ordinary arguments in the GAP list arguments and keyword arguments given by the component names (keys) and values of the record kwargs, and returns the function value.

Note that the entries of arguments and the components of kwargs are not implicitly converted to Julia.

gap> CallJuliaFunctionWithKeywordArguments( Julia.Base.round,
>        [ GAPToJulia( Float( 1/3 ) ) ], rec( digits:= 5 ) );
<Julia: 0.33333>
gap> CallJuliaFunctionWithKeywordArguments(
>        Julia.Base.range, [ 2 ], rec( length:= 5, step:= 2 ) );
<Julia: 2:2:10>
gap> m:= GAPToJulia( JuliaEvalString( "Matrix{Int}" ),
>            [ [ 1, 2 ], [ 3, 4 ] ] );
<Julia: [1 2; 3 4]>
gap> CallJuliaFunctionWithKeywordArguments(
>        Julia.Base.reverse, [ m ], rec( dims:= 1 ) );
<Julia: [3 4; 1 2]>
gap> CallJuliaFunctionWithKeywordArguments(
>        Julia.Base.reverse, [ m ], rec( dims:= 2 ) );
<Julia: [2 1; 4 3]>
gap> tuptyp:= JuliaEvalString( "Tuple{Int,Int}" );;
gap> t1:= GAPToJulia( tuptyp, [ 2, 1 ] );
<Julia: (2, 1)>
gap> t2:= GAPToJulia( tuptyp, [ 1, 3 ] );
<Julia: (1, 3)>
gap> CallJuliaFunctionWithKeywordArguments(
>        Julia.Base.( "repeat" ), [ m ],
>        rec( inner:= t1, outer:= t2 ) );
<Julia: [1 2 1 2 1 2; 1 2 1 2 1 2; 3 4 3 4 3 4; 3 4 3 4 3 4]>

2.4 Calling Julia functions

The simplest way to execute Julia code from GAP is to call JuliaEvalString (2.2-1) with a string that contains the Julia code in question.

gap> JuliaEvalString( "sqrt( 2 )" );
<Julia: 1.4142135623730951>

However, it is usually more suitable to create GAP variables whose values are Julia objects, and to call Julia functions directly. The GAP function call syntax is used for that.

gap> jsqrt:= JuliaEvalString( "sqrt" );
<Julia: sqrt>
gap> jsqrt( 2 );
<Julia: 1.4142135623730951>

In fact, there are slightly different kinds of function calls. A Julia function such as Julia.sqrt is represented by a GAP function object, and calls to it are executed on the C level, using Julia's jl_call.

gap> fun:= Julia.sqrt;
<Julia: sqrt>
gap> IsJuliaObject( fun );
false
gap> IsFunction( fun );
true
gap> fun( 2 );
<Julia: 1.4142135623730951>

Note that in Julia any object (not just functions) is potentially callable (in fact this is the same as in GAP), for example Julia types can be

gap> smalltype:= Julia.Int32;
<Julia: Int32>
gap> IsJuliaObject( smalltype );
true
gap> IsFunction( smalltype );
false
gap> val:= smalltype( 1 );
<Julia: 1>
gap> JuliaTypeInfo( val );
"Int32"
gap> JuliaTypeInfo( 1 );
"Int64"

2.4-1 Convenience methods for Julia objects

For the following operations, methods are installed that require arguments in IsJuliaObject (2.1-1) and delegate to the corresponding Julia functions.

gap> m:= GAPToJulia( JuliaEvalString( "Matrix{Int}" ),
>            [ [ 1, 2 ], [ 3, 4 ] ] );
<Julia: [1 2; 3 4]>
gap> m[1,2];
2
gap> - m;
<Julia: [-1 -2; -3 -4]>
gap> m + m;
<Julia: [2 4; 6 8]>

2.5 Utilities

2.5-1 GetJuliaScratchspace
‣ GetJuliaScratchspace( key )( function )

Returns: a string

Returns the path of a Julia scratchspace associated to the given key. This scratchspace gets created if it did not exist already, one can rely on the fact that the returned path describes a writable directory. Subsequent calls with the same key yield the same result, and calls with different keys yield different results. The directory may be removed by Julia as soon as the Julia package GAP.jl gets uninstalled.

2.6 Access Julia help from a GAP session

In a Julia session, one can ask for help about the object with the name obj (a function or a type) by entering ?obj, and Julia prints all matches to the screen. One can get the same output in a GAP session by entering ?Julia:obj, cf. Section Reference: Invoking the Help in the GAP Reference Manual. For example, ?Julia:sqrt shows the Julia help about the Julia function sqrt (which is available in GAP as Julia.sqrt).

Note that this way to access the Julia help is different from the usual access to GAP help books, in the following sense.

 [Top of Book]  [Contents]   [Previous Chapter]   [Next Chapter] 
Goto Chapter: Top 1 2 3 Ind

generated by GAPDoc2HTML