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 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.

The results of JuliaModule (2.3-3) are always in IsJuliaObject.

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
gap> julia_x:= JuliaEvalString( "x = 4" );
4
gap> IsJuliaObject( julia_x );
false
gap> IsJuliaObject( JuliaModule( "Main" ) );
true

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).

Examples of objects in IsJuliaWrapper are the return values of JuliaModule (2.3-3).

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.

gap> Julia;
<Julia module Main>
gap> IsJuliaObject( Julia );
false
gap> IsJuliaWrapper( Julia );
true
gap> ptr:= JuliaPointer( Julia );
<Julia: Main>
gap> IsJuliaObject( ptr );
true

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.julia_to_gap;
<Julia: julia_to_gap>
gap> JuliaFunction( "julia_to_gap", "GAP" );  # the same function
<Julia: julia_to_gap>

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

2.3-1 JuliaFunction
‣ JuliaFunction( function_name[, module_name] )( function )

Returns: a function

Returns a GAP function that wraps the Julia function with identifier function_name from the module module_name. Both arguments must be strings. If module_name is not given, the function is taken from Julia's Main module.

gap> fun:= JuliaFunction( "sqrt" );
<Julia: sqrt>
gap> Print( fun );
function ( arg... )
    <<kernel code>> from Julia:sqrt
end
gap> IsFunction( fun );
true
gap> IsJuliaObject( fun );
false

Alternatively, one can access Julia functions also via the global object Julia (2.3-2), as follows.

gap> Julia.sqrt;
<Julia: sqrt>

Note that each call to JuliaFunction and each component access to Julia (2.3-2) create a new GAP object.

gap> IsIdenticalObj( JuliaFunction( "sqrt" ), JuliaFunction( "sqrt" ) );
false
gap> IsIdenticalObj( Julia.sqrt, Julia.sqrt );
false

2.3-2 Julia
‣ Julia( global variable )

This global variable represents the Julia module Main, see IsJuliaModule (2.1-4).

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

2.3-3 JuliaModule
‣ JuliaModule( name )( function )

Returns: a Julia object

Returns the Julia object that points to the Julia module with name name.

gap> gapmodule:= JuliaModule( "GAP" );
<Julia: GAP>
gap> gapmodule = JuliaPointer( Julia.GAP );
true

2.3-4 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( JuliaPointer( Julia.GAP ) );
"Module"
gap> JuliaTypeInfo( JuliaEvalString( "sqrt(2)" ) );
"Float64"
gap> JuliaTypeInfo( 1 );
"Int64"

2.3-5 CallJuliaFunctionWithCatch
‣ CallJuliaFunctionWithCatch( juliafunc, arguments )( function )

Returns: a record.

The function calls the Julia function juliafunc with arguments in the GAP list arguments, 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"

2.3-6 CallJuliaFunctionWithKeywordArguments
‣ CallJuliaFunctionWithKeywordArguments( juliafunc, arguments, arec )( 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 arec, and returns the function value.

Note that the entries of arguments and the components of arec 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 (or equivalently JuliaFunction( "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 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