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-4) are always in IsJuliaObject. The results of JuliaEvalString (2.2-1) are in IsArgumentForJuliaFunction (2.1-5) but not necessarily 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 MPtr, 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-4).

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. A submodule of a module can be accessed like a record component, provided that this submodule has already been imported, see ImportJuliaModuleIntoGAP (2.3-3). Julia variables from a 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.1-5 IsArgumentForJuliaFunction
‣ IsArgumentForJuliaFunction( obj )( function )

This function returns true for all those GAP objects that can be used as arguments of Julia functions. These are the objects in IsJuliaObject (2.1-1), IsJuliaWrapper (2.1-2), IsBool (Reference: IsBool), IsInt and IsSmallIntRep (see Reference: Integers), and IsFFE and IsInternalRep (see IsFFE (Reference: IsFFE)). For all other GAP objects, the function returns false.

gap> m:= JuliaEvalString( "sqrt(2)" );;
gap> ForAll( [ m, Julia, true, 1, Z(2) ], IsArgumentForJuliaFunction );
true
gap> ForAny( [ 2^62, [], (1,2) ], IsArgumentForJuliaFunction );
false

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. If the file defines a new Julia module then the next step will be to import this module, see ImportJuliaModuleIntoGAP (2.3-3).

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. The returned function can be called on arguments in IsArgumentForJuliaFunction (2.1-5).

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 ImportJuliaModuleIntoGAP
‣ ImportJuliaModuleIntoGAP( name )( function )

Returns: nothing.

The aim of this function is to make the Julia module with name name available in the current GAP session. After the call, the name component of the global object Julia (2.3-2) will be bound, and one can access the module as a component of Julia (2.3-2) or via JuliaModule (2.3-4).

gap> ImportJuliaModuleIntoGAP( "GAP" );
gap> Julia.GAP;
<Julia module GAP>

The Julia modules Base, Core, and GAP have in fact already been imported when the JuliaInterface package got loaded.

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

Returns: a Julia object

Returns the Julia object that points to the Julia module with name name. Note that this module needs to be imported before being present, see ImportJuliaModuleIntoGAP (2.3-3).

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

2.3-5 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-6 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 occured then ok has the value true, and value is the value returned by juliafunc. If an error occured 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( "Array{Int,2}" ), [[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-7 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( "Array{Int,2}" ),
>            [ [ 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>

There are also callable Julia objects which aren't represented by GAP functions, for example Julia types can be called like functions. In this situation, the function call is executed via the applicable CallFuncList (Reference: CallFuncList) method, which calls Julia's Core._apply.

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( "Array{Int,2}" ),
>            [ [ 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