Contents Previous Next Index
14 Advanced Connectivity
This chapter describes how to connect Maple to other applications. Maple can be connected as the main interface (for example, to a database), as a hidden engine (for example, as part of a Microsoft® Excel® plug-in), or side-by-side with another application (for example, a CAD application). You can also use Maple to generate code.
14.1 In This Chapter
Connecting to the Maple engine
Embedding external libraries in Maple
Connecting Maple to another program
Code generation
Connecting to the Maple Engine
There are several ways to use the Maple computation engine in other applications. For example, you can create a financial application that runs calculations using the MapleNet web service API; create a plug-in for Microsoft Excel in both Visual Basic and C++ to perform Maple computations in a spreadsheet; and create an engineering process to generate and batch process scripts using the Maple command-line interface. These are examples of real situations where you can use Maple as a calculation engine embedded in an external interface.
For more information, see MapleNet, OpenMaple, and The Maple Command-line Interface.
Using External Libraries in Maple
Most dynamic link-libraries (.dlls) that contain mathematical functions written in another programming language can be linked directly in Maple. Using the commands in these libraries usually requires you to translate the Maple data into a format that the external library can interpret. Maple provides an extensive API for data conversions, evaluation, and process control. You can, therefore, use a custom library of functions in Maple as if it were a regular Maple package. You can also use the Maple external API to connect Maple to a hardware device for data acquisition, link with an open source suite of utilities, or to avoid rewriting legacy code.
For more information, see External Calling: Using Compiled Code in Maple.
Connecting Maple to Another Program
You can set up Maple to communicate with another program by using the Maple external API. For example, by using the CAD package, you can set up Maple to communicate with CAD applications. Other methods are available, such as setting up a communication channel to the other program using the Sockets package.
For more information, see CAD Connectivity, Maple Plug-in for Excel, Connecting MATLAB® and Maple, and Accessing Data over a Network with TCP/IP Sockets.
Code Generation
The Maple programming language and the worksheet environment are ideal for creating prototypes. They are also ideal for error-free algebraic manipulations and long calculations. When you create a prototype that includes various formulas, you can easily write that program in the native language that is used by your application. The generated code can then be compiled and embedded directly in your application.
The CodeGeneration package provides commands for translating Maple code into other programming languages such as C, Visual C#, Fortran, Java, MATLAB®, and Visual Basic. The resulting code can be compiled and used in applications other than Maple.
For more information, see Code Generation.
14.2 MapleNet
MapleNet provides online viewing and execution of Maple documents and access to a Maple compute programming interface. Maple worksheets and workbooks can be viewed in web browsers and embedded components in those documents are interactive.
In addition, Maple help databases can be hosted to allow web-based navigation of Maple help content. The compute endpoint allows programmatic access to the Maple computation engine. Third party applications that require complex mathematical computations can send compute requests to MapleNet via standard HTTP POST request.
Computation on Demand
The MapleNet Compute Service allows for applications to send a Maple computation to MapleNet and receive the result of executing that computation. This service is an application programming interface, not an end user interface.
For more details about the Compute Service see https://www.maplesoft.com/documentation_center/MapleNet2019/MapleNetComputeAPI.pdf.
14.3 OpenMaple
OpenMaple is an interface that lets you access the Maple computation engine by referencing its dynamic-link library (.dll) file.
Note: The OpenMaple interface is available on all platforms supported by Maple. The convention in this guide is to use the terminology related to .dll files, in place of .so or .dylib on other systems.
You can use this interface to embed calls to Maple in other applications.
Interfaces to access the OpenMaple API are provided for use with C, C++, Java, Fortran, C#, and Visual Basic. All of these interfaces are built on the C API, so they all reference the primary library, maplec.dll, which is located in your Maple binary directory. This library can be accessed from other languages by following the protocol established in the maplec.dll file.
Complete example programs are available in the samples/OpenMaple subdirectory of your Maple installation. In conjunction with reading this section, you may want to try extending one or more of those examples before creating your own programs.
Application-specific header files can be found in the extern/include subdirectory of your Maple installation. If you are developing a Java application, you can find the jopenmaple.jar file in the java subdirectory of your Maple installation.
Runtime Environment Prerequisites
To run your application, two paths must be set up in your local environment.
the path to the maplec.dll file
the path to the top-level directory in which Maple is installed
In Windows, depending on the source programming language, calls to initialize the OpenMaple interface will locate these paths automatically so that the Maple commands will work without additional configuration steps.
Note: If your application does not initialize, set your Windows %PATH% environment variable to include the Maple bin.win or bin.X86_64_WINDOWS directory. To find out which path to use, run the kernelopts(bindir) command in Maple.
In Linux and macOS, the MAPLE, and LD_LIBRARY_PATH or DYLD_LIBRARY_PATH environment variables must be set before starting your application. To set these environment variables, add the following lines to the start-up script of your calling application, where $MAPLE is your Maple installation directory.
#!/bin/sh export MAPLE="/usr/local/maple" . $MAPLE/bin/maple -norun myapp $*
These commands run the maple launch script to configure your environment without starting Maple. The period (.) prefix in a Bourne shell causes the commands to be sourced, thus, applying the settings to future sessions. Starting the application would be done via the above script rather than calling the executable directly.
Interface Overview
Each OpenMaple application contains the following components:
a text callback to display or hide the output generated by Maple when an expression is evaluated
commands to initialize the Maple engine
calls to API commands to compute with Maple
The examples in this section show how to run a Maple computation using the OpenMaple API. Each example evaluates an expression and then exits. The examples in this section are intended to help you get started using the API. For detailed examples and descriptions, refer to the OpenMaple help page.
Text Callbacks
In each example, a text callback is defined. Output that would normally be displayed after an expression is evaluated in a Maple session is routed through callbacks. This output includes the following:
results from evaluated commands that are terminated with a semicolon
output from the print command, the printf command, and other display-related commands
userinfo and warning messages
diagnostic output from the debugger, high settings from printlevel, and trace output
error messages if no error callback is defined
resource status messages if no status callback is defined
displayed help pages
The text callback is not the only way to control output in your OpenMaple application. For more control, individual results can be converted to strings, which can be displayed in text boxes or directed in any way you want. When using this method of controlling output, the text callback can be defined as a procedure that does not perform any operations (that is, does not direct the output anywhere). Note that the Java example below uses the predefined EngineCallBacksDefault class, which configures a method to print output using the System.out method. In general, if the text callback is left undefined by setting it to 0 or null, the output is directed to the console.
Initializing the Maple Engine
You can initialize the Maple engine by calling the StartMaple function in most versions of the API, or by creating an Engine class in the Java version of the API. In all cases, the initialization process loads the maplec.dll file and sets up the initial state so that the OpenMaple interface can evaluate commands. Despite the name StartMaple, this is only an initialization step; no separate Maple process is started.
The initialization process follows standard Maple start-up steps, including reading and running initialization files, setting library paths, and setting default security options. The startup state can be controlled by using the first parameter passed to the StartMaple function. This parameter is an array of strings that specify options accepted by the command-line interface. For more information about these options, refer to the maple help page.
Calling API Commands to Compute with Maple
When the OpenMaple interface is initialized, a kernel vector handle (or engine class), can be used to access all of the other methods in the API. The most common method lets you parse and evaluate arbitrary Maple commands. If a command is terminated with a semicolon, the display output is directed to your defined callbacks. This command also returns the result as a Maple data structure. The return value can be passed to other API commands for further analysis.
The OpenMaple interface manages Maple internal data structures and performs garbage collection. The data structures that are returned by an API function are automatically protected from garbage collection. The Maple command unprotect:-gc must be called to clean the memory reserved for these tasks. The OpenMaple Java interface is the only exception to this rule. Because the OpenMaple Java interface implements Maple structures as native objects, it manages object references by using a weak hash map, and therefore Maple data does not need to be unprotected.
Maple data structures are all declared as a single black box ALGEB, IntPtr, or similar type. Methods for inspecting and manipulating these data structures are provided. The API methods should be used, rather than dereferencing them directly in the data.
C/C++ Example
#include <stdio.h> #include <stdlib.h> #include "maplec.h" /* callback used for directing result output */ static void M_DECL textCallBack( void *data, int tag, const char *output ) { printf("%s\n",output); } int main( int argc, char *argv[] ) { char err[2048]; /* command input and error string buffers */ MKernelVector kv; /* Maple kernel handle */ MCallBackVectorDesc cb = { textCallBack, 0, /* errorCallBack not used */ 0, /* statusCallBack not used */ 0, /* readLineCallBack not used */ 0, /* redirectCallBack not used */ 0, /* streamCallBack not used */ 0, /* queryInterrupt not used */ 0 /* callBackCallBack not used */ }; ALGEB r; /* Maple data-structures */ /* initialize Maple */ if( (kv=StartMaple(argc,argv,&cb,NULL,NULL,err)) == NULL ) { printf("Fatal error, %s\n",err); return( 1 ); } r = EvalMapleStatement(kv,"int(x,x);"); StopMaple(kv); return( 0 ); }
Additional examples are available in the samples/OpenMaple directory of your Maple installation.
The method used to build this program depends on which compiler you are using. The following command is specific to the GCC compiler on a 64-bit version of Linux; it is useful as a reference for other platforms.
gcc -I $MAPLE/extern/include test.c -L $MAPLE/bin.X86_64_LINUX -lmaplec -lmaple -lhf -lprocessor64
In this example, $MAPLE is your Maple installation directory. Note that the C header files can be found in the $MAPLE/extern/include directory and the library files can be found in the $MAPLE/bin.$SYS directory. In this case,$SYS is X86_64_LINUX; check the library path you need to specify by running the kernelopts(bindir) command in Maple. The remaining -l options specify which libraries need to be linked. In Windows, you only need to link to the maplec.lib library. Other platforms may require several libraries to be linked, including libmaplec.so, libmaple.so, and libhf.so. If you do not specify a library as required, the compiler returns a message indicating that undefined references to functions exist, or a dependent library cannot be found.
When this example is built, a file called test.exe is created. Note: The file might be called a.out or another name, depending on your compiler. Before this executable file can be run, you must specify the path of the Maple dynamic libraries. For more information, see Runtime Environment Prerequisites.
After setting up your environment, run the binary file as you would run any other executable file. For example, create a shortcut icon and double-click it, or enter the file name at a command prompt.
test.exe
The following output is displayed.
1/2*x^2
C# Example
using System; using System.Text; using System.Runtime.InteropServices; class MainApp { // When evaluating an expression, Maple sends all of the displayed // output through this function. public static void cbText( IntPtr data, int tag, String output ) { Console.WriteLine(output); } public static void Main(string[] args) { MapleEngine.MapleCallbacks cb; byte[] err = new byte[2048]; IntPtr kv; // pass -A2 which sets kernelopts(assertlevel=2) just to show // how in this example. The corresponding argc parameter // (the first argument to StartMaple) should then be 2 // argv[0] should always be filled in with a value. String[] argv = new String[2]; argv[0] = "maple"; argv[1] = "-A2"; // assign callbacks cb.textCallBack = cbText; cb.errorCallBack = null; cb.statusCallBack = null; cb.readlineCallBack = null; cb.redirectCallBack = null; cb.streamCallBack = null; cb.queryInterrupt = null; cb.callbackCallBack = null; try { kv = MapleEngine.StartMaple(2,argv,ref cb,IntPtr.Zero,IntPtr.Zero,err); } catch(DllNotFoundException e) { Console.WriteLine(e.ToString()); return; } catch(EntryPointNotFoundException e) { Console.WriteLine(e.ToString()); return; } // make sure we have a good kernel vector handle back if( kv.ToInt64() == 0 ) { // If Maple does not start properly, the "err" parameter will be filled // in with the reason why (usually a license error). // Note that since we passed in a byte[] array, we need to remove // the characters past \0 during conversion to string Console.WriteLine("Fatal Error, could not start Maple: " + System.Text.Encoding.ASCII.GetString(err,0,Array.IndexOf(err,(byte)0)) ); return; } MapleEngine.EvalMapleStatement(kv,"int(x,x);"); MapleEngine.StopMaple(kv); } }
To build this example, you can open a Microsoft .NET Framework SDK Command Prompt. Browse to the directory that contains the test.cs file and enter the following command.
csc test.cs $MAPLE\\extern\\include\\maple.cs
$MAPLE is the directory in which Maple is installed. The maple.cs file contains the MapleEngine class definition. and defines an interface to the maplec.dll file.
When this example is built, a file called test.exe is created. This file can usually be run without additional environment settings. For more information, see Runtime Environment Prerequisites.
Run the binary file as you would run any other executable file. For example, create a shortcut icon and double-click it, or enter the file name at a command prompt.
Java Example
import com.maplesoft.openmaple.*; import com.maplesoft.externalcall.MapleException; class test { public static void main( String args[] ) { String a[]; Engine t; int i; a = new String[1]; a[0] = "java"; try { t = new Engine( a, new EngineCallBacksDefault(), null, null ); t.evaluate( "int( x,x );" ); } catch ( MapleException e ) { System.out.println( "An exception occurred" ); return; } System.out.println( "Done" ); } }
This example and others are available in the samples/OpenMaple/Java/simple subdirectory of your Maple installation.
To build this program, enter the following at a command prompt, where $JDKBINDIR is the directory in which your Java development tools are installed, and $MAPLE is the directory in which Maple is installed.
$JDKBINDIR/javac -classpath "$MAPLE/java/externalcall.jar;$MAPLE/java/jopenmaple.jar" test.java
Note: The same command can be used to build the example in Macintosh; however, use a colon (:) to separate the directories in the classpath instead of a semicolon.
When this example is built, a test.class file is created in the current directory. Before this file can be run, the path of the Java OpenMaple native library must be specified for your Java Virtual Machine. For more information, see Runtime Environment Prerequisites. In Windows, Java OpenMaple applications also require the %PATH% environment variable to be set.
You can use the Java Virtual Machine to run the generated class file by entering the following command. Note that the third entry in the classpath is a period (.) indicating the current directory.
$JDKBINDIR/java -classpath "$MAPLE/java/externalcall.jar;$MAPLE/java/jopenmaple.jar;." test
1/2*x^2 Done
Visual Basic 6 Example
Public kv As Long Public cb As MapleCallBack Public Sub TextCallBack(data As Long, ByVal tag As Integer, ByVal output As Long) Dim OutputString As String OutputString = MaplePointerToString(output) MainForm.OutputText.Text = MainForm.OutputText.Text + vbCrLf + OutputString End Sub Private Sub Form_Load() Dim error As String Dim args(0 To 1) As String 'init callbacks cb.lpTextCallBack = GetProc(AddressOf TextCallBack) cb.lpErrorCallBack = 0 cb.lpStatusCallBack = 0 cb.lpReadLineCallBack = 0 cb.lpRedirectCallBack = 0 cb.lpQueryInterrupt = 0 cb.lpCallBackCallBack = 0 ' start Maple kv = StartMaple(0, args, cb, 0, error) If kv = 0 Then MsgBox "Error starting Maple: " + StrConv(error, vbUnicode), vbCritical, "" Unload Me End End If dim result as Long = EvalMapleStatement(kv, "int(x,x);" ) End Sub
Other examples are available in the samples/OpenMaple/msvb directory of your Maple installation.
To build this example, create a new project, and add both the test.bas and $MAPLE/extern/include/maple.bas files to your project. $MAPLE is the directory in which Maple is installed. Create a form called "MainForm" and add a text box named "OutputText" to the form.
Build and run this example by pressing F5. When this example is built, a form that shows a text box filled with the value 1/2*x^2 is displayed.
Visual Basic .NET Example
Friend Class MainForm Inherits System.Windows.Forms.Form Public kv As IntPtr Public cb As MapleCallBack Public Sub MyTextCallBack(ByRef data As Integer, ByVal tag As Short, ByVal output As String) tbOutput.Text = tbOutput.Text & vbCrLf & " (" & tag & ") " & output End Sub Public Sub MyErrorCallBack(ByRef data As Integer, ByVal Offset As Short, ByVal output As String) MsgBox(" at offset " & Str(Offset) & " " & output, MsgBoxStyle.Information, "") End Sub Private Sub MainForm_Load(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles MyBase.Load Dim args(1) As String 'init callbacks cb.lpTextCallBack = AddressOf MyTextCallBack cb.lpErrorCallBack = AddressOf MyErrorCallBack cb.lpStatusCallBack = 0 cb.lpReadLineCallBack = 0 cb.lpRedirectCallBack = 0 cb.lpQueryInterrupt = 0 cb.lpCallBackCallBack = 0 ' start Maple Try args(1) = "-A2" kv = StartMaple(1, args, cb, 0) Catch e As StartMapleException MsgBox("Error starting Maple: " & e.Message, "") Me.Close() End Try Dim result As IntPtr result = EvalMapleStatement(kv, "int( x,x );" ) If result = 0 Then tbOutput.Text = "invalid expression" Else tbOutput.Text = MapleToString(kv, MapleALGEB_SPrintf1(kv, "%a", result)) End If End Sub End Class
To build this example, create a new project, and add both the test.bas and $MAPLE/extern/include/maple.vb files to your project. $MAPLE is the directory in which Maple is installed. Create a form called "MainForm" and add a text box named "tbOutput" to the form.
Build and run the example by pressing F5. When this example is built, a form that shows a text box filled with the value 1/2*x^2 is displayed.
Memory Usage
Maple allocates memory from the operating system in large portions. On most platforms, this process is performed by the C malloc function; 64KB of memory is allocated during a Maple session. This memory is not returned to the operating system (that is, by free) until the Maple session ends.
When Maple no longer requires a block of memory, that memory block is added to an internal list of free storage. Maple has several storage lists for different sizes of memory blocks so that it can quickly find blocks of the required size. For example, Maple uses three-word blocks, so it maintains a list of blocks of that size that are free. Maple allocates additional memory from the operating system only when it cannot respond to a request using its storage lists.
The more memory Maple is allocated by the operating system, the less it is allocated in the future because it reuses memory. For most applications, 4MB of memory must be available for allocation.
14.4 The Maple Command-line Interface
When considering how to use the Maple engine as part of another application, interface, or automatic process, several options are available. One of the simplest options is to use the Maple command-line interface.
The command-line version of Maple is a simple interface that, when used interactively, displays an input prompt (>), runs commands, and displays the output as text-based results. You can use this interface in batch mode to direct input to an application, specify a text file to run, or evaluate a command using the -c option.
In Windows, the command-line interface is called cmaple.exe. You can run this file from either the bin.win or bin.X86_64_WINDOWS directory of your Maple installation, depending on your platform. On other platforms, you can start the command-line interface by running the maple script located in the bin directory of your Maple installation.
Starting the Maple command-line interface, automatically executing a command file, and stopping the Maple session can take about one tenth of a second, depending on which commands are run and the speed of your system. The quick start-up time and the minimal amount of processing required make the Maple command-line interface suitable to be called from other applications, even for quick calculations.
Batch Files
A batch file is a Maple script that can run in the Maple command-line interface, run statements, and exit. The results can be displayed or directed to a file.
One method of using the command-line interface to solve a problem is to create an .mpl script, which includes the input data. If this script is called solve.mpl, you could run the script as follows.
cmaple solve.mpl > solve.output
In this example, the output is redirected to a file named solve.output. You can configure an application to read this output file to capture the result.
Note: .mpl is the standard file extension for a Maple language file, which is a text file that can contain Maple statements. For more information, refer to the file help page.
You can use the -q option to hide extra output that interferes with parsing results automatically. For more options on the Maple command-line interface, refer to the maple help page.
Directing Input to a Pipeline
To avoid using the file system, input to the command-line interface can be directed to a pipeline. The following example shows how to perform this task at a command prompt.
echo "int(x,x);" | cmaple
Specifying Start-up Commands
You can use the -c option to specify start-up commands to be run by the Maple command-line interface. Although the -c option can be followed by any valid Maple statement, the syntax must be carefully quoted to avoid being interpreted by the shell of the calling system. For example, the following syntax can be entered at a Windows command prompt.
cmaple -c "datafile := `c:/temp/12345.data`" -c N:=5;
The equivalent command in a UNIX shell requires different quoting.
/usr/local/maple/bin/maple -c 'datafile:="/tmp/12345.data";' -c N:=1;
Statements that do not use characters that are special to the system interpreter can be left unquoted, as with the case of -c N:=1; above. This statement does not use spaces, pipe characters, redirect symbols, quotes, or other characters that have meaning to the interpreter.
14.5 External Calling: Using Compiled Code in Maple
In Maple, you can load a dynamic-link library (.dll) file that contains functions written in a programming language other than Maple, and then use those functions in Maple as you would use any other commands.
External functions that accept and return basic types (for example, integers and floats) can be called directly in Maple after defining the calling sequence of the external function. Alternatively, if you want to call functions that use complicated types, or if you require more control over the conversion of data structures you want to access, you can use the Maple external function interface to create and compile a wrapper file.
Calling a Function in a Dynamic-link Library
Most external functions that are compiled in a .dll file use standard hardware types such as integers, floating-point numbers, strings, pointers (to strings, integers, and floating-point numbers), matrices, and vectors. Maple can translate the hardware representation of these external functions so that the external functions are recognized in Maple. This method is efficient and easy to use because it does not require the use of a compiler. This method of directly calling the external code allows you to use an external library without modifying the Maple library.
To understand the Maple external calling facility, consider the following C code, which adds two numbers and returns the result.
int add( int num1, int num2 ) { return num1+num2; }
Three basic steps are required to call a function in a .dll library.
Create or obtain a .dll file
Create a function specification in Maple
Call the external function from within Maple
Create or Obtain a .dll file
The external functions that you want to call from Maple must be compiled in a .dll file. You can either create the code and compile the .dll file yourself or obtain an existing .dll file that contains the functions you want to use in Maple.
The external library functions in a .dll file must have been compiled using the _stdcall calling convention, which is the default convention used by most compilers in Macintosh and 64-bit Windows, but must be specified when using most 32-bit Windows compilers. Options are also available for calling .dll files created by Fortran compilers and classes created in Java.
Create a Function Specification
Before using an external function in Maple, you must provide a description (or function specification), which includes the following information.
Name of the function in the .dll file. In the example above, the name is add.
Type of parameters the function passes and returns. In the example above, all of the parameters are of type int.
Name of the .dll file that contains the function. In the example above, assume that the C code has been compiled into a .dll file called mylib.dll.
A function specification translates the external function into a form that can be recognized and interpreted by Maple.
At a Maple prompt, you can define the function specification by calling define_external as follows.
myAdd := define_external( 'add', 'num1'::integer[4], 'num2'::integer[4], 'RETURN'::integer[4], 'LIB'="mylib.dll" );
Examine this function and note the following characteristics.
The first argument of the define_external function (in this example, add) is the name of the external function as exported by the .dll file. In the C code, the function is called add. However, because the define_external function is assigned to the name myAdd above, the Maple procedure that will be generated after you define the function specification will be called myAdd. This is the command that you will use to call the external function within Maple.
If Java or Fortran was used to create the external function, you must specify JAVA or FORTRAN as the second argument to indicate the programming language. The default language is C, so this parameter does not need to be specified in the example above since the add function was written in C.
The parameters num1 and num2, which are both of type int, describe the arguments of the function to be called. These values should be specified in the order in which they appear in your documentation or source code for the external function, regardless of issues such as the passing order (left to right versus right to left). By doing so, the Maple procedure returned by the define_external function has the same calling sequence as the external function when it is used in the language for which it was written. The only exception is that one argument can be assigned the name RETURN. This argument specifies the type returned by the function rather than a parameter passed to the function. In the example above, the return type does not have a name, so the keyword RETURN is used.
For information on specifying parameter types, see Specifying Parameter Types for Function Specifications.
Specifying the parameter types is independent of the compiler. The specification is always defined in the same way, regardless of the method used to compile the .dll file. The example above uses the C type int, which is specified as integer[4] in Maple. The 4 in the square brackets indicates the number of bytes used to represent the integer. Some C compilers use 4-byte (32-bit) long data types, but other compilers use 8-bytes (64-bit) for the same data structure. If you are using the long data type, the specification in Maple will need to be either integer[4] or integer[8], depending on the way your .dll file was built. For more information about common type relations, see Table 14.2.
The name of the .dll file containing the external function is specified by defining the LIB parameter. In the example above, mylib.dll specifies the file name of the library in which the function is located. The format of this name is system-dependent and certain systems require a full path to the file. In general, the name should be in the same format as you would specify for a compiler on the same system. If you are calling a Java method, dllName is the name of the class containing the method.
Important: Specify the function exactly and make sure that the arguments are in the correct order. Failure to do this will not cause any problems when you are defining the specification; however, unexpected results may be produced or your program may stop responding when the external function is called within Maple.
Calling the External Function
Calling the define_external function for myAdd returns a Maple procedure that translates the Maple types to hardware types that can work with an external function. This procedure can be used in the same way as other Maple commands.
myAdd(1,2);
3
a := 33:
b := 22:
myAdd(a,b);
55
r:= myAdd(a,11);
r ≔ 44
Specifying Parameter Types for Function Specifications
Maple uses its own notation to provide a generic well-defined interface for calling compiled code in any language. The format of each type descriptor parameter is as follows.
argumentIdentifier :: dataDescriptor
The return value description is also defined by using a data descriptor, with the name RETURN as the argumentIdentifier. If the function returns no value, no RETURN parameter is specified. Also, if no parameters are passed, no argument identifiers are required.
Scalar Data Formats
External libraries generally handle scalar data formats that are supported by your platform. All array, string, and structured formats are created from these. The data descriptors used to represent scalar formats usually contain a type name and size. The size represents the number of bytes needed to represent the given hardware type. Table 14.1 lists the basic type translations for standard C, Fortran, and Java compilers.
Maple Data Descriptor
C Type
Fortran Type
Java Type
integer[1]
char
BYTE
byte
integer[2]
short
INTEGER2
integer[4]
int or long^1
INTEGER or INTEGER4
int
integer[8]
long^1 or long long
INTEGER8
long
float[4]
float
REAL or REAL4
float[8]
double
DOUBLE PRECISION or REAL8
char[1]
CHARACTER
boolean[1]
LOGICAL1
boolean
boolean[2]
LOGICAL2
boolean[4]
LOGICAL or LOGICAL4
boolean[8]
LOGICAL8
Note: The C type long is typically 4 bytes on 32-bit systems and 4 or 8 bytes on 64-bit systems. Use the sizeof operator or consult your compiler documentation to verify sizeof(long).
Structured Data Formats
In addition to the basic types listed in Table 14.1, Maple also recognizes certain compound types that can be derived from the basic types, such as arrays and pointers. These compound types are listed in Table 14.2. For a complete list and a detailed specification, refer to the define_external,types help page.
ARRAY( datatype = float[8], ... )
type A
type[] A
string[n]
char x[n]
CHARACTER2
string
complex[4]
struct{ float re, im; }
COMPLEX or COMPLEX8
NA
complex[8]
struct{ double re, im; }
DOUBLE COMPLEX or COMPLEX16
REF(typename)
TYPENAME
External Function Interface
Alternatively, you may want to call a .dll file that directly manipulates Maple data structures, rather than converting them automatically to standard data types. By doing so, you can either write custom applications that are integrated with Maple or provide custom conversions for data passed to prebuilt .dll files. Maple provides an API for directly managing Maple data structures and operations performed on them.
This API, or external function interface, is a subset of the API provided by the OpenMaple interface. Unlike the OpenMaple interface, you do not need to define stream callbacks because Maple is the primary interface. Also, the kernel-vector handle returned from a call to the StartMaple function in the OpenMaple API is, instead, passed as an argument to the external function defined in your .dll file.
Currently, the API is defined for C/C++ and Fortran, and certain portions of the API can be used for external functions written in Java. Other languages such as Visual C# and Visual Basic can interface through a small C++ layer.
The API function prototypes for manipulating Maple data structures are located in the $MAPLE/extern/include directory where $MAPLE is the directory in which Maple is installed. The header file maplec.h must be included when writing custom C wrappers. One of the header files, maplefortran.hf or maplefortran64bit.hf, must be included when writing custom Fortran wrappers. Other header files, mplshlib.h, and mpltable.h contain macros, types, and data structures that are needed for direct manipulation of Maple data structures.
In your C code, Maple uses the following lines as an entry point to call the external function directly with no argument conversion.
ALGEB myExternalFunction( MKernelVector kv, ALGEB args );
Two parameters are in the external function declaration. The first is a handle that will be required to call any Maple API function. The second is a Maple expression sequence of all the arguments passed when the external function is called. The API function MapleNumArgs can be used to determine the number of elements in the expression sequence. This variable can be treated as an array of DAGs starting at index 1 (not 0). Therefore, args[1] is the first parameter passed to the external function.
myFunc := define_external('myExternalFunction', 'MAPLE', 'LIB'= "myStuff.dll"):
When using the define_external function to declare an interface to an external function that directly manipulates Maple structures, you do not need to provide a description of the arguments and their types. Instead, add the keyword option, MAPLE.
Again, consider the simple example that adds two numbers passed by Maple. This time, with explicit data type conversions using the API, and defining the external function prototype, as described above, the C function appears as follows.
/* Program to add two numbers from Maple */ #include <stdio.h> #include <stdlib.h> #include <maplec.h> ALGEB myAdd( MKernelVector kv, ALGEB args ) { int a1, a2, r; if( MapleNumArgs(kv,args) != 2 ) MapleRaiseError(kv,"Incorrect number of arguments"); a1 = MapleToInteger32(kv,((ALGEB*)args)[1]); a2 = MapleToInteger32(kv,((ALGEB*)args)[2]); r = a1 + a2; return( ToMapleInteger(kv,(M_INT) r) ); }
This program first checks if the Maple function call passes exactly two arguments. It then converts the two arguments to hardware integers and adds them. The result is converted to a Maple integer and returned.
This program can be compiled into a .dll file using a C compiler of your choice. Ensure that you link with the Maple API .dll file. The .dll file can be placed in the Maple binary directory, as given by kernelopts(bindir), or a subdirectory within the directory specified by the PATH environment variable. If you are using .dll files outside of the Maple binary directory, you may need to specify the full path to the .dll file in the LIB argument to the define_external function.
To complete the example, the myAdd function can be linked in Maple and used as any other Maple procedure.
myAdd := define_external('myAdd', 'MAPLE', 'LIB'= "myAdd.dll"):
myAdd(2,3);
5
myAdd(2.2,1);
Error, (in myAdd) integer expected for integer[4] parameter
myAdd(2^80,2^70);
Error, (in myAdd) integer too large in context
The equivalent Fortran wrapper is as follows.
Program to add two numbers from Maple INTEGER FUNCTION myAdd(kv, args) INCLUDE "maplefortran.hf" INTEGER kv INTEGER args INTEGER arg INTEGER a1, a2, r CHARACTER ERRMSG*20 INTEGER ERRMSGLEN ERRMSGLEN = 20 IF ( maple_num_args(kv, args) .NE. 2 ) THEN ERRMSG = 'Incorrect number of arguments' CALL maple_raise_error( kv, ERRMSG, ERRMSGLEN ) myAdd = to_maple_null( kv ) RETURN ENDIF arg = maple_extract_arg( kv, args, 1 ) a1 = maple_to_integer32(kv, arg) arg = maple_extract_arg( kv, args, 2 ) a2 = maple_to_integer32(kv, arg) r = a1 + a2 myAdd = to_maple_integer( kv, r ) END
Once compiled into a .dll file, the same syntax can be used in Maple to access the function. The only difference is the additional keyword 'FORTRAN' in the define_external call.
myAdd := define_external('myAdd','MAPLE','FORTRAN','LIB'= "myAdd.dll"):
For more examples, refer to the define_external,CustomWrapper help page.
Specifying Parameter Passing Conventions
Each programming language uses a specific convention for parameter passing. For example, C uses the pass-by-value convention; passing parameters by reference must be performed explicitly by passing an address. Fortran uses the pass-by-reference convention. Pascal uses either, depending on how the parameter was declared.
The Maple external calling mechanism supports C, Fortran, and Java calling conventions. There is an external API for writing custom wrappers for C and Fortran, but not for Java. The default convention used is C. To use Fortran calling conventions, specify the name FORTRAN as a parameter to the define_external function.
f := define_external(`my_func`,`FORTRAN`, ...);
To use Java calling conventions, specify the name JAVA as a parameter to the define_external command. Also, specify the CLASSPATH= option to point to the classes used.
f := define_external(`my_func`,`JAVA`, CLASSPATH="...", ...);
Some other compiler implementations, such as Pascal and C++, can work with C external calling by using the correct definitions and order of passed parameters.
Generating Wrappers Automatically
When you specify the keyword WRAPPER in the call to the define_external function, Maple generates code for data translations. Maple compiles this code into a .dll file and dynamically links to the new library. Subsequently invoking the procedure that is returned by the define_external function calls the newly generated conversion command before calling the external function in the library you provided.
The C code generated by Maple wraps the Maple data structures by translating them to hardware equivalent types. Therefore, the code file is called the wrapper, and the library generated by this code is called the wrapper library.
Generating wrappers can provide an easy way to start writing custom code that references the Maple external function interface. The term wrapper also refers to the code you write to communicate with existing .dll files, as it does for the code Maple generates for the same reason. Your code is sometimes called a custom wrapper.
Consider the original add function that was introduced at the beginning of this chapter. In the following example, the WRAPPER option is used to generate a wrapper using the define_external function. As shown, you can also use the NO_COMPILE option to prevent the generated wrapper from compiling. The name of the generated file is returned instead.
myAdd := define_external( 'add', 'WRAPPER', 'NO_COMPILE', 'num1'::integer[4], 'num2'::integer[4], 'RETURN'::integer[4] );
myAdd := "mwrap_add.c"
The file mwrap_add.c resembles the following.
/* MWRAP_add Wrapper Generated automatically by Maple Do not edit this file. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <mplshlib.h> #include <maplec.h> MKernelVector mapleKernelVec; typedef void *MaplePointer; ALGEB *args; /* main - MWRAP_add */ ALGEB MWRAP_add( MKernelVector kv, INTEGER32 (*fn) ( INTEGER32 a1, INTEGER32 a2 ), ALGEB fn_args ) { INTEGER32 a1; INTEGER32 a2; INTEGER32 r; ALGEB mr; int i; mapleKernelVec = kv; args = (ALGEB*) fn_args; if( MapleNumArgs(mapleKernelVec,(ALGEB)args) != 2 ) MapleRaiseError(mapleKernelVec,"Incorrect number of arguments"); /* integer[4] */ a1 = MapleToInteger32(mapleKernelVec,args[1]); /* integer[4] */ a2 = MapleToInteger32(mapleKernelVec,args[2]); r = (*fn)(a1, a2); mr = ToMapleInteger(mapleKernelVec,(long) r); return( mr ); }
The generated wrapper is a good starting point for writing your own code. Some extra variables and declarations may be used because the wrapper generation is generic. For example, the use of args rather than fn_args avoids the need for a cast with args[1], but it is also a static global variable which is useful when working with callbacks that need access to the argument sequence outside the main entry point.
Passing Arguments by Reference
External functions follow normal Maple evaluation rules in that the arguments are evaluated during a function call. It therefore may be necessary to enclose assigned names in right single quotes (unevaluation quotes) when passing arguments by reference. For example, consider the following function that multiplies a number by two in-place.
void double\_it( int *i ) { if( i == NULL ) return; *i *= 2; }
In Maple, the definition of this function could appear as follows.
double_it := define_external('double_it', i::REF(integer[4]), LIB="libtest.dll");
When running this function, the argument 'i' is converted from the Maple internal representation of an integer to a 4-byte hardware integer. A pointer to the hardware integer is then passed to the external function, 'double_it'. Although 'i' is declared as a pointer to an integer, you can call 'double_it' with non-pointer input.
double_it(3);
In this case, a pointer to the hardware integer 3 is sent to 'double_it'. The modified value is not accessible from Maple. To access the modified value, the parameter must be assigned to a name. The name must be enclosed in unevaluation quotes to prevent evaluation.
n:=3;
double_it(n); # n is evaluated first, so 3 is passed
n;
double_it('n'); # use unevaluation quotes to pass 'n'
6
For numeric data, the string "NULL" can be passed as a parameter to represent the address 0 (the C NULL). For strings, because "NULL" is a valid string, the integer 0 represents the address 0.
double_it("NULL"); concat := define_external('concat', RETURN::string, a::string, b::string, LIB="libtest.dll"): concat("NULL","x");
NULLx
concat(0,0);
0
In the concat example, the C code might look like the following. Note that this function does not clean the memory as it should.
char * concat( char* a, char *b ) { char *r; if( !a \\ !b ) return( NULL ); r = (char*)malloc((strlen(a)+strlen(b)+1)*sizeof(char)); strcpy(r,a); strcat(r,b); return( r ); }
External API
An external API is provided if you want to expand existing wrappers or write custom wrappers. Because this API is the same as that of OpenMaple, most of the internal documentation is referenced in the OpenMaple help pages. In particular, refer to the OpenMaple,C,API and OpenMaple,VB,API help pages and related pages.
Additional examples can be found in the examples,ExternalCalling page. Sample code is provided in the samples/ExternalCall directory of your Maple installation. In particular, all of the external calling sample code provided in the individual help pages in OpenMaple,C,API can be found in the samples/ExternalCall/HelpExamples directory. This code is precompiled in the HelpExamples.dll file provided with Maple so that you can run the examples in your Maple session.
System Integrity
The Maple kernel cannot control the quality or reliability of external functions. If an external function performs an illegal operation, such as accessing memory outside of its address space, that operation may result in a segmentation fault or system error. The external function may stop responding and cause Maple to stop responding as well.
If an external function accesses memory outside of its address space but within the Maple address space, the external function will likely not stop responding, but certain parts of Maple may not function correctly, resulting in unexpected behavior or a crash later in the Maple session. Similarly, an external function that directly manipulates Maple data structures can produce unexpected results by misusing the data structure manipulation facilities.
Therefore, use external calling at your own risk. Whether an external function is one that you have written, or supplied by a third party to which you have declared an interface (that is, by using the define_external function), Maple must rely on the integrity of the external function when it is called.
14.6 Accessing Data over a Network with TCP/IP Sockets
The Sockets package allows Maple to communicate with data sources over the Internet, such as web sites and remote Maple sessions running on a network.
You can create a Maple server in a Maple session and configure the server to send a message to that session.
Socket Server
A socket server can be a public web service such as a stock quote service. The following example shows how to create a Maple procedure that acts as a service that listens for a socket connection and sends a message when a connection is found.
The server action is defined in the following procedure.
myServer := proc( sid ) uses Sockets; Write( sid, sprintf( "Hello %s on port %d, from %s\r\n", GetPeerHost( sid ), GetPeerPort( sid ), GetHostName() ) ) end proc:
The following commands cause the Maple session in which they are called to start the servicing requests. This call is not returned. To run the code, enter the procedure definition above and the Serve command below in a Maple worksheet. (Remove the comment character (#) before running the code.)
#Sockets:-Serve( 2525, myServer );
Socket Client
To write a simple client, the socket must first be opened. To do so, specify the name of the host and the port number of the host to which you want to connect.
sid := Sockets:- Open ( "localhost", 2525 );
To get information from the server, the socket must be read.
Sockets:-Read(sid);
When you are finished, close the socket.
Sockets:-Close(sid);
14.7 Code Generation
In Maple, code generation is one of several powerful tools for deploying results to other systems. Maple can translate formulas, numerical procedures, data sets, and matrices to compiled languages. Maple supports translation to C, C#, MATLAB®, Java, JavaScript, Perl, Python, R, Visual Basic, and Fortran.
with( CodeGeneration );
C,CSharp,Fortran,IntermediateCode,Java,JavaScript,Julia,LanguageDefinition,Matlab,Names,Perl,Python,R,Save,Swift,Translate,VisualBasic
Calling CodeGeneration Commands
You can call the CodeGeneration commands using the following syntax, where L is one of the supported languages, for example, C.
CodeGeneration[*L*]( *expression*, *options* )
The expression can take one of the following forms.
A single algebraic expression: Maple generates a statement in the target language assigning this expression to a variable.
A list of equations of the form *name*=*expression*: Maple interprets this list as a sequence of assignment statements and generates the equivalent sequence of assignment statements in the target language.
A list, array, or rtable: Maple generates a statement or sequence of statements assigning the elements to an array in the target language.
A Maple procedure or module: Maple generates an equivalent structure in the target language. For example, to translate a procedure to C, Maple generates a function along with any necessary directives for library inclusion. To translate a module to Java, Maple generates a Java class declaration with exports translated to public static methods and module locals translated to private static methods. For more information on translating code to a specific language, refer to the CodeGeneration help page and browse to the help page for the target programming language that you want to use.
You can use many options with the CodeGeneration commands. For more information, refer to the CodeGenerationOptions help page. Some of the commonly used options are listed below.
optimize=value: This option specifies whether the Maple code is optimized before it is translated. The default value is false. When this option is set to true, the codegen[optimize] function is used to optimize the Maple code before it is translated.
output=value: This option specifies the form of the output. By default, the formatted output is printed to the console. If a name (different from the name string) or a string is specified as the value, the result is appended to a file of that name. If the value is the name string, a string containing the result is returned. This string can then be assigned and manipulated.
declare=[declaration(s)]: This option specifies the types of variables. Each declaration has the form varname::vartype where varname is a variable name and vartype is one of the Maple type names recognized by the CodeGeneration package, as described in the TranslationDetails help page. Declarations specified using this option override any other type declarations in the input code.
Notes on Code Translation
Because the Maple programming language differs from the target languages supported by the CodeGeneration package, the generated output may not be completely equivalent to the input code. The CodeGeneration/Details help page provides more information on the translation process and hints on how to take full advantage of the facilities. In addition, some help pages contain notes that are relevant to specific languages. For more information, refer to the help pages for the corresponding target language, for example, CodeGeneration/General/CDetails.
Translation Process
The CodeGeneration commands recognize a subset of the Maple types, which are listed in the CodeGeneration/Details help page. The Maple types are translated to appropriate types in the target language. Compatibility of types is checked before operations are translated, and type coercions are performed if necessary. The CodeGeneration commands attempt to determine the type of any untyped variables. You can control type analysis and deduction by using the coercetypes, declare, deducetypes, and defaulttype options. For more information, refer to the CodeGenerationOptions help page.
The CodeGeneration commands can translate a subset of the Maple commands, which are listed on the CodeGeneration/Details help page. Some commands are translated only to certain target languages. For more information about a specific language, refer to its detailed help page, for example, CodeGeneration/General/CDetails.
The return type of a procedure is determined automatically if you do not declare it. If more than one return statement is specified, the types of all objects returned must be compatible in the target language. If a return statement contains a sequence of objects, the sequence is translated into an array. Implicit returns are recognized in some cases, but translations to explicit returns can be suppressed by using the deducereturn=false option. When necessary, an automatically generated return variable is used to store a return value.
Lists, Maple data structures of the type array, and rtables are translated to arrays in the target languages such as C which do not have a built-in list type. It is recommended that you declare the type and ranges for all arrays. In some target languages, arrays are reindexed to begin at index 0.
Example 1: Translating a Procedure to Java
f := proc(x) local y; y := ln(x)*exp(-x); printf("The result is %f", y); end proc:
CodeGeneration[Java](f);
import java.lang.Math; class CodeGenerationClass { public static void f (double x) { double y; y = Math.log(x) * Math.exp(-x); System.out.print("The result is " + y); } }
Example 2: Translating a Procedure to C
In this example, the defaulttype option sets the default type to integer and the output option specifies that a string is returned. In this case, the output is assigned to the variable s.
g := proc(x, y, z) return x*y+x*z-y*z; end proc:
s := CodeGeneration[`C`](g, defaulttype=integer, output=string);
s≔int g (int x, int y, int z) { return(y * x + x * z - y * z); }
Example 3: Translating a Procedure to Fortran
In this example, because Fortran 77 is not case-sensitive, the variable X is renamed to avoid a conflict with the variable x.
h := proc(X::numeric, x::Array(numeric, 5..7)) return X+x[5]+x[6]+x[7]; end proc:
CodeGeneration[Fortran](h);
Warning, the following variable name replacements were made: x -> cg
doubleprecision function h (X, cg) doubleprecision X doubleprecision cg(5:7) h = X + cg(5) + cg(6) + cg(7) return end
Example 4: Translating an Expression to MATLAB®
In this example, the optimize option is used to minimize the number of arithmetic operations called in the exported code. The default exported code would have recomputed a value for (3 - c * b + a * b), which appears many times. To avoid recomputing the value, common subexpressions are evaluated once and stored in variables so that other expressions can refer to the value.
M := 1 / < a,3,c; 1,b,2; -1,0,-1 >;
M≔
CodeGeneration:-Matlab(M, optimize = true);
t1 = -729 + 22 * c; t1 = 1 / t1; t2 = 22 * t1; t3 = 3 * t1; cg = [-t2 t3 (-22 * c + 6) * t1; -t1 (c - 33) * t1 (c - 66) * t1; t2 -t3 723 * t1;];
Example 5: Translating an Expression to Perl
In this example, the fact that the Perl language has a built-in primitive for list construction is exploited to provide a natural translation of the Maple input.
f := proc(L::list) local x, s := ""; for x in L do s := cat(s, x); end do; end proc:
CodeGeneration[Perl](f);
#!/usr/bin/perl sub f { local($L) = @_; local($x, $s, $cgret); $s = ""; foreach $x (@{$L}) { $s = $s . $x; $cgret = $s; } return($cgret); }
Example 6: Translating an Expression to Python
In addition to the core Python language, the output of the Python translator includes references to several standard Python libraries used in scientific computation. In this example, Maple input making use of the number-theoretic function ithprime is translated to an equivalent using the Python sympy package.
AddFirstMPrimes := m->add( ithprime(i), i = 1..m ):
Warning, (in AddFirstMPrimes) `i` is implicitly declared local
CodeGeneration:-Python( AddFirstMPrimes );
import sympy def AddFirstMPrimes (m): r = 0 for i in range(1, m + 1): r = r + sympy.prime(i) return(r)
Example 7: Translating Commands to R
When a statistics command outside of a procedure is translated to R, unevaluation quotes are required so that the commands are not automatically simplified. When translating statistics commands that occur inside of a procedure, the use of unevaluation quotes is optional.
CodeGeneration:-R(Statistics:-Mean([1,3,5,7]));
cg0 <- 0.4e1
CodeGeneration:-R('Statistics:-Mean'([1,3,5,7]));
cg1 <- mean(c(1,3,5,7))
f := proc( x ) return sprintf("Mean = %.1f\n Standard Deviation = %.1f", Statistics:-Mean(x), 'Statistics:-StandardDeviation'(x) ); end proc:
CodeGeneration:-R( f );
f <- function(x) return(sprintf("Mean = %.1f\n Standard Deviation = %.1f", mean(x), sd(x)))
Example 8: Translating a Procedure to Visual Basic
In the following example, all of the parameters are assigned a floating-point type by default. The defaulttype option determines how to interpret variables that do not have a type.
f := proc(x, y, z) return x*y+x*z-y*z; end proc:
CodeGeneration:-VisualBasic(f,defaulttype=integer);
Public Module CodeGenerationModule Public Function f( _ ByVal x As Integer, _ ByVal y As Integer, _ ByVal z As Integer) As Integer Return y * x + x * z - y * z End Function End Module
Example 9: Using the defaulttype and deducetypes Options
Maple attempts to determine the types of variables that do not have a type automatically. The default type is assigned only to those variables that do not have a type after the automatic type deduction process. In the following example, the parameters y and z are assigned a floating-point type because they are in an expression involving the float variable x. Therefore, the default type, integer, is not assigned.
f := proc(x::float, y, z) x*y+x*z-y*z; end proc:
CodeGeneration:-C( f, defaulttype=integer );
double f (double x, double y, double z) { return(y * x + x * z - y * z); }
You can turn off the automatic type deduction process by setting the deducetypes option to false. In the following example, the parameters y and z are now assigned the default type.
CodeGeneration:-C(f, defaulttype=integer, deducetypes=false);
double f (double x, int y, int z) { return((double) y * x + x * (double) z - (double) (y * z)); }
You can turn off the explicit type coercion process by setting the coercetypes option to false.
CodeGeneration:-C(f, defaulttype=integer, deducetypes=false, coercetypes=false);
double f (double x, int y, int z) { return(y * x + x * z - y * z); }
Example 10: Using the declare Option
You can control how types are assigned by specifying the parameter, local variable, and return types explicitly in procedures or by using the declare option with expressions.
CodeGeneration:-C(1+x+y, declare=[x::float, y::integer]);
cg2 = 0.1e1 + x + (double) y;
The Intermediate Code
All Maple input to the CodeGeneration translators is processed and converted to an inert intermediate form called intermediate code. The intermediate code is the basic object on which all CodeGeneration translators operate. For information about the intermediate code, refer to the CodeGeneration/General/IntermediateCodeStructure help page.
The names that appear in intermediate code expressions are part of the CodeGeneration:-Names subpackage.
Error and warning messages displayed from CodeGeneration package commands sometimes refer to the intermediate form of the Maple expression that triggered the message.
When determining the cause of an error message or writing and debugging custom language definitions, it is recommended that you determine the intermediate form of a Maple expression input. In general, you can determine the intermediate form by using the CodeGeneration:-IntermediateCode translator. However, because some aspects of the intermediate code are specific to the language to which you are translating, it may help to see the intermediate code for a specific translator. This can be done by setting the command infolevel[CodeGeneration] to a value greater than 3 and performing a translation.
The following example shows the intermediate code for the expression 2x^2-1. The first argument of the Scope structure is the name of a type table used internally during the translation process.
CodeGeneration[IntermediateCode](2*x^2-1);
Scope( nametab, StatementSequence( Assignment(GeneratedName("cg3"), Sum(Product(Integer(2), Power(Name("x"), Integer(2))), Negation(Integer(1)))) ) )
Extending the CodeGeneration Translation Facilities
The CodeGeneration package is distributed with translators for several programming languages. In addition, you can define new translators to enable the CodeGeneration package to generate code for other languages. Tools for this task are available in the LanguageDefinition subpackage of CodeGeneration.
Custom translators can define a complete language, extend existing language definitions, overriding and extending only those language components that need to be changed.
To view a list of languages that are currently supported by the CodeGeneration package, and thus available for extending, use the CodeGeneration:-LanguageDefinition:-ListLanguages command.
The Printing Phase
As described previously, the CodeGeneration package first processes the Maple input and translates it to an intermediate form. This step is followed by the printing phase, which translates the intermediate form to a Maple string according to transformation rules specific to the target language.
For each name used in the intermediate form, there is a print handler procedure. During the printing phase, Maple traverses the intermediate form recursively. For each subexpression of the intermediate form, Maple invokes the print handler associated with that class of expressions.
Defining a Custom Translator
This section explains the process of defining a translator for a target language.
Using a Printer Module
For each CodeGeneration language definition, there is an associated Maple module called a Printer module, which contains language-specific print handlers and data. A Printer module has several functions, which set and reference language-specific printing data.
There are two ways to obtain a Printer module: the LanguageDefinition:-GenericPrinter() returns a generic Printer module containing no language-specific data, and the LanguageDefinition:-Get(language_name):-Printer command returns a copy of the Printer module used for a previously defined language language_name.
The most frequently used Printer package command is Print. When it is given a string, the Print command prints the string to a buffer. When given an intermediate-form expression, the Print command invokes the print handler appropriate for the expression. In this manner, Print recurses through the intermediate form until it is printed in its entirety to the buffer. At this point, the translation process is complete.
Table 14.3 lists the important Printer commands. For a complete list and more detailed information, refer to the CodeGeneration/LanguageDefinition/Printer help page.
AddFunction
Define a translation for a command name and type signature
AddOperator
Define a translation for a unary or binary operator
AddPrintHandler
Set a procedure to be the print handler for an intermediate form name
GetFunction
Get a translation for a command name and type signature
GetOperator
Get a translation for a unary or binary operator
GetPrintHandler
Get the current print handler' procedure for an intermediate form name
Indent
Indent a printed line when supplied as an argument to Print
Print
Print arguments to buffer
PrintTarget
Initiate printing of an intermediate form
The following commands illustrate how data is stored and retrieved from a Printer module.
with(CodeGeneration:-LanguageDefinition):
Printer := GenericPrinter():
Printer:-AddOperator( Addition = "+" );
+
Printer:-AddFunction( "sin", [numeric]::numeric, "sine" );
sine,∅
Printer:-GetOperator( Addition );
Printer:-GetFunction( "sin", [numeric]::numeric );
sine,∅,Names:−FunctionCall
Within a language definition, the Printer module associated with the language definition can be referenced by the name Printer. Note: This applies to both of the language definition methods described in the next section.
Language Translator Definition
There are two distinct methods of defining a language translator for use by the CodeGeneration package: using the LanguageDefinition:-Define command and creating a language definition module.
For simple languages or small extensions of existing languages, use the LanguageDefinition:-Define command. To create a translator that preprocesses or postprocesses the generated output, or makes frequent use of a utility function in translations, create a language definition module. The language definition module approach is used for all translators supplied with the CodeGeneration package.
Using the Define Command
The Define command takes a series of function call arguments f1, f2, ... where the command names are, for example, AddFunction, AddFunctions, AddOperator, AddPrintHandler, AddType, and SetLanguageAttribute.
These function calls accept identical syntax and perform the same actions as the Printer commands of the same name. That is, they define print handlers and other data specific to the language translation you are defining. For more information on these commands, refer to the CodeGeneration/LanguageDefinition/Printer help page.
The Define command automatically creates a Printer module for the language. You do not need to create one using the LanguageDefinition:-GenericPrinter or LanguageDefinition:-Get commands.
This example illustrates a C translator, in which the translated code uses a specialized library function mymult for multiplication instead of the built-in * operator.
CodeGeneration:-LanguageDefinition:-Define("MyNewLanguage", extend="C", AddPrintHandler( CodeGeneration:-Names:-Product = proc(x,y) Printer:-Print("mymult(", args[1], ", ", args[2], ")"); end proc ) ):
Note that in the previous example, one of the arguments of the LanguageDefinition:-Define command is the function call AddPrintHandler, which takes a name and a procedure as arguments. The supplied procedure therefore prints any Product subexpression of the intermediate form. The call to Printer:-Print specifies that the translator uses the automatically generated Printer module.
Creating a Language Definition Module
A language definition module is a Maple module with the exports PrintTarget and Printer. The module exports must satisfy the following criteria.
Printer: A Printer module, that is, either a generic Printer module returned by the CodeGeneration:-LanguageDefinition:-GenericPrinter command or a Printer module obtained from another language definition module using the LanguageDefinition:-Get("language_name"):-Printer command.
PrintTarget: Returns the translated output as a string. In most cases, the PrintTarget command calls the Printer:-PrintTarget command.
The body of the module definition must contain a sequence of calls to Printer commands that define language-specific data and utility procedures.
Once defined, a language definition module can be added to to the set of languages recognized by the CodeGeneration package by using the CodeGeneration:-LanguageDefinition:-Add command.
When creating your language definition module, you must delay the evaluation of the module by using unevaluation quotes before adding it using the LanguageDefinition:-Add command. That is, the language definition module must be added as a module definition and not as a module.
The following example adds a definition module. Note the use of unevaluation quotes around the module definition to delay its evaluation.
UppercaseFortran77 := 'module() export Printer, PrintTarget; Printer := eval(CodeGeneration:-LanguageDefinition:-Get( "Fortran")):-Printer; PrintTarget := proc(ic, digits, prec, func_prec, namelist) Printer:-SetLanguageAttribute("Precision" = prec); StringTools:-UpperCase(Printer:-PrintTarget(args)); end proc: end module':
CodeGeneration:-LanguageDefinition:-Add("UppercaseFortran", UppercaseFortran77);
Using a New Translator
After creating the language definition using either the LanguageDefinition:-Define or LanguageDefinition:-Add commands, translate your code to the new language using the CodeGeneration:-Translate command.
The following example demonstrates the use of a new translator. Compare the output of the Fortran command with that of the new translator.
p1 := proc() sin(x+y*z)+trunc(x); end proc:
CodeGeneration:-Fortran(p1);
doubleprecision function p1 () p1 = sin(y * z + x) + dble(int(aint(x))) return end
CodeGeneration:-Translate(p1, language="UppercaseFortran");
DOUBLEPRECISION FUNCTION P1 () P1 = DSIN(Y * Z + X) + DBLE(INT(DINT(X))) RETURN END
14.8 CAD Connectivity
If Autodesk Inventor®, NX®, or SolidWorks® software is installed on your computer, you can set up Maple to communicate with your computer aided design (CAD) application. By connecting your CAD application to Maple, you can retrieve parameters from a CAD drawing, work with those values in Maple, and send the new values to the CAD application to incorporate them in your drawing.
The commands available to the interface depends on the CAD application that you are using. Each application uses a different naming convention for parts and hierarchies of parts in a CAD drawing. In Maple, you can use the CAD Link Assistant as a starting point to find out about the information available from your CAD application and how to reference it. For more information, refer to the CADLink help page.
The Maple CAD package contains subpackages that are specific to individual CAD applications. After selecting the relevant subpackage to use, the first step is to establish a connection to the CAD system. The OpenConnection command opens the CAD application, or connects to a session that is already running on your computer. Both Maple and the CAD application can run side-by-side; updates in either system are automatically reflected in the other system.
The next step is to connect to a particular part or assembly within the CAD application. The GetActiveDocument, OpenPart, or another related command will establish this connection.
When a connection is established with the CAD application and part of the CAD drawing is opened, Maple can extract parameter values, properties, and in some cases the geometry of the part. Maple can then be used to analyze these values to optimize the configuration or test aspects of the design (for example, whether the part can withstand applied force or heat). If necessary, modified parameters can be saved in the CAD application.
14.9 Maple Plug-in for Excel
Maple is available as an add-in to Microsoft Excel for Windows. The Maple Excel link allows you to use Maple commands, including commands that generate Maple plots, as formulas in Microsoft Excel spreadsheets.
Figure 14.1: Maple in Excel
In the following example, an Excel formula forms a quadratic polynomial from the coefficients in cells C1, D3, and B6. You can enter this formula in cell A1 of the Excel spreadsheet.
=Maple( "&1*x^2 + &2*x + &3;", $C$1, $D$3, $B$6 )
In the Excel spreadsheet, enter a string containing the Maple code that you want to return, substituting any parameters contained in spreadsheet cells using an ampersand character (&) followed by a number. Include a semicolon (;) after the Maple command. After the string, list the cell references that should be substituted into the string in the order of the numbers you entered.
For more examples, refer to the Excel help page.
14.10 Connecting MATLAB® and Maple
If MATLAB® is installed on your computer, you can access the MATLAB® computation engine to perform computations in Maple and you can access the Maple computation engine to perform computations in MATLAB®.
You must first configure Maple to communicate with MATLAB®. For more information, refer to the Matlab,setup help page.
Accessing the MATLAB® Computation Engine from Maple
If you have a MATLAB® .m file file with legacy code that you want to run as a step in a longer calculation within Maple, you can run the following commands in Maple.
with(Matlab);
AddTranslator,CloseEngine,ExecuteCommand,FromMFile,FromMatlab,GetVariable,IsAssigned,OpenEngine,SetVariable,chol,closelink,defined,det,dimensions,eig,evalM,fft,getvar,inv,lu,ode15s,ode45,openlink,qr,setvar,size,square,transpose
a := <1,2,3 ; 4,5,6; 7,8,9>;
a≔
b := <3,2,5 ; 1,8,2; 7,3,4>;
b≔
setvar("ma",a)
setvar("mb",b)
evalM("result = yourmfile(ma,mb)")
getvar("result")
The example above illustrates how Maple and MATLAB® maintain separate name spaces, and the specific commands for transferring data between both applications. The matrices a and b are initially defined as Maple variables. By using the setvar command, they are copied into MATLAB® data structures and assigned to the MATLAB® variables ma and mb. By using the evalM command, you specify a command for MATLAB® to parse and run. The result can be copied into a Maple data structure by using the getvar command.
For more information, refer to the Matlab help page.
Accessing the Maple Computational Engine from MATLAB®
Maple provides the Maple Toolbox, which contains hundreds of MATLAB® commands to communicate directly with the Maple engine. To perform a computation, you enter Maple Toolbox commands in MATLAB®, for example,
>> syms x y >> f = x^2+3*y^2-5*x*y+2*x-7*y-12 f = 2 2 x + 3 y - 5 y x + 2 x - 7 y - 12 >> P = solve( diff(f,x), diff(f,y) ); >> P.x ans = -23 --- 13 >> P.y ans = -4 -- 13
In MATLAB®, the variables x and y are declared as symbolic by using the syms command. These variables can then be used with normal MATLAB® operators to create larger symbolic expressions. The Maple Toolbox provides commands, such as solve and diff, to manipulate the Maple expressions you created. In addition to these commands you can use a generic maple command to evaluate arbitrary Maple commands.
Download Help Document