• Ei tuloksia

Extending Maple

In document 1.1 Getting Started (sivua 110-121)

y:=a x2+ 1

When you parse the strings you get an expression. In this case, you get a sum.

> type(s, string), type(y, ‘+‘);

true,true

Theparsecommand does not evaluate the expression it returns. You must use eval to evaluate the expression explicitly. Below, Maple does not evaluate the variableato its value, 2, until you explicitly use theeval command.

> a := 2;

a:= 2

> z := parse( s );

z:=a x2+ 1

> eval(z);

2x2+ 1

See section 10.7 for more details about theparsecommand.

The techniques you have seen in this section are all very simple, but you can use them to create powerful applications such as Maple tutorials, procedures that drill students, or interactive lessons.

3.4 Extending Maple 99 displays expressions, and extending the abilities of such useful commands assimplifyand expand.

Defining New Types

If you use a complicated structured type you may find it easier to assign the structured type to a variable of the form‘type/name‘. That way you only have to write the structure once, thus reducing the risk of errors.

When you have defined the variable ‘type/name‘, you can usename as a type.

> ‘type/Variables‘ := {name, list(name), set(name)}:

> type( x, Variables );

true

> type( { x[1], x[2] }, Variables );

true

When the structured type mechanism is not powerful enough, you can define a new type by assigning a procedure to a variable of the form

‘type/name‘. When you test whether an expression is of type name, Maple invokes the procedure ‘type/name‘ on the expression if such a procedure exists. Your procedure should return true or false. The

‘type/permutation‘ procedure below determines if p is a permutation of the firstnpositive integers. That is,pshould contain exactly one copy of each integer from 1 throughn.

> ‘type/permutation‘ := proc(p)

> local i;

> type(p,list) and { op(p) } = { seq(i, i=1..nops(p)) };

> end proc:

> type( [1,5,2,3], permutation );

false

> type( [1,4,2,3], permutation );

true

Your type-testing procedure may have more than one parameter.

When you test if an expression,expr, has typename(parameters), then Maple invokes

‘type/name‘( expr, parameters )

if such a procedure exists. The ‘type/LINEAR‘ procedure below deter-mines iff is a polynomial in V of degree 1.

> ‘type/LINEAR‘ := proc(f, V::name)

> type( f, polynom(anything, V) ) and degree(f, V) = 1;

> end proc:

> type( a*x+b, LINEAR(x) );

true

> type( x^2, LINEAR(x) );

false

> type( a, LINEAR(x) );

false

Exercises

1. Modify the ‘type/LINEAR‘procedure so that you can use it to test if an expression is linear in a set of variables. For example,x+ay+ 1 is linear in bothx and y, butxy+a+ 1 is not.

2. Define the type POLYNOM(X)which tests if an algebraic expression is a polynomial inX whereX may be a name, a list of names, or a set of names.

Neutral Operators

Maple understands a number of operators, for example+,*,^,and,not, and union. All of these operators have special meaning to Maple: some represent algebraic operations, such as addition or multiplication; some represent logical operations; and some represent operations performed on sets. Maple also has a special class of operators, the neutral operators, on which it does not impose any meaning. Instead, Maple allows you to define the meaning of any neutral operator. The name of a neutral operator begins with the ampersand character, &. Section 4.4 describes the naming conventions for neutral operators.

3.4 Extending Maple 101

> 7 &^ 8 &^ 9;

(7 &^ 8) &^ 9

> evalb( 7 &^ 8 = 8 &^ 7 );

false

> evalb( (7&^8)&^9 = 7&^(8&^9) );

false

Internally, Maple represents neutral operators as procedure calls.

Thus,7&^8 is just a convenient way of writing&^(7,8).

> &^(7, 8);

7 &^ 8

Maple uses the infix notation only if your neutral operator has exactly two arguments.

> &^(4), &^(5, 6), &^(7, 8, 9);

&^(4),5 &^ 6,&^(7,8,9)

You can define the actions of a neutral operator by assigning a pro-cedure to its name. The example below implements the Hamiltonians by assigning a neutral operator to a procedure that multiplies two Hamil-tonians. The next paragraph explains all you need to know about the Hamiltonians to understand the example.

TheHamiltonians orQuaternions extend the complex numbers in the same way the complex numbers extend the real numbers. Each Hamil-tonian has the forma+bi+cj+dk wherea,b,c, anddare real numbers.

The special symbolsi,j, and ksatisfy the following multiplication rules:

i2 = −1, j2 = −1, k2 =−1, ij = k, ji = −k, ik =−j, ki= j,jk = i, and kj=−i.

The‘&^‘procedure below usesI,J, andK as the three special sym-bols. However,I is implemented as thecomplex imaginary unit in Maple.

Therefore, you should assign another letter to represent the imaginary

unit by using theinterfacefunction. See?interfacefor more informa-tion.

> interface(imaginaryunit=j);

You can multiply many types of expressions by using‘&^‘, making it convenient to define a new type,Hamiltonian, by assigning a structured type to the name‘type/Hamiltonian‘.

> ‘type/Hamiltonian‘ := { ‘+‘, ‘*‘, name, realcons,

> specfunc(anything, ‘&^‘) };

type/Hamiltonian :=

{name,∗,+,realcons,specfunc(anything,&^)}

The‘&^‘procedure multiplies the two Hamiltonians,xandy. If either xoryis a real number or variable, then their product is the usual product denoted by *in Maple. If x or y is a sum, ‘&^‘ maps the product onto the sum; that is,‘&^‘ applies the distributive laws:x(u+v) = xu+xv and (u+v)x = ux+vx. If x or y is a product, ‘&^‘ extracts any real factors. You must take special care to avoid infinite recursion when x or y is a product that does not contain any real factors. If none of the multiplication rules apply,‘&^‘ returns the product unevaluated.

> ‘&^‘ := proc( x::Hamiltonian, y::Hamiltonian )

> local Real, unReal, isReal;

> isReal := z -> evalb( is(z, real) = true );

>

> if isReal(x) or isReal(y) then

> x * y;

>

> elif type(x, ‘+‘) then

> # x is a sum, u+v, so x&^y = u&^y + v&^y.

> map(‘&^‘, x, y);

>

> elif type(y, ‘+‘) then

> # y is a sum, u+v, so x&^y = x&^u + x&^v.

> map2(‘&^‘, x, y);

>

> elif type(x, ‘*‘) then

> # Pick out the real factors of x.

> Real, unReal := selectremove(isReal, x);

> # Now x&^y = Real * (unReal&^y)

> if Real=1 then

> if type(y, ‘*‘) then

> Real, unReal := selectremove(isReal, x);

> Real * ’‘&^‘’(x, unReal);

> else

> ’‘&^‘’(x, y);

3.4 Extending Maple 103

> end if;

> else

> Real * ‘&^‘(unReal, y);

> end if;

>

> elif type(y, ‘*‘) then

> # Similar to the x-case but easier since

> # x cannot be a product here.

> Real, unReal := selectremove(isReal, y);

> if Real=1 then

> ’‘&^‘’(x, y);

> else

> Real * ‘&^‘(x, unReal);

> end if;

>

> else

> ’‘&^‘’(x,y);

> end if;

> end proc:

You can place all the special multiplication rules for the symbols I, J, and K in the remember table of‘&^‘. See section 2.5.

> ‘&^‘(I,I) := -1: ‘&^‘(J,J) := -1: ‘&^‘(K,K) := -1:

> ‘&^‘(I,J) := K: ‘&^‘(J,I) := -K:

> ‘&^‘(I,K) := -J: ‘&^‘(K,I) := J:

> ‘&^‘(J,K) := I: ‘&^‘(K,J) := -I:

Since‘&^‘ is a neutral operator, you can write products of Hamilto-nians using&^ as the multiplication symbol.

> (1 + 2*I + 3*J + 4*K) &^ (5 + 3*I - 7*J);

20 + 41I+ 20J−3K

> (5 + 3*I - 7*J) &^ (1 + 2*I + 3*J + 4*K);

20−15I −4J+ 43K

> 56 &^ I;

56I

Below,ais an unknown Hamiltonian until you tell Maple thatais an unknown real number.

> a &^ J;

a&^J

> assume(a, real);

> a &^ J;

a~J

Exercise

1. The inverse of a general Hamiltonian,a+bi+cj+dk, is (a−bi−cj− dk)/(a2+b2+c2+d2). You can demonstrate this fact by assuming thata,b,c, and dare real and define a general Hamiltonian,h.

> assume(a, real); assume(b, real);

> assume(c, real); assume(d, real);

> h := a + b*I + c*J + d*K;

h:=a~ +b~I+c~J+d~K

By the formula above, the following should be the inverse ofh.

> hinv := (a-b*I-c*J-d*K) / (a^2+b^2+c^2+d^2);

hinv := a~−b~I−c~J−d~K a~2+b~2+c~2+d~2

Now all you have to check is that h &^ hinv and hinv &^ h both simplify to 1.

> h &^ hinv;

3.4 Extending Maple 105

a~ (a~−b~I−c~J−d~K)

%1

+b~ (Ia~ +b~−c~K+d~J)

%1

+c~ (Ja~ +b~K+c~−d~I)

%1

+d~ (Ka~−b~J +c~I+d~)

%1

%1 :=a~2+b~2+c~2+d~2

> simplify(%);

1

> hinv &^ h;

a~ (a~−b~I−c~J−d~K)

%1

+ a~b~I+b~2+b~c~K−b~d~J

%1

+ a~c~J−b~c~K+c~2+c~d~I

%1

+ a~d~K+b~d~J−c~d~I +d~2

%1

%1 :=a~2+b~2+c~2+d~2

> simplify(%);

1

Write a procedure,‘&/‘, that computes the inverse of a Hamiltonian.

You may want to implement the following rules.

&/( &/x ) = x, &/(x&^y) = (&/y) &^ (&/x), x &^ (&/x) = 1 = (&/x) &^ x.

Extending Certain Commands

If you introduce your own data structures, Maple cannot know how to ma-nipulate them. In most cases, you design new data structures because you want to write special-purpose procedures that manipulate them. However, sometimes extending the capabilities of one or more of Maple’s built-in commands is more intuitive. You can extend several Maple commands, among themexpand,simplify,diff,series, and evalf.

Suppose you choose to represent a polynomialanun+an−1un−1+· · ·+

a1u+a0 by using the data structure POLYNOM( u, a_0, a_1, ..., a_n )

You can then extend the diff command so that you can differentiate polynomials represented in that way. If you write a procedure with a name of the form ‘diff/F‘ then diff invokes it on any unevaluated calls to F. Specifically, if you use diff to differentiate F(arguments) with respect tox, thendiff invokes ‘diff/F‘as follows.

‘diff/F‘( arguments, x )

The procedure below differentiates a polynomial inuwith constant coef-ficients with respect tox.

> ‘diff/POLYNOM‘ := proc(u)

> local i, s, x;

> x := args[-1];

> s := seq( i*args[i+2], i=1..nargs-3 );

> ’POLYNOM’(u, s) * diff(u, x);

> end proc:

> diff( POLYNOM(x, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), x );

POLYNOM(x,1,2,3,4,5,6,7,8,9)

> diff( POLYNOM(x*y, 34, 12, 876, 11, 76), x );

POLYNOM(x y, 12,1752,33,304)y

The implementation of the Hamiltonians that section 3.4 describes does not know that multiplication of Hamiltonians is associative, that is (xy)z = x(yz). Sometimes, using associativity simplifies a result. Re-call that I here is not the complex imaginary unit, but rather, one of

3.4 Extending Maple 107 the special symbols I, J, and K that are part of the definition of the Hamiltonians.

> x &^ I &^ J;

(x&^I) &^J

> x &^ ( I &^ J );

x&^K

You can extend the simplify command so that it applies the as-sociative law to unevaluated products of Hamiltonians. If you write a procedure with a name of the form ‘simplify/F‘, then simplify in-vokes it on any unevaluated function calls toF. Thus, you must write a procedure‘simplify/&^‘that applies the associative law to Hamiltoni-ans.

The procedure below uses the typematch command to determine if its argument is of the form (a&^b)&^c and, if so, it picks out the a, b, and c.

> s := x &^ y &^ z;

s:= (x&^y) &^z

> typematch( s, ’‘&^‘’( ’‘&^‘’( a::anything, b::anything ),

> c::anything ) );

true

> a, b, c;

x, y, z

You can give the user details about the simplifications your procedure makes through the userinfo command. The ‘simplify/&^‘ procedure prints out an informative message if you set infolevel[simplify] or infolevel[all]to at least 2.

> ‘simplify/&^‘ := proc( x )

> local a, b, c;

> if typematch( x,

> ’‘&^‘’( ’‘&^‘’( a::anything, b::anything ),

> c::anything ) ) then

> userinfo(2, simplify, "applying the associative law");

> a &^ ( b &^ c );

> else

> x;

> end if;

> end proc:

Applying the associative law does make some products of Hamiltoni-ans simpler.

> x &^ I &^ J &^ K;

((x&^I) &^J) &^K

> simplify(%);

−x

If you setinfolevel[simplify]large enough, Maple prints out infor-mation on whatsimplifytries in order to make your expression simpler.

> infolevel[simplify] := 5;

infolevelsimplify := 5

> w &^ x &^ y &^ z;

((w&^x) &^y) &^z

> simplify(%);

simplify/&^: "applying the associative law"

simplify/&^: "applying the associative law"

w&^ ((x&^y) &^z)

The help pages forexpand,series, andevalfprovide details on how you may extend those commands. See also section 8.4.

In document 1.1 Getting Started (sivua 110-121)