Skip to content

Commit

Permalink
Merge branch 'dev' of https://github.com/maths/moodle-qtype_stack int…
Browse files Browse the repository at this point in the history
…o iss1085
  • Loading branch information
EJMFarrow committed Jan 7, 2025
2 parents 239faf0 + 48104f5 commit befcf39
Show file tree
Hide file tree
Showing 31 changed files with 373 additions and 101 deletions.
1 change: 1 addition & 0 deletions .github/workflows/moodle-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ jobs:
moodle-branch: 'MOODLE_405_STABLE'
database: 'pgsql'
maxima: 'SBCL'
moodle-app: true
- php: '8.2'
moodle-branch: 'MOODLE_404_STABLE'
database: 'pgsql'
Expand Down
14 changes: 10 additions & 4 deletions doc/dev/Updating_JSXGraph.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
# Updating JSXGraph

Since the stack-js update, we have been able to use JSXGraph in its released form.
JSXGraph is used in its released form. The files related to it are now stored under `corsscripts/`.

The files related to it are now stored under `corsscripts/`.
Download JSXGraph: [https://github.com/jsxgraph/jsxgraph](https://github.com/jsxgraph/jsxgraph).

Download JSXGraph from here: [https://github.com/jsxgraph/jsxgraph](https://github.com/jsxgraph/jsxgraph).
1. Copy over minified files `jsxgraph.min.css` and `jsxgraphcore.min.js`, there is no need to copy the non minified versions.
2. Add frozen CDN URLs to the named version map in stack/cas/castext2/blocks/jsxgraph.block.php.
3. Test that the healthcheck example works, the binding in particular and the render of the formula.
4. Test the binding in more detail by checking that samplequestions/stack_jxg.binding-demo-4.4.xml still does sensible things.
5. If all looks good and keeps looking good after Moodle JavaScript reset, NOOP edit of question to recompile it and running in private-mode/incognito-mode to ensure that things are new and not coming from any cache. Then things should be fine.
6. Check Meclib materials, and further testing!

The files one needs to copy over are `jsxgraph.min.css` and `jsxgraphcore.min.js`, there is no need to copy the non minified versions.
An example commit is https://github.com/maths/moodle-qtype_stack/commit/409cd0960f003e80d81a982fb96d6f7c310576de

The old STACK side `jsxgraph.js` that provided the `stack_jxg` features is now called `stackjsxgraph.js` and is being served from that same CORS-header tuning directory with that specific script. (Minification can be done using uglify-js:
`npm install -g uglify-js`
Expand All @@ -18,3 +23,4 @@ We do not apply Moodles or any other systems JavaScript processing on these, no

We really want to have a local JSXGraph copy instead of relying on a CDN version. We want to make it easy to run STACK in a closed network with no external requirements and having a local JSXGraph is one of the things we do to remove an external requirement.

One can always just state that a particular `[[jsxgraph]]` block should use the official CDN version by setting `[[jsxgraph version="cdn"]]` or any specific other versions by tuning `[[jsxgraph overridecss="..." overridejs="..."]]` separately. Then, one can test if things work, but then one needs to check that one is testing with the correct version every time one tests...
4 changes: 2 additions & 2 deletions doc/en/Authoring/Answer_Tests/Equivalence.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ This is the most commonly used test. The pseudo code
This test will work with a variety of [types of object](../../CAS/Maxima_background.md#Types_of_object) of mathematical objects, including lists, sets, equations, inequalities and matrices.

* This test disregards whether [simplification](../../CAS/Simplification.md) is switched on, it always fully simplifies all its arguments.
* Use `AlgEquiv(predicate(ex),true)` with [predicate functions](../../CAS/Predicate_functions.md).
* Use `AlgEquiv(predicate(ex),true)` with [predicate functions](../../CAS/Predicate_functions.md), to test the result is true. But note that many predicate functions only work without simplification! In particular, testing for "form" such as scientific notation need `simp:false`, in which case use `EqualComAss(predicate(ex),true)` rather than algebraic equivalence.

Note: exactly what it does depends on what objects are given to it. In particular the pseudo code above only applies to expressions. We cannot subtract one list or set from another, so we have to use other tests.
Note: exactly what this answer test does depends on what objects are given to it. In particular the pseudo code above only applies to expressions. We cannot subtract one list or set from another, so we have to use other tests.

For sets, the CAS tries to write the expression in a canonical form. It then compares the string representations these forms to remove duplicate elements and compare sets. This is subtly different from trying to simplify the difference of two expressions to zero. For example, imagine we have \(\{(x-a)^{6000}\}\) and \(\{(a-x)^{6000}\}\). One canonical form is to expand out both sides. While this work in principal, in practice this is much too slow for assessment.

Expand Down
40 changes: 37 additions & 3 deletions doc/en/Authoring/Answer_Tests/Rule_based.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ This is an advanced test.

This test allows question authors to create equivalence classes based on equality up to associativity and commutativity with the addition of optional rules. For example, the teacher can include the identity operations of addition and multiplication: \(0+ x\rightarrow x\) and \(1\times x\rightarrow x\). This makes it much easier to establish things like \(0-1\times i\) is equivalent to \(-i\). However, more general integer arithmetic is still not automatically included so \(2\times 3 \neq 6\).

This test always assumes associativity and commutativity of addition and multiplication. Essentially this test extends the `EqualComAss` test by adding in additional rules. Without assumptions of commutativity and associativity we would need all sorts of additional rules, such as \(x+0 \rightarrow x\), since without commutativity this would not be captured by the rule `zeroAdd`, i.e. \(0+x \rightarrow x\). Furthermore, the way `EqualComAss` deals with unary minus and division make associativity and commutativity difficult to add in their pure form.
This test always assumes associativity of addition and multiplication. By default the test assumes commutativity of addition and multiplication, but this can be dropped. Essentially this test extends the `EqualComAss` test by adding in additional rules. Without assumptions of commutativity and associativity we would need all sorts of additional rules, such as \(x+0 \rightarrow x\), since without commutativity this would not be captured by the rule `zeroAdd`, i.e. \(0+x \rightarrow x\). Furthermore, the way `EqualComAss` deals with unary minus and division make associativity and commutativity difficult to add in their pure form.

Each rule is a named function in Maxima, and each rule has an associated predicate function to decide if the rule is applicable at the top level of an expression. E.g. `zeroAddp(0+x)` would return `true` and `zeroAdd(0+x)` would return `x`.

Expand All @@ -46,6 +46,9 @@ The teacher must supply an option consisting of a list of the following rule nam
| `assMul` | Associativity of multiplication |
| `comAdd` | Commutativity of addition |
| `comMul` | Commutativity of multiplication |
| - | _Options to switch off the defaults_ |
| `noncomAdd` | Indicate addition is non-commutative |
| `noncomMul` | Indicate multiplication is non-commutative |
| (`ID_TRANS`) | |
| `zeroAdd` | \(0+x \rightarrow x\) |
| `zeroMul` | \(0\times x \rightarrow 0\) |
Expand Down Expand Up @@ -78,6 +81,8 @@ However \(y-x\) is never ordered as \(-x+y\). Furthermore, \(-(x-y) \neq -x+y\)
Factoring out is better than distributing here, since in a produce such as \(-(x-y)(x-z)\) it is not clear which term in the product the initial minus sign will end up in.
Since `negOrd` is a factor command, it is incompatible with `negDist`.

By default the test assumes commutativity of addition and multiplication. If you choose the `nonmulCom` rule then you can switch off commutativity of multiplication. However, rules such as `zeroMul` include both \(0\times x \rightarrow 0\) and \(x\times 0 \rightarrow 0\). The rules `intMul` (etc) would appear to be non-compatible with `nonmulCom`, however they are very useful in that by performing integer arithmetic we bring integers to the front of the expression.

For convenience sets of rules can be specified. E.g. you can use the name `ID_TRANS` in place of the list `[zeroAdd,zeroMul,oneMul,oneDiv,onePow,idPow,zeroPow,zPow]` to include all of the basic identity operators.

If you want to remove tests from a list you can use code such as `delete(zeroAdd, ID_TRANS)`.
Expand Down Expand Up @@ -110,9 +115,38 @@ Imagine the teacher's answer is \(\frac{\sin(3x)}{2}\) but a student types in \(

This functionality was introduced in April 2021. It is essential that the rules, and any combination of the rules, can only proceed in a single direction and that no infinite loops are created. So, `intAdd` is fine because adding together two integers will make an expression _simpler_ which in this case is shorter. For this reason we do not have expanding out (i.e. distribution) rules in the above set, and no rules of indices (which readily lead to mathematical errors). Use Maxima's simplifier if you want to include such rules.

The rules names are Maxima functions, but they assume `simp:false` and that the expression has noun forms e.g. `nounadd` instead of `+`. You can use `equals_commute_prepare(ex)` to change an expression into this noun form. The goal of this code is to create reliable equivalence classes of expressions, not perform algebraic manipulation as we traditionally know it. In particular the way unary minus is transformed into multiplication with a special tag `UNARY_MINUS` is likely to cause confusion to students if an expression is manipulated using these rules and then shown to a student. The transformation is designed to go in one direction only, and we do not support displaying the resulting manipulated expressions in traditional form.
STACK creates parallel operators for `*`, `+` etc. so that we have full control over the simplifier and the rules in play. E.g. `nounadd` instead of `+`.

You can use `equals_commute_prepare(ex)` to change an expression into this noun form. An optional second argument `equals_commute_prepare(ex, sc)` is the set of operators considered commutative, e.g. typically a subset of `{"nouneq", "nounand", "nounor", "nounset", "nounadd", "nounmul"}`.

The simplifier rules assume `simp:false` and that the expression has noun forms.
The simplifier rule names are Maxima functions with exactly the names shown above.
Each rule has a predicate function, which decides if the rule can be applied _at the top level of the expression_. E.g. `oneMulp(1*x)` is true, but `oneMulp(2+(1*x))` is false because the `1*x` is not at the top level of the expression `2+(1*x)`, it's deeper within the expression.

To deal with unary minus we transform it into multiplication with a special tag `UNARY_MINUS`. For example `-x` becomes `UNARY_MINUS * x`. This approach looks odd at first, but does not confuse `UNARY_MINUS` with the integer \(-1\) or the unary function "minus". In this way multiple unary minus operations commute to the front of an expression. E.g. `(-x)*(-y) = UNARY_MINUS * UNARY_MINUS * x * y` (when `*` is assumed to be commutative and associative, of course!)

Similarly, division is also conveted to `UNARY_RECIP`. E.g. `(-x)/(-y) = UNARY_MINUS nounmul UNARY_RECIP(UNARY_MINUS nounmul y) nounmul x`.


We the use the rule `negDiv` to pull out the unary minus outside the devision (pulls `UNARY_MINUS` outside `UNARY_RECIP`), but we also need the rules `assMul` (associativity) and `comMul` (commutativity). E.g. try the following in the STACK-maxima sandbox.

ex:(-x)/(-y);
ex:equals_commute_prepare(ex);
transl(ex,[assMul, comMul, negDiv]);

This results in `UNARY_MINUS nounmul UNARY_MINUS nounmul x nounmul UNARY_RECIP(y)`, literally `- * - * x * 1/y`. We could also include the rule `negNeg` to remove the double minus.

transl(ex,[assMul, comMul, negDiv, negNeg]);

gives `x nounmul UNARY_RECIP(y)`.

The goal of this code is to create reliable equivalence classes of expressions, not perform algebraic manipulation as we traditionally know it. In particular the use of `UNARY_MINUS` and `UNARY_RECIP` are likely to cause confusion to students if an expression is manipulated using these rules and then shown to a student. The function `verb_arith` removes all the noun forms used by this simplfier, translating the expression back to core Maxima functions. Note however that `UNARY_MINUS` and `UNARY_RECIP(ex)` are replaced by `(-1)*` and `ex^(-1)` respectively. E.g. `verb_arith(UNARY_MINUS nounmul x nounmul UNARY_RECIP(y)) = (-1)*x*y^-1`.

The simplfier is designed to go in one direction only to establish membership of an equivalence class. We do not (as of Dec 2024) support displaying the resulting manipulated expressions in traditional form.

The code is all in `stack/maxima/noun_simp.mac`.

__As of May 2023, these rules are not intended as an end-user simplifier and we do not currently support user-defined rules (sorry!).__
__We do not currently support user-defined rules (sorry!).__

# See also

Expand Down
1 change: 1 addition & 0 deletions doc/en/Authoring/Inputs/Input_options.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Normally a _blank_, i.e. empty, answer has a special status and are not consider
* String inputs will return the empty string `""` as an empty answer (to avoid a type-mismatch).
* Textarea inputs will return `[EMPTYANSWER]` to make sure the answer is always a list (to avoid a type-mismatch).
* Matrix inputs will return the correct size matrix filled with `null` atoms, e.g. `matrix([null,null],[null,null])`.
* Checkbox inputs will return `[]` to make sure the answer is always a list (to avoid a type-mismatch).

We strongly recommend (with many years of experience) that teachers do not use this option without very careful thought!

Expand Down
3 changes: 3 additions & 0 deletions doc/en/Authoring/Inputs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,12 @@ If you wish to forbid commas, then escape it with a backslash.
There are groups of common keywords which you can forbid simply as

* `[[BASIC-ALGEBRA]]` common algebraic operations such as `simplify`, `factor`, `expand`, `solve`, etc.
* `[[BASIC-TRIG]]` names of all the trig and hyperbolic trig functions and their inverses, e.g. `sin`, `asin`, `sinh`, `asinh`, etc.
* `[[BASIC-CALCULUS]]` common calculus operations such as `int`, `diff`, `taylor`, etc.
* `[[BASIC-MATRIX]]` common matrix operations such as `transpose`, `invert`, `charpoly`, etc.

These list are hard-wired into the code [here](https://github.com/maths/moodle-qtype_stack/blob/master/stack/cas/cassecurity.class.php#L56).

If you have suggestions for more lists, or additional operations which should be added to the existing lists, please contact the developers.


Expand Down
12 changes: 10 additions & 2 deletions doc/en/Authoring/Question_options.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,15 +168,23 @@ See the entry on [matrices](../CAS/Matrix.md#matrixparens).

### Inline and displayed fractions. ###

The display of fractions can take two forms: inline \( 1/x \) and displayed \( \frac{1}{x} \).
There are three ways to display fractions.

1. displayed \( \frac{1}{x} \);
2. inline \( 1/x \);
3. negative powers \( x^{-1} \).

The default behaviour is displayed, i.e. using LaTeX `\frac{}{}`.

The function `stack_disp_fractions(ex)` can be used to control the display.
The function `stack_disp_fractions(ex)` can be used to control the display globally within a question.

* `stack_disp_fractions("i")` switches display to inline.
* `stack_disp_fractions("d")` switches display to display.

Note, for CASText the display is controlled by the prevailing setting at the moment the text is displayed, not when a variable is defined in the question variables. Hence, if you would like a single inline fraction within a CASText you will need to use

Normally fractions are displayed {@1/x@}. This switches to inline {@(stack_disp_fractions("i"), 1/x)@}, which persists {@1/a@}. Switch explicitly back to displayed {@(stack_disp_fractions("d"),1/x)@}.

For scientific units we also have an input "extra option" `negpow` for student's input to be displayed as negative powers, e.g. \(m\,s^{-1}\).

We do not, currently, have support for global display of fractions using negative powers (Dec 2024). This is because the difference between displayed and inline fractions is purely notational, involving the TeX output from the division operator. Converting division to negative powers is a mathematical re-write rule and is therefore significantly more complicated. E.g. we would have to decide how to display \( \frac{1}{x^{-2}} \).
24 changes: 13 additions & 11 deletions doc/en/CAS/Numbers.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,18 +87,19 @@ To force all floating point numbers to decimal floating point numbers use

You can also force all integers to be displayed as floating point decimals or in scientific notation using `stackintfmt` and the appropriate template. This function calls the LISP `format` function, which is complex and more example are available [online](http://www.gigamonkeys.com/book/a-few-format-recipes.html) elsewhere.

| Template | Input | TeX Output | Description/notes
| Template | Input | TeX Output | Description/notes
| ----------- | ----------- | ---------------- | ----------------------------------------------------------------------------------------------
| `"~,4f"` | `0.12349` | \(0.1235\) | Output four decimal places: floating point.
| | `0.12345` | \(0.1234\) | Note the rounding.
| | `0.12` | \(0.1200\) |
| `"~,5e"` | `100.34` | \(1.00340e+2\) | Output five decimal places: scientific notation.
| `"~:d"` | `10000000` | \(10,000,000\) | Separate decimal groups of three digits with commas.
| `~r` | `9` | \(\text{nine}\) | Rhetoric.
| `~:r` | `9` | \(\text{ninth}\) | Ordinal rhetoric.
| `~7r` | `9` | \(12\) | Base 7.
| `~@r` | `9` | \(IX\) | Roman numerals.
| `~:@r` | `9` | \(VIIII\) | Old style Roman numerals.
| `"~,4f"` | `0.12349` | \(0.1235\) | Output four decimal places: floating point.
| | `0.12345` | \(0.1234\) | Note the rounding.
| | `0.12` | \(0.1200\) |
| `"~,5e"` | `100.34` | \(1.00340e+2\) | Output five decimal places: scientific notation.
| `"~:d"` | `10000000` | \(10,000,000\) | Separate decimal groups of three digits with commas.
| `"~,,\' ,:d"` | `10000000` | \(10\ 000\ 000\) | Separate decimal groups of three digits with spaces.
| `~r` | `9` | \(\text{nine}\) | Rhetoric.
| `~:r` | `9` | \(\text{ninth}\) | Ordinal rhetoric.
| `~7r` | `9` | \(12\) | Base 7.
| `~@r` | `9` | \(IX\) | Roman numerals.
| `~:@r` | `9` | \(VIIII\) | Old style Roman numerals.

There are many other options within the LISP format command. Please note with the rhetoric and Roman numerals that the numbers will be in LaTeX mathematics environments.

Expand Down Expand Up @@ -155,3 +156,4 @@ The following commands generate displayed forms of numbers. These will not be m
| `anyfloatex(ex)` | Decides if any floats are in the expression.
| `scientific_notationp(ex)` | Determines if \(ex\) is written in the form \(a10^n\) where \(a\) is an integer or float, and \(n\) is an integer.

Please note that these predicate functions need to be used with `simp:false`. Some answer tests, including the default algebraic equivalence (`ATAlgEquiv`) always simplify their arguments. Instead use a non-simplifying answer test such as `EqualComAss`.
1 change: 1 addition & 0 deletions doc/en/Developer/Development_track.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ We use the [github issue tracker](https://github.com/maths/moodle-qtype_stack/is

Done.
1. Introduce `ta` as the default teacher's answer in the question variables, and use this in the input and default prt.
2. Support `allowrmpty` for dropdown, radio and checkbox inputs.

Issues with [github milestone 4.9.0](https://github.com/maths/moodle-qtype_stack/issues?q=is%3Aissue+milestone%3A4.9.0) include

Expand Down
Loading

0 comments on commit befcf39

Please sign in to comment.