|
1 | | -# PHP interface with the CAS |
2 | | - |
3 | | -This document describes the design of the PHP interface to the CAS. This interface was developed specifically to connect STACK to Maxima. |
4 | | - |
5 | | -# High level objects |
6 | | - |
7 | | -## CAS text |
8 | | - |
9 | | -CAS text is literally "computer algebra active text". This is documented [here](../Authoring/CASText.md). Note, that when constructing CAS text it must be able to take a CAS session as an argument to the constructor. In this way we can use lists of variables, such as [question variables](../Authoring/Variables.md) to provide values. |
10 | | - |
11 | | -E.g. we might have : |
12 | | - |
13 | | - n:rand(5)+2; |
14 | | - |
15 | | -in the question variables field. The question is instantiated, and then later we need to create some CAS text, e.g. feedback, in which we have : |
16 | | - |
17 | | - You were asked to find the integral of \((x+2)^{@n@}\) with respect to \(x\). In fact .... |
18 | | - |
19 | | -Here, we need the CAS text to be able to construct the text, with the previously evaluated value of `n`. This need is also why the CAS session returns the *value* of the variables as well as a displayed form. It is used here. |
20 | | - |
21 | | -## CAS session |
22 | | - |
23 | | -This class actually calls the CAS itself. The basic ideas is to take a list of maxima commands, including assignments of the form. |
24 | | - |
25 | | - key:rawvalue |
26 | | - |
27 | | -and executes this list in a single Maxima session. The results of this are then captured and fed back into the CAS strings so we essentially have data in the form: |
28 | | - |
29 | | - key => |
30 | | - value |
31 | | - display |
32 | | - error |
33 | | - |
34 | | -An important point here is that expressions (values) can refer to previous keys. This is one reason why we can't tie teachers down to a list of allowed functions. They will be defining variable and function names. We have implemented a lazy approach where the connection to the CAS is only made, and automatically made, when we ask for the values, display form or error terms for a variable. And we don't generate display and value unless they are needed later. E.g. intermediate values do not create LaTeX. |
35 | | - |
36 | | - |
37 | | -## Answer tests |
38 | | - |
39 | | -The answer tests essentially compare two expressions. These may accept an option, e.g. the number of significant figures. |
40 | | -Details of the current answer tests are available [elsewhere](../Authoring/Answer_Tests/index.md). The result of an answer test should be |
41 | | - |
42 | | -1. Boolean outcome, true or false, |
43 | | -2. errors, |
44 | | -3. feedback, |
45 | | -4. note. |
46 | | - |
47 | | -# Other concepts |
48 | | - |
49 | | -## Validity |
50 | | - |
51 | | -There are a number of reasons why a CAS expression needs to be "valid". These are |
52 | | - |
53 | | -1. security, |
54 | | -2. stability, |
55 | | -3. error trapping, |
56 | | -4. pedagogic reasons, specific to a particular question. |
57 | | - |
58 | | -## Single call PRTs and simplification |
59 | | - |
60 | | -As of STACK 4.4 we make a single Maxima call to exectue an entire PRT. |
61 | | -This reduces significantly the number of separate calls to maxima, which |
62 | | -is a significant efficienty boost for more complex questions. |
63 | | - |
64 | | -Some answer tests rely on "unsimplified" expressions with a "what you see |
65 | | -is what you get" approach. |
66 | | - |
67 | | -This example illustrates the issue. The teacher computes the answer to their |
68 | | -question, e.g. find \(\int_{0}^{1} {\frac{{\left(1-x\right)}^4\cdot x^4}{x^2+1}} \mathrm{d}x\), |
69 | | -with the Maxima code |
70 | | - |
71 | | - p1:int(x^4*(1-x)^4/(1+x^2),x,0,1); |
72 | | - |
73 | | -using simplification (obviously) at the start of the quetion variables, and this simplified expression is used |
74 | | -by the PRT. The answer, \(\frac{22}{7}-\pi\), is held internally in "simplified" form. |
75 | | -The Maxima string output is `22/7-%pi` but internally the answer is actually the following |
76 | | - |
77 | | - ((MPLUS SIMP) ((RAT SIMP) 22 7) ((MTIMES SIMP) -1 $%PI)) |
78 | | - |
79 | | -You can see the internal tree structure of a Maxima expression with the following code. |
80 | | - |
81 | | - (simp:true, p1:22/7-%pi, ?print(p1)); |
82 | | - |
83 | | -Rather than `22/7-%pi` the internal structure is really closer to this: `rat(22,7)+ (-1*%pi)`. |
84 | | - |
85 | | -On the other hand, a student types in the expression `22/7-%pi` and we deal with this without simplification. |
86 | | -The internal Maxima expression is now |
87 | | - |
88 | | - (simp:false, p1:22/7-%pi, ?print(p1)); |
89 | | - |
90 | | -which gives a different internal structure |
91 | | - |
92 | | - ((MPLUS) ((MQUOTIENT) 22 7) ((MMINUS) $%PI)) |
93 | | - |
94 | | -which might best be thought of as `22/7+-(%pi)` where `-` is now a function of a single argument. |
95 | | - |
96 | | -In this example, the unsimplified `MQUOTIENT` and simplified `RAT SIMP` are not particularly problematic. However, the difference between `-%pi` and `-1*%pi` is seriouly problematic. Indeed, this kind of distinction is exactly what some |
97 | | -answer tests, e.g. `EqualComAss` and `CasEqual`, are designed to establish. These tests will not work with a mix of simplified and unsimplified expressions, even if to the user they look completely identical! |
98 | | - |
99 | | -The solution to this problem is to "rinse" away any maxima internal simplification by using the Maxima `string` function to return the expression to the top level which a user would expect to type. This process corresponds to what happend in older versions of Maxima in which expressions were routinely passed between Maxima and PHP, with the string representation being used. |
100 | | - |
101 | | -Some expressions (lists, matrices) are passed by reference in Maxima, so even if the teacher's answer is created without simplification in the first instance, when it is evaluated by the answer tests there is a risk of it becoming simplified when it is later compared by an answer test function. |
102 | | - |
103 | | -## Wrinkles: `ordlerless` and `ordergreat` |
104 | | - |
105 | | -The Maxima functions `ordlerless` and `ordergreat` can only be executed once. In a Maxima desktop session try |
106 | | - |
107 | | - orderless(a,b); |
108 | | - orderless(x,y); |
109 | | - |
110 | | -This will result in a Maxima error "orderless: reordering is not allowed.". |
111 | | - |
112 | | -These functions only take effect outside the current block. Try executing the following two calls in a clean Maxima desktop session. |
113 | | - |
114 | | -```` |
115 | | -block( |
116 | | - ordergreat(a,b), |
117 | | - expand((a+b)^2) |
118 | | -); |
119 | | -
|
120 | | -expand((a+b)^2); |
121 | | -```` |
122 | | - |
123 | | -Notice that the requested ordering (`a` before `b`) is implemented once the first block is completed, only in the second command. You might expect _both_ commands to respect the requested ordering. |
124 | | - |
125 | | -This is a problem in STACK. STACK creates a block to manage execution of commands, including evaluation of the question variables, construction of castext and execution of PRTs. Any call to these functions will therefore be inside a block containing all the other commands. |
126 | | - |
127 | | -STACK makes an exception for `ordlerless` and `ordergreat`. However, the work-around means STACK only supports single and simple uses. You must give an explicit call to these functions. You cannot create a list of variables, and then apply `ordlerless` and `ordergreat`. By design expressions like |
128 | | - |
129 | | - apply(ordergreat, random_permuation([a,b,c])); |
130 | | - |
131 | | -will not work in STACK (but similar constructs will work in the desktop). |
132 | | - |
133 | | -See these issue |
134 | | - |
135 | | -* https://github.com/maths/moodle-qtype_stack/issues/1384 |
136 | | -* https://github.com/maths/moodle-qtype_stack/issues/1241 |
137 | | -* https://github.com/maths/moodle-qtype_stack/issues/1207 |
| 1 | +# PHP interface with the CAS |
| 2 | + |
| 3 | +This document describes the design of the PHP interface to the CAS. This interface was developed specifically to connect STACK to Maxima. |
| 4 | + |
| 5 | +# High level objects |
| 6 | + |
| 7 | +## CAS text |
| 8 | + |
| 9 | +CAS text is literally "computer algebra active text". This is documented in [/Authoring/CASText](../Authoring/CASText.md). Note, that when constructing CAS text it must be able to take a CAS session as an argument to the constructor. In this way we can use lists of variables, such as [question variables](../Authoring/Variables.md) to provide values. |
| 10 | + |
| 11 | +E.g. we might have : |
| 12 | + |
| 13 | + n:rand(5)+2; |
| 14 | + |
| 15 | +in the question variables field. The question is instantiated, and then later we need to create some CAS text, e.g. feedback, in which we have : |
| 16 | + |
| 17 | + You were asked to find the integral of \((x+2)^{@n@}\) with respect to \(x\). In fact .... |
| 18 | + |
| 19 | +Here, we need the CAS text to be able to construct the text, with the previously evaluated value of `n`. This need is also why the CAS session returns the *value* of the variables as well as a displayed form. It is used here. |
| 20 | + |
| 21 | +## CAS session |
| 22 | + |
| 23 | +This class actually calls the CAS itself. The basic ideas is to take a list of maxima commands, including assignments of the form. |
| 24 | + |
| 25 | + key:rawvalue |
| 26 | + |
| 27 | +and executes this list in a single Maxima session. The results of this are then captured and fed back into the CAS strings so we essentially have data in the form: |
| 28 | + |
| 29 | + key => |
| 30 | + value |
| 31 | + display |
| 32 | + error |
| 33 | + |
| 34 | +An important point here is that expressions (values) can refer to previous keys. This is one reason why we can't tie teachers down to a list of allowed functions. They will be defining variable and function names. We have implemented a lazy approach where the connection to the CAS is only made, and automatically made, when we ask for the values, display form or error terms for a variable. And we don't generate display and value unless they are needed later. E.g. intermediate values do not create LaTeX. |
| 35 | + |
| 36 | + |
| 37 | +## Answer tests |
| 38 | + |
| 39 | +The answer tests essentially compare two expressions. These may accept an option, e.g. the number of significant figures. |
| 40 | +Details of the current answer tests are available [elsewhere](../Authoring/Answer_Tests/index.md). The result of an answer test should be |
| 41 | + |
| 42 | +1. Boolean outcome, true or false, |
| 43 | +2. errors, |
| 44 | +3. feedback, |
| 45 | +4. note. |
| 46 | + |
| 47 | +# Other concepts |
| 48 | + |
| 49 | +## Validity |
| 50 | + |
| 51 | +There are a number of reasons why a CAS expression needs to be "valid". These are |
| 52 | + |
| 53 | +1. security, |
| 54 | +2. stability, |
| 55 | +3. error trapping, |
| 56 | +4. pedagogic reasons, specific to a particular question. |
| 57 | + |
| 58 | +## Single call PRTs and simplification |
| 59 | + |
| 60 | +As of STACK 4.4 we make a single Maxima call to exectue an entire PRT. |
| 61 | +This reduces significantly the number of separate calls to maxima, which |
| 62 | +is a significant efficienty boost for more complex questions. |
| 63 | + |
| 64 | +Some answer tests rely on "unsimplified" expressions with a "what you see |
| 65 | +is what you get" approach. |
| 66 | + |
| 67 | +This example illustrates the issue. The teacher computes the answer to their |
| 68 | +question, e.g. find \(\int_{0}^{1} {\frac{{\left(1-x\right)}^4\cdot x^4}{x^2+1}} \mathrm{d}x\), |
| 69 | +with the Maxima code |
| 70 | + |
| 71 | + p1:int(x^4*(1-x)^4/(1+x^2),x,0,1); |
| 72 | + |
| 73 | +using simplification (obviously) at the start of the quetion variables, and this simplified expression is used |
| 74 | +by the PRT. The answer, \(\frac{22}{7}-\pi\), is held internally in "simplified" form. |
| 75 | +The Maxima string output is `22/7-%pi` but internally the answer is actually the following |
| 76 | + |
| 77 | + ((MPLUS SIMP) ((RAT SIMP) 22 7) ((MTIMES SIMP) -1 $%PI)) |
| 78 | + |
| 79 | +You can see the internal tree structure of a Maxima expression with the following code. |
| 80 | + |
| 81 | + (simp:true, p1:22/7-%pi, ?print(p1)); |
| 82 | + |
| 83 | +Rather than `22/7-%pi` the internal structure is really closer to this: `rat(22,7)+ (-1*%pi)`. |
| 84 | + |
| 85 | +On the other hand, a student types in the expression `22/7-%pi` and we deal with this without simplification. |
| 86 | +The internal Maxima expression is now |
| 87 | + |
| 88 | + (simp:false, p1:22/7-%pi, ?print(p1)); |
| 89 | + |
| 90 | +which gives a different internal structure |
| 91 | + |
| 92 | + ((MPLUS) ((MQUOTIENT) 22 7) ((MMINUS) $%PI)) |
| 93 | + |
| 94 | +which might best be thought of as `22/7+-(%pi)` where `-` is now a function of a single argument. |
| 95 | + |
| 96 | +In this example, the unsimplified `MQUOTIENT` and simplified `RAT SIMP` are not particularly problematic. However, the difference between `-%pi` and `-1*%pi` is seriouly problematic. Indeed, this kind of distinction is exactly what some |
| 97 | +answer tests, e.g. `EqualComAss` and `CasEqual`, are designed to establish. These tests will not work with a mix of simplified and unsimplified expressions, even if to the user they look completely identical! |
| 98 | + |
| 99 | +The solution to this problem is to "rinse" away any maxima internal simplification by using the Maxima `string` function to return the expression to the top level which a user would expect to type. This process corresponds to what happend in older versions of Maxima in which expressions were routinely passed between Maxima and PHP, with the string representation being used. |
| 100 | + |
| 101 | +Some expressions (lists, matrices) are passed by reference in Maxima, so even if the teacher's answer is created without simplification in the first instance, when it is evaluated by the answer tests there is a risk of it becoming simplified when it is later compared by an answer test function. |
| 102 | + |
| 103 | +## Wrinkles: `ordlerless` and `ordergreat` |
| 104 | + |
| 105 | +The Maxima functions `ordlerless` and `ordergreat` can only be executed once. In a Maxima desktop session try |
| 106 | + |
| 107 | + orderless(a,b); |
| 108 | + orderless(x,y); |
| 109 | + |
| 110 | +This will result in a Maxima error "orderless: reordering is not allowed.". |
| 111 | + |
| 112 | +These functions only take effect outside the current block. Try executing the following two calls in a clean Maxima desktop session. |
| 113 | + |
| 114 | +```` |
| 115 | +block( |
| 116 | + ordergreat(a,b), |
| 117 | + expand((a+b)^2) |
| 118 | +); |
| 119 | +
|
| 120 | +expand((a+b)^2); |
| 121 | +```` |
| 122 | + |
| 123 | +Notice that the requested ordering (`a` before `b`) is implemented once the first block is completed, only in the second command. You might expect _both_ commands to respect the requested ordering. |
| 124 | + |
| 125 | +This is a problem in STACK. STACK creates a block to manage execution of commands, including evaluation of the question variables, construction of castext and execution of PRTs. Any call to these functions will therefore be inside a block containing all the other commands. |
| 126 | + |
| 127 | +STACK makes an exception for `ordlerless` and `ordergreat`. However, the work-around means STACK only supports single and simple uses. You must give an explicit call to these functions. You cannot create a list of variables, and then apply `ordlerless` and `ordergreat`. By design expressions like |
| 128 | + |
| 129 | + apply(ordergreat, random_permuation([a,b,c])); |
| 130 | + |
| 131 | +will not work in STACK (but similar constructs will work in the desktop). |
| 132 | + |
| 133 | +See these issue |
| 134 | + |
| 135 | +* https://github.com/maths/moodle-qtype_stack/issues/1384 |
| 136 | +* https://github.com/maths/moodle-qtype_stack/issues/1241 |
| 137 | +* https://github.com/maths/moodle-qtype_stack/issues/1207 |
0 commit comments