Creating a Maple procedure
This page was not originally going to be about Maple code; Maple is, after all, simply a means to an end. This was initially going to be about the one-seventh ellipse, but that didn't turn out to be as interesting as I had originally hoped. The more interesting part, to me at least, was the Maple code that I wrote to discover conic sections similar to the one-seventh ellipse. The final procedure (if there is such a thing as a final version) is not fancy or even really elegant, but it touched on a lot of intricacies that one must deal with when writing code in Maple. In addition, someone showed some interest in the Maple programs I wrote for my first essay, and expressed a desire to write her own procedures, so I thought a small essay on writing in Maple might prove useful. This comes with the disclaimer that I have little formal knowledge of programming in general, but have some experience in using Maple's language to automate various mathematical calculations.
An interesting task
No one (at least that I know of) starts writing a program just because - there must be a goal in mind. The program must do something the programmer wants done. In the case of the one-seventh ellipse task, the question the programmer (me) wanted to answer was: How unique is this property of one-seventh? Now, in case you are not familiar with the one-seventh ellipse and are too lazy to click on the link I provided, I'll give a short explanation here:
It just so happens that any five points in a plane, no three of which are collinear, define a non-trivial conic section (ellipse, circle, parabola, hyperbola). This is because the equation for a conic may be written as
If you want more details on why this is so, you may find some help here. In any case, the above equation has 6 parameters, a0 through a5, but we can divide the equation by one of them to get a 1 somewhere, so it is reasonable to suspect that choosing 5 appropriate pairs of values for x and y would yield unique values for the remaining 5 parameters. This is not only reasonable, but true as well. What does this have to do with one-seventh? Well, it just so happens that the decimal expansion of one-seventh, 0.142857... has precisely 6 numbers in its repetend. If you pair these numbers in sequence, you can come up with 6 points in the plane: (1, 4), (4, 2), (2, 8), (8, 5), (5, 7), (7, 1), no three of which are collinear. Well, we know that any 5 of these will define a unique conic, in this case an ellipse, but the rather odd thing is that the sixth point happens to lie on the ellipse as well! This does seem a bit bizarre, but how bizarre is it really? Well, if we were to set out to find another fraction with this property, we'd first have to find its decimal expansion, see if it has a 6 digit repetend, seperate the repetend into 6 points, plug in 5 of them to solve for the parameters in the conic's equation, and then plug the 6th point into this equation to see if we get 0. This is a lot of calculating and we might well go through many fractions without finding one, or we might stumble upon one fairly early in our search. In either case, unless we systematically search all possible 6 digit repetends, we'd really not be sure how often this occurred. This is where Maple comes in.
Input/Output
It may help to think of a Maple procedure as a function machine, one where we simply put something in and get something out. Of course, to create the function machine we have to worry about how we take what goes in and make it into what comes out, but initially it might help to just think of the type of input/output that we want. I remember the first time I tried to write a Maple procedure completely on my own. It seemed daunting, but fortunately the professor that had asked me to write it stated things in terms that helped me focus. He said, "I want a procedure where I input a set of matrices with property X, and which returns a list of integers with property Y." It doesn't matter what properties X and Y were now, what matters are the phrases "set of matrices" and "list of integers." You see, not only does this tell us what goes in and what comes out, but it tells us how they are organized when they go in and when they come out. How is this relevant to our current challenge of writing a program to find fractions that share the 'conic generation' property with one-seventh? Well, perhaps our input should be a fraction, and our output should be a statement about whether the property exists, something along the lines of yes/no or true/false. How does this relate to the organization of the input and output? Well, might things be different if the goal had been to find numbers that share the 'conic generation' property with one-seventh? What's the difference between a number and a fraction? Well, mathematically there may be little difference, but to Maple (and probably to our own ways of thinking), there are some key differences, for example in the ways they are represented. This brings us to our first programming idiosyncracy - types.
Maple Types
Though mathematically 1/7 = 0.142857142857..., I doubt that our brains react precisely the same way when they see 1/7 as they do when they see 0.142857142857...; I know mine doesn't. It turns out that Maple doesn't think about them identically either, it classifies them differently. It classifies 1/7 as a fraction and 0.142857142857... as a float. Why is this? Well, Maple has finite memory and no matter how far it carries the repetition 142857, if it's only repeated finitely many times then we are really just seeing an approximation of the number intended. I'm not sure why Maple programmers used the term float, but I like to think it is because the value Maple sees is floating somewhere near the value intended, and how close it floats depends on how many decimal places we want to use. In any case, both fraction and float are examples of types inside the Maple structure. There are many other types, and a few of them will be immediately relevant to our problem. In fact, I already mentioned two other Maple types, the set and the list. What's different about these? Maple thinks of a set much in the same way mathematicians do (thankfully, Maple programmers had substantial mathematical background, so the terminology is often obvious), it's an unordered collection where each element occurs once. Thus, in Maple's thinking {1, 2, 3, 4, 5} = {5, 5, 4, 3, 3, 2, 1, 1, 1, 1, 1, 1, 1, 5}. A list is different in that it's an ordered collection that preserves the number of times an element occurs. So, for Maple, [1, 2, 3, 4, 5] is different then [2, 1, 3, 4, 5] and both of these are different from [1, 1, 2, 3, 4, 5]. To find out what type Maple is assigning to a given object, x, we can just ask it, using the command whattype(x); which brings us to another programming idiosyncracy - syntax.
Maple Syntax
Syntax refers to the rules and structure for language. This deals both with the ordering of words and also with the punctuation that indicates when a thought is complete. We deal with this in English all the time - a period denotes that a sentence (or chunk of thought) is complete while rearranging the word order inside a chunk (sentence) can drastically confuse the meaning or destroy the meaning all together. Sometimes we are not very careful about the ordering, but with Maple we have to be. For example, sometimes I'll ask my wife something like "Remind me that tomorrow I need to buy green peppers." to which she'll sarcastically and immediately reply, "Don't forget that tomorrow you need to buy green peppers." However, I can preempt her joke by being more precise and asking "Tomorrow, remind me that I need to buy green peppers." Notice the difference? Maple does notice differences like these, so precision is key. So, what is Maple's syntax? Well, Maple operates primarily by means of functions. That is, its commands require an input and they give an output and the input is usually entered in much the same way we use function notation. Thus, whattype is a function in Maple, and to input something into it, like 1/7, we'd type whattype(1/7) and Maple would think of this as us asking for the image of 1/7 under the function whattype. Of course, Maple programmers realize that our conventional notation is not always done in terms of functions, and they've honored this wherever possible. Thus, we wouldn't type plus(3, 4) to add 3 and 4, we'd just type 3 + 4 and Maple can handle it. Typing whattype(1/7) isn't the whole story, however. What we want is to view the result of the function whattype applied to 1/7, and so we'd need to use a semi-colon ';'. So, if we type whattype(1/7); and then hit enter, we'll see the response fraction. One may also use the colon ':' to evaluate the input through the function without showing the output. Typing whattype(1/7): and then hitting enter doesn't show us anything except a new command line, but rest assured that Maple has evaluated 1/7 through the function whattype. Hiding the output can be useful if a particular output is rather unruly and you don't need to see it so much as you need to use it in further calculations. It's important to remember that ; completes a thought for Maple, and if the thing you type is not logical without it, you'll likely get an error. Example: executing whattype(B) whattype(A); gives me an error, because it's not a type of thought Maple understands. whattype(B) is incomplete in and of itself, it needs ; or : just like a sentence is incomplete without a period. Full thoughts must be ended by ; or : or else Maple doesn't know what you mean. This doesn't mean 3 + 4 should be executed as 3; + 4; because, while 3 and 4 can be complete thoughts by themselves, 3 + 4 is also a complete thought by itself so 3 + 4; is the way it should be executed. In addition, Maple is cAsE sEnSiTiVe, so executing Whattype(1/7); is different than executing whattype(1/7); which can be both a curse and a blessing.
Okay, so now we can execute a Maple command, with the option of viewing or hiding the output. The next thing we need to learn how to do is to define a variable. Though the notation is not universal, it's not uncommon to see a mathematician write something like A := whatever. The := (colon followed immediately by equals) means 'define.' So A := whatever means A is defined to be whatever. Maple uses this notation the same way. If I type A := 4; and then hit enter, Maple will record that A is defined to be 4. Then, whenever I type A later in that Maple session, Maple will think of 4. This is immensely useful. Suppose I have a huge list of numbers I want to work with, but I obviously don't want to type the list over and over again. I can use the define feature like this: L := [1, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 5]; and by hitting enter Maple knows that if I later just type L, I really mean the whole list. This can also be used for changing values, like the following example. Let's say I type (and execute by hitting enter) A := x + y; and then later I type x := 4; and y := 5; What will Maple think of when I type A? It will think of 9 = (4 + 5). If, even later, I change x to 6 by executing x := 6; then Maple begins to think of A as 11 from then on. We will use this later.
I encourage you to open this Maple worksheet and walk through the examples, just so you're sure you've got it. (You may want to right-click and save the file first, and then open it from there, especially if your browser tries to open it and displays something about an XML file.) I know right now that it doesn't seem like we can do very much, but we're off to a good start. Maple is a tremendously powerful set of programs and so a bit of complexity is to be expected. The next thing we'll talk about is indexing -the way we know how far along we are in a sequence. In written notation we use subscripts, as in the parameters a0 through a5 in the equation for the conic (though my text editor doesn't let me put them as subscripts, you saw that they were subscripts in the equation). Maple will display this indexing as subscripts when it returns a result to us that contains them, but in the input we use something different. Let's say I have a list L, with 10 elements in it, L := [a, b, c, d, e, f, g, h, i, j]; like so. To tell Maple I want the 4th item in L, I'd execute L[4]; and I'd get back d. To ask for the 3rd through 7th items, I'd type L[3..7]; and would get back a sublist [c, d, e, f, g]. Why do I get a sublist with the [] instead of just the letters c, d, e, f, g? That's a convention decided upon by whomever wrote the original Maple code (aka Maplesoft) and is one of the quirks we just have to deal with. As with any convention, sometimes it's useful and sometimes it's annoying, but it's still there. In any case, this indexing will prove indispensible as we begin programming. One other nice feature is the ability to ask for items in reverse order, for example executing L[- 1]; returns j and so on. Be careful though, about asking for an index that doesn't exist - executing L[11]; will return the error 'invalid subscript selector,' which is an error you will probably become all too accquainted with as you struggle through your first few programs.
Finally, you should know that Maple largely ignores white-space. That is, it recognizes a space between words but it doesn't differentiate between 1 space and 15 spaces. The # symbol also has a specialized function. Typing # lets Maple know that we are just entering text to read and that it shouldn't think about what follows as commands. Programmers use this to leave notes in their code for other programmers and for purposes of debugging. You'll see an example soon.
Maple Vocabulary
Now that we've gotten a bit of syntax and typing down, we can worry about the vocabulary - the actual words, or in this case, functions that we will be using. Building a vocabulary large enough to accomodate your needs is largely a matter of experience, but some of the commands are the precise equivalent of what you'd expect them to be from their mathematical meanings. For example, there is a Matrix command and a Transpose command, and, as you'd expect, the Matrix command arranges its inputs into a matrix and the Transpose command transposes the matrix it receives as input. For any Maple command, if you want to view the help page that describes exactly how the input should be entered, you can type ?command and help will appear. So, if I type in integrate(x^3, x, 4, 5); and don't get the integral of x^3 evaluated from 4 to 5, but rather the symbolic integration (without the constant), I can type ?integrate to find out that I needed to execute integrate(x^3, x = 4..5); to get 369/4. A warning is in order, however. Maple's help files are written for the Maple proficient, not for the Maple beginner, so they can be a bit difficult to understand. I know this because I used to be employed proofreading them for a professor who wrote many Maple procedures and the associated helpfiles. Anyway, don't give up if you struggle, just take a break and then come back. One other quirk about Maple vocabulary, again a convention decided upon by Maplesoft, is that the various functions are arranged into packages. Transpose is part of the LinearAlgebra package. Why do we care? Well, to allow Maple to boot up faster, many packages aren't loaded initially and have to be added before the commands will work. So, if you open a blank Maple worksheet and type in a Matrix named M and then type Transpose(M); you will be out of luck until you execute with(LinearAlgebra): Click here to see an example. This is a good example of where you might use : instead of ; because typing with(LinearAlgebra); gives a big sequence of the names of the functions in the LinearAlgebra package. This can be a bit of a pain, especially when Maplesoft tries to upgrade Maple's packages. Most new versions of Maple have both a LinearAlgebra package and the package it supplanted, linagl. This means there is a transpose command in the linalg package as well as the Transpose command in the LinearAlgebra package. In most cases use the newer packages (if you know what they are) because they work a bit better, but that is a topic for a different essay.
There are a few absolutely essential pieces of Maple vocabulary for us. One is the proc command. This command tells Maple we are going to begin writing a procedure. If my procedure needs to be named Brian and it will have input parameters n and m, I'd write Brian := proc(n, m) ...code here... end proc; or end proc: as you desire. There are some slight extensions of this available, but this should do us for now. Another vital piece is the if/then statement. This asks Maple to check the Boolean value of a statement and then, depending on that value, to carry out a specific action or sequence of actions. There is also the for/do loop and while/do loop. The for/do loop tells Maple to carry out a command a specified number of times, while the while/do loop tells Maple to repeat the command until a certain condition is met. Finally, the break command tells Maple to exit the for/do loop or while/do loop it's currently operating in and proceed with the next step in the program. I think this will all make a bit more sense as we try our hand at writing some code.
Let's get started!
Let's practice by writing a small Maple program to find the slope between two points. We'll try and flesh it out here and then execute it in a Maple worksheet to see if it works. We know the formula for slope requires that we have two ordered pairs, the points (x1, y1) and (x2, y2) and we also know that we want our output to be a single value, m. Well, how might one enter 2 ordered pairs? Maple treats ( ) as either parantheses for operations or as in function notation, so we can't use them. The key is that these are ordered, so we turn our thoughts to the list type that was discussed earlier. Someone could think of the notation [3, 4] as the point (3, 4) without too much difficulty, so let's run with that. We've got two inputs, point 1 and point 2, let's refer to them as p1 and p2 and off we go!
slope := proc(p1, p2); # Here we've given our process a name and said that we'll have two input values called p1 and p2.
mtop := (p1[2] - p2[2]); # These next three steps are basically the whole program. We have Maple compute the difference of the y-values and the difference of the x-values and then compute the quotient and call it m.
mbottom := (p1[1] - p2[1]);
m := mtop/mbottom;
RETURN(m) end proc; # This tells Maple to output m and then to end the procedure. Let's put this into Maple and test it!
That's pretty cool, but now comes the inevitable part of programming - unanticipated troubles. What happens if someone runs the program above on the points [3, 4], [3, - 6]? Aargh!! We get something about an error called numeric exception:division by zero. Technically, nothing is wrong with this, but it's hardly the kind of output we want from our program. We'd much rather that the slope program actually returned something besides an error, like the word 'undefined' or maybe even infinty. Let's rework the problem with an if/then statement to make this happen:
slope := proc(p1, p2);
mtop := (p1[2] - p2[2]);
mbottom := (p1[1] - p2[1]);
m := mtop/mbottom;
if mbottom = 0 then RETURN(infinity) end if;
RETURN(m) end proc;
How does this work? What!? It didn't work? Let's figure out why. Ok, I'll admit that this was a contrived mistake to teach you another valuable piece of Maple vocabulary, the trace command. The trace command reveals the inner calculations a process runs through on the way to it's output (well, not all of them, but all those that form a complete chunk and are ended by ;) so we can often use this to debug a program. On a new command line, execute trace(slope); and then rerun the slope procedure with the points [3, 4], [3, - 6]. One interesting thing about Maple that wasn't immediately obvious to me (though now it seems that way) is that it is time dependent on its executions, not placement dependent. Thus, I can execute trace(slope); below slope([3, 4], [3, - 6]); and then move the cursor back up to reexecute the slope procedure and trace is in effect. Thus, it doesn't matter where your parameters are defined or where a procedure was run, it only matters if it has already been run before you try and do something with it. You can force Maple to forget what it has already run by executing restart; or by hitting the restart button in the panel (it looks like a circular blue arrow with a secondary red tip in my version). Anyway, the trace feature should reveal that the program correctly computed mtop to be -10 and mbottom to be 0 and then ran into the error. Now it hits us, Maple returns the error as soon as it discovers it, which is before the if/then statement we put in. So, let's move the if/then statement up and try again.
slope := proc(p1, p2);
mtop := (p1[2] - p2[2]);
mbottom := (p1[1] - p2[1]);
if mbottom = 0 then RETURN(infinity) end if;
m := mtop/mbottom;
RETURN(m) end proc;
Now, turn off trace by running untrace(slope); and then execute slope([3, 4], [3, -6]); What do you get? ∞ of course!
The One-Seventh Ellipse Code
Okay, let's move on to something more challenging - the problem we were initially interested in. Earlier, we decided that the input should be a fraction and the output should be a Boolean result like true or false. So what happens in between? Let's take a crack at writing a program, with extensive programming notes to assist us. To expedite the execution, we'll do it all in a Maple worksheet.
Once you've worked through all that you've hopefully got a better grip on how to write simple programs in Maple and you also have some nice little code to check fractions for the property that one-seventh has, namely the existence of a conic like the one known as the one-seventh ellipse. Now we can begin to answer the question of 'how bizarre is this property' of one-seventh? In other words, do lots of other fractions have it as well? To answer this, you can see the results in a series of three Maple worksheets (they each take about 1.5 hours if you execute them, but you can simply open them and view the output) or three html files (I recommend just looking at the html, unless you want to grab the results from Maple in some sort of searchable form). That should help you see why I felt the original question wasn't as interesting as the programming that went into constructing an answer for it.
Maple Worksheet 1, Maple Worksheet 2, Maple Worksheet 3
Okay, okay I know what you are saying now, at least if you looked at the Wolfram link I provided about the one-seventh ellipse. "But 1/7th was even more special in that it had a conic through both (1, 4), (4, 2), (2, 8), (8, 5), (5, 7), (7, 1) and through (14, 42), (42, 28), (28, 85), (85, 57), (57, 71), (71, 14), its double digit pairs!" Well, this is true, but it turns out that 1/7th is still not unique in that regard either. I'm not going to show you the Maple program I used to do it, partially because I want you to write your own to check my results but also because I'm a bit embarrased at how long it takes to execute, but I'll let you see a Word document (or a text file, if you prefer) with all the fractions with 6 digit repetends that have both the single digit conic and the double digit conic. There are certainly more than I expected. In any case, there are now all sorts of interesting questions we can ask, such as: "How are these fractions distributed?" or "Are there any fractions with a 7 digit repetend with all 7 points on a conic?" or "Are there any fractions with not only a single and double digit conic, but also a triple digit conic?" For answers to these and more questions, I refer you to this.