MuPAD® provides a variety of tools for handling built-in mathematical functions such as sin, cos, exp, and so on. These tools implement the mathematical properties of these functions. Typical examples are the float conversion routine, the diff differentiation function, or the expand function, which you use to manipulate expressions:
float(sin(1)); diff(sin(x), x, x, x); expand(sin(x + 1))
You can say that the mathematical knowledge about the built-in functions is distributed over several system functions: float knows how to compute numerical approximations of the sine function, diff knows the derivative of the sine function, and expand knows the addition theorems of the trigonometric functions.
When you implement your own function, you can integrate it into the MuPAD system, making other functions aware how to work with this new function correctly. If the new function consists only of built-in MuPAD functions, then you do not need to take extra steps. All MuPAD functions interact correctly with the new function:
f := x -> (x*sin(x)): diff(f(x), x)
However, if you implement a function that is not composed of the standard MuPAD objects (for example, a new special function), you must distribute the knowledge about the mathematical meaning of the new function to standard MuPAD functions, such as diff, expand, float, and so on. This extra task is necessary for integrating the new function with the rest of the system. For example, you might want to differentiate an expression that contains both the new function and some built-in functions, and such differentiation is only possible via the MuPAD differentiation routine. Therefore, this routine must know how to handle the new symbol.
MuPAD uses function environments (domain type DOM_FUNC_ENV) to integrate functions into the system. A function environment stores special function attributes (slots) in an internal table. Whenever an overloadable system function, such as diff, expand, or float, encounters an object of type DOM_FUNC_ENV, it searches the function environment for a corresponding slot. If a system function finds the appropriate slot, it calls that slot and returns the value produced by the slot. All built-in MuPAD functions are implemented as function environments:
You can call a function environment as you would call any MuPAD function or procedure:
Suppose you implement the complete elliptic integral functions of the first and second kind, K(z) and E(z). These functions appear in different contexts, such as calculating the perimeter of an ellipsis, the gravitational or electrostatic potential of a uniform ring, and the probability that a random walk in three dimensions ever goes through the origin. The elliptic integrals have the following special values:
, E(1) = 1, , .
MuPAD provides the built-in functions ellipticE and ellipticK for computing these elliptic integrals. However, you can implement your own functions for the same task. For example, write the procedures ellipE and ellipK. These procedures define the values of the elliptic integrals for special values of x. For all other argument values, the values of elliptic integrals are unknown, and the procedures return the symbolic expressions ellipE(x) and ellipK(x). Use procname to return symbolic expressions:
ellipE := proc(x) begin if x = 0 then PI/2 elif x = 1 then 1 else procname(x) end_if end_proc:
ellipK := proc(x) begin if x = 0 then PI/2 elif x = 1/2 then 8*PI^(3/2)/gamma(-1/4)^2 elif x = -1 then gamma(1/4)^2/4/sqrt(2*PI) else procname(x) end_if end_proc:
ellipE and ellipK return special values for particular arguments. For all other arguments, they return symbolic expressions:
ellipE(0), ellipE(1/2), ellipK(12/17), ellipK(x^2 + 1)
The first derivatives of these elliptic integrals are as follows:
The standard MuPAD differentiation function diff does not know about these rules. Therefore, trying to differentiate ellipE and ellipK simply returns the symbolic notations of the derivatives:
diff(ellipE(x), x), diff(ellipK(x), x)
To make diff work with the new functions, create function environments from the procedures ellipE and ellipK. In addition, function environments let you control the appearance of the symbolic function calls in outputs.
A function environment consists of three operands.
The first operand is a procedure that computes the return value of a function call.
The second operand is a procedure for printing a symbolic function call on the screen.
The third operand is a table that specifies how the system functions handle symbolic function calls.
To create function environments, use funcenv. For example, create function environments ellipE and ellipK. Use the second argument to specify that symbolic calls to ellipE and ellipK must appear as E and K outputs:
output_E := f -> hold(E)(op(f)): ellipE := funcenv(ellipE, output_E):
output_K := f -> hold(K)(op(f)): ellipK := funcenv(ellipK, output_K):
Although ellipE and ellipK are now function environments, you can call them as you would call any other MuPAD function:
ellipE(0), ellipE(1/2), ellipK(12/17), ellipK(x^2+1)
The third argument funcenv is a table of function attributes. It tells the system functions (such as float, diff, expand, and so on) how to handle symbolic calls of the form ellipE(x) and ellipK(x). You can update this table specifying the rules for the new function. For example, specify the new differentiation rules by assigning the appropriate procedures to the diff slot of the function environments:
ellipE::diff := proc(f,x) local z; begin z := op(f); (ellipE(z) - ellipK(z))/(2*z) * diff(z, x) end_proc:
ellipK::diff := proc(f,x) local z; begin z := op(f); (ellipE(z) - (1-z)*ellipK(z))/ (2*(1-z)*z) * diff(z, x) end_proc:
Now, whenever f = ellipE(z), and z depends on x, the call diff(f, x) uses the procedure assigned to ellipE::diff:
diff(ellipE(z), z); diff(ellipE(y(x)), x); diff(ellipE(x*sin(x)), x)
The new differentiation routine also finds higher-order derivatives:
diff(ellipE(x), x, x)
taylor(ellipK(x), x = 0, 6)
If a derivative of a function contains the function itself, the integration routine has a good chance of finding symbolic integrals after you implement the diff attributes. For example, int now computes the following integrals: