Using the GAUSS Engine#
This chapter covers the general guidelines for creating an application that uses the GAUSS Engine. Specific multi-threading issues are covered in Chapter 5.
The use of the GAUSS Engine can be broken up into the following steps:
Setup and Initialization
Set up logging
Set home directory
Hook I/O callback functions
Initialize Engine
Computation
Create workspaces
Copy or move data
Compile or load GAUSS code
Execute GAUSS code
Free workspaces
Shutdown
Setup and Initialization#
Logging#
General GAUSS Engine system errors are sent to a file and/or a stream pointer. Default values are provided for each. You can change the default values or turnoff logging altogether with GAUSS_SetLogFile and GAUSS_SetLogStream. This should be done before calling any other GAUSS Engine functions.
Home Directory#
The GAUSS Engine home directory location is usually set to the same directory as the main executable of the calling application. It is used to locate the configuration file, Run-Time Library files, etc. used by the GAUSS Engine.
Use GAUSS_SetHome to set the home directory, prior to calling GAUSS_Initialize. An alternate method is to use GAUSS_SetHomeVar to set the name of an environment variable that contains the home directory location.
I/O CallbackFunctions#
The GAUSS Engine calls user defined functions for program output from print statements and for error messages. Default functions are provided for the main thread in console applications.
Normal program output |
stdout |
Program error output |
stderr |
Program input |
stdin |
To change the default behavior, you can supply callback functions of your own and use the following functions to hook them:
Normal program output |
GAUSS_HookProgramOutput |
Program error output |
GAUSS_HookProgramErrorOutput |
Program input |
GAUSS_HookProgramInputString |
The functions GAUSS_HookProgramInputChar, GAUSS_HookProgramInputCharBlocking and GAUSS_HookProgramInputCheck are also supported, but no default behavior is defined.
All I/O callback functions are thread specific and must be explicitly hooked in each thread that uses them, except for the three above that are hooked by default for the main thread.
Use the hook functions to specify the input functions that the GAUSS Engine calls as follows:
Functions Hooked By |
Are Called By |
GAUSS_HookProgramInputChar |
key |
GAUSS_HookProgramInputCharBlocking |
keyw, show |
GAUSS_HookProgramInputCheck |
keyav |
GAUSS_HookProgramInputString |
con, cons |
There are two hook functions that are used to control output from GAUSS programs. Use GAUSS_HookProgramOutput
to hook a function that GAUSS will call to display all normal program output. Use GAUSS_HookProgramErrorOutput
to hook a function that GAUSS will call to display all program error output.
Initialize Engine#
Call GAUSS_Initialize after the previous steps are completed. The GAUSS Engine ready for use.
Computation#
Workspaces#
All computation in the GAUSS Engine is done in a workspace. Workspaces are independent from one another and each workspace contains its own global data and procedures. Workspaces are created with GAUSS_CreateWorkspace, which returns a workspace handle.
Workspaces are freed with GAUSS_FreeWorkspace. The contents of a workspace can be saved to disk with GAUSS_SaveWorkspace.
Programs#
Two functions are provided in order to execute GAUSS program code. Each requires a program handle.
GAUSS_Execute |
Executes a GAUSS program |
GAUSS_ExecuteExpression |
Executes a right-hand side expression |
Six functions are provided to create program handles. A program handle contains compiled GAUSS program code.
GAUSS_CompileExpression |
Compiles a right-hand side expression |
GAUSS_CompileFile |
Compiles a GAUSS program file |
GAUSS_CompileString |
Compiles GAUSS commands in a character string |
GAUSS_CompileStringAsFile |
Compiles GAUSS commands in a character string |
GAUSS_LoadCompiledFile |
Loads a compiled program from disk |
GAUSS_LoadCompiledBuffer |
Loads a compiled program from disk |
The following code illustrates a simple program that creates a random matrix and computes its inverse.
WorkspaceHandle_t *w1;
ProgramHandle_t *ph;
Int rv;
w1 = GAUSS_CreateWorkspace(“Workspace 1”);
ph = GAUSS_CompileString(w1,”x = rndu(10,10);xi = inv(x);”,0,0);
rv = GAUSS_Execute(ph);
When this program is finished executing, the workspace will contain two global matrices. x is a 10×10 matrix of random numbers and xi is its inverse.
The following code retrieves xi from the workspace to the calling application.
Matrix_t *mat;
mat = GAUSS_GetMatrix(w1,”xi”);
The following code copies the retrieved matrix to another workspace as xinv.
WorkspaceHandle_t *w2;
w2 = GAUSS_CreateWorkspace(“Workspace 2”);
rv = GAUSS_CopyMatrixToGlobal(w2, mat,”xinv”);
The copy can also be done directly from one workspace to another.
WorkspaceHandle_t *w2;
w2 = GAUSS_CreateWorkspace(“Workspace 2”);
rv = GAUSS_CopyGlobal(w2,”xinv”,w1,”xi”);
GAUSS Engine Data Structures#
The following data structures are used for moving data between the application and the GAUSS Engine. See Chapter 12 for detailed information on the structures.
Array_t |
N-dimensional array, real or complex |
|---|---|
Matrix_t |
2-dimensional matrix, real or complex |
String_t |
character string |
StringArray_t |
string array |
StringElement_t |
string array element |
API calls to create and free this data. You can create copies of the data or aliases to the data.
If you have a lot of data, you will want to minimize the amount of memory used and the number of times a block of data is copied from one location in memory to another.
Use GAUSS_Matrix to create a Matrix_t structure. The following code creates a copy of the matrix x.
WorkspaceHandle_t *w1;
Matrix_t *mat;
double x[100][20];
w1 = GAUSS_CreateWorkspace(“Workspace 1”);
mat = GAUSS_Matrix(w1, 100, 20, x);
The call to GAUSS_Matrix calls malloc once for the Matrix_t structure and once for the matrix data. It then copies the matrix into the newly allocated block.
The following code creates an alias for the matrix x.
Matrix_t *matalias;
Matalias = GAUSS_MatrixAlias(w1, 100, 20, x );
The call to GAUSS_MatrixAlias calls malloc only once for the Matrix_t structure. It then sets the data pointer in the Matrix_t structure to the address of x. No copy is necessary.
The following code frees both mat and matalias.
GAUSS_FreeMatrix( mat );
GAUSS_FreeMatrix( matalias );
The first call above frees both the data block (which is a malloc‘d copy of x) and the Matrix_t structure for mat. The second call frees only the Matrix_t structure for matalias because that Matrix_t structure contained only an alias to data that the user is left responsible for freeing if necessary.
Memory Ownership Reference#
Every Matrix_t structure contains a freeable flag that determines whether the data block is freed when GAUSS_FreeMatrix is called. The following table summarizes who owns the data after each API call:
Pattern |
Data owned by |
Shell struct owned by |
Caller action after |
|---|---|---|---|
|
Engine (copy) |
Engine |
Call |
|
Caller (alias) |
Engine |
Call |
|
Engine (transferred) |
Freed inside Move |
Do NOT call FreeMatrix – the shell is already freed by the move. |
|
Caller (original) + Engine (copy) |
Caller |
Call |
|
Caller (copy) |
Caller |
Call |
|
Caller (zero-copy) |
Caller |
Call |
|
Engine takes ownership |
N/A (no shell) |
Do NOT free the pointer – the engine will free it. |
Warning
Do not call GAUSS_FreeMatrix after GAUSS_MoveMatrixToGlobal. The Move function frees the Matrix_t shell internally. Calling FreeMatrix afterward is a double-free that will crash your application.
Do not free memory passed to GAUSS_AssignFreeableMatrix. After this call, the engine owns the data block and will free it when the variable is reassigned or the workspace is destroyed.
Copying and Moving Data to a Workspace#
Use the GAUSS Engine API calls to pass the data between a GAUSS Engine workspace and your application. There are two versions of many of these API calls. One makes a copy of the data (malloc’s a new data block) and the other moves the data (gives the data pointer away without any calls to malloc and frees the original structure). The functions are named accordingly.
The following code uses GAUSS_CopyMatrixToGlobal to copy a matrix to the GAUSS Engine. The matrix will be called xm in the workspace.
WorkspaceHandle_t *w1;
Matrix_t *mat;
double x[100][20];
int rv;
w1 = GAUSS_CreateWorkspace( “Workspace 1” );
mat = GAUSS_Matrix( w1, 100, 20, x );
rv = GAUSS_CopyMatrixToGlobal( w1, mat, “xm” );
The following code uses GAUSS_MoveMatrixToGlobal to move a matrix to the GAUSS Engine and free the Matrix_t
structure. The matrix will be called xm in the workspace. The original malloc’d block held by the double pointer x is left intact.
WorkspaceHandle_t *w1;
Matrix_t *mat;
double *x;
int r, c;
int rv;
r = 1000;
c = 10;
x = (double *) malloc( r*c*sizeof(double) );
memset( x, 0, r*c*sizeof(double) );
w1 = GAUSS_CreateWorkspace( “Workspace 1” );
mat = GAUSS_Matrix( w1, 100, 20, x );
rv = GAUSS_MoveMatrixToGlobal( w1, mat, “xm” );
This can also be accomplished with a nested call, eliminating the need for the intermediate structure. Again, the original malloc’d block held by the double pointer x is left intact.
WorkspaceHandle_t *w1;
double *x;
int r, c;
int rv;
r = 1000;
c = 10;
x = (double *) malloc( r*c*sizeof(double) );
memset( x, 0, r*c*sizeof(double) );
w1 = GAUSS_CreateWorkspace(“Workspace 1”);
rv = GAUSS_MoveMatrixToGlobal( w1, GAUSS_Matrix( w1, r, c, x ), “xm” );
A very large malloc’d matrix can be given to a workspace without any additional malloc’s or copying with GAUSS_AssignFreeableMatrix. In the code below, a 1000000×100 real matrix is created and placed in a workspace.
WorkspaceHandle_t *w1;
double *x;
int r, c;
int rv;
r = 1000000;
c = 100;
x = (double *) malloc( r*c*sizeof(double) );
memset( x, 0, r*c*sizeof(double) );
w1 = GAUSS_CreateWorkspace( “Workspace 1” );
rv = GAUSS_AssignFreeableMatrix( w1, r, c, 0, x, “largex” );
After the call to GAUSS_AssignFreeableMatrix, the block of memory pointed to by the double pointer is owned by the GAUSS Engine. An attempt by the user to free it will cause a fatal error. The GAUSS Engine will free the block when necessary.
Getting Data From a Workspace#
The following code retrieves the matrix xi from the workspace to the calling application.
Matrix_t *mat;
mat = GAUSS_GetMatrix( w1, “xi” );
The following code checks the type of the symbol xi and retrieves it from the workspace to the calling application.
Array_t *arr;
Matrix_t *mat;
StringArray_t *sa;
String_t *st;
int type;
arr = NULL;
mat = NULL;
sa = NULL;
st = NULL;
type = GAUSS_GetSymbolType( w1, “xi” );
switch( type )
{
case GAUSS_ARRAY:
arr = GAUSS_GetArray( w1, “xi” );
break;
case GAUSS_MATRIX:
mat = GAUSS_GetMatrix( w1, “xi” );
break;
case GAUSS_STRING_ARRAY:
sa = GAUSS_GetStringArray( w1, “xi” );
break;
case GAUSS_STRING:
st = GAUSS_GetString( w1, “xi” );
break;
default:
fprintf( stderr, “Invalid type (%d)n”, type);
break;
}
Calling Procedures#
Two functions are provided to call GAUSS procedures, passing the arguments directly to the calling application and receiving the returns back directly, without the use of globals. Each requires an empty program handle. An empty program handle can be created with GAUSS_CreateProgram.
GAUSS_CallProc |
Calls a GAUSS procedure |
|---|---|
GAUSS_CallProcFreeArgs |
Calls a GAUSS procedure and frees the arguments |
Shutdown#
When your application has completed using the GAUSS Engine you should call GAUSS_Shutdown before exiting the application.
It is possible to restart the GAUSS Engine by calling GAUSS_Initialize again after calling GAUSS_Shutdown.