Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2608,11 +2608,40 @@ the right shape, written out or once its operations are carried out, throws `Inv
|---|---|---|
| `"det([[1, 2], [3, 4]])".ToEntity()` | `det * [[1, 2], [3, 4]]` | `-2` |
| `"transpose([1, 2])".ToEntity()` | `transpose * [1, 2]` | `[[1, 2]]`, the row |
| `"transpose(x)".ToEntity()` | `transpose * x` | throws `InvalidArgumentParseException` |
| `"transpose(x)".ToEntity()` | `transpose * x` | the node `transpose(x)`, see the entry below |
| `"cross([a, b])".ToEntity()` | `cross * [a, b]` | `[-b, a]` |
| `"dot([1, 2], [3, 4])".ToEntity()` | throws `UnhandledParseException` | `1 * 3 + 2 * 4` |
| `"resultant(x^2 - 1, x - a, x)".ToEntity()` | throws `UnhandledParseException` | `a ^ 2 - 1` |

### `det`, `transpose`, `trace` and an entry `A[k, l]` of a name for a matrix are nodes

**Not silent: these were read as products, or did not parse, and are now nodes, carried out once the name is a
matrix.** `det(A)`, `transpose(A)` and `trace(A)` of a name, or of an expression that is not yet a matrix, are the
nodes `Determinantf`, `Transposef` and `Tracef`; `A[k, l]` is `MatrixEntryf`, the entry in row `k` and column `l`
counted from 1, and did not parse at all. Each is computed when the name is substituted by a matrix, by `Simplify`,
`Evaled` and a compiled delegate taking `double[,]` or `Complex[,]`. Of a matrix written out each is computed as it is
parsed, as `det` and `transpose` already were, and `trace` is new. A matrix that is not square has no determinant
and no trace, and an index out of range names no entry: the node is left, never a number. A number, a statement or
a set is refused with `InvalidArgumentParseException`. A product with a transpose in it is no longer reordered by
`Simplify`, since matrices do not commute: `transpose(A) * A - A * transpose(A)` is not `0`. A name beside a written
matrix, as in `det(x * [[1, 2], [3, 4]])`, may stand for a matrix too, and such an argument is not carried out until
it is one, where it was multiplied into the entries as a number
([#2147](https://github.com/asc-community/AngouriMath/issues/2147)).

| Input | Was (2.6.0) | Now |
|---|---|---|
| `"det(A)".ToEntity()` | `det * A` | `det(A)`, a `Determinantf` |
| `"transpose(A)".ToEntity()` | `transpose * A` | `transpose(A)`, a `Transposef` |
| `"transpose(A) * B".ToEntity()` | `transpose * A * B` | `transpose(A) * B` |
| `"trace(A)".ToEntity()` | `trace * A` | `trace(A)`, a `Tracef` |
| `"trace([[1, 2], [3, 4]])".ToEntity()` | `trace * [[1, 2], [3, 4]]` | `1 + 4` |
| `"trace(A * B)".Simplify()` | `A * B * trace` | `trace(A * B)` |
| `"A[1, 2]".ToEntity()` | throws `UnhandledParseException` | `A[1, 2]`, a `MatrixEntryf` |
| `"[[1, 2], [3, 4]][2, 1]".ToEntity()` | throws `UnhandledParseException` | `3` |
| `"det(5)".ToEntity()` | `det * 5` | throws `InvalidArgumentParseException` |
| `"det(x * [[1, 2], [3, 4]])".ToEntity()` | `det * x * [[1, 2], [3, 4]]` | `det(x * [[1, 2], [3, 4]])`: `x` may stand for a matrix |
| `"det(A)".Substitute("A", "[[1, 2], [3, 4]]").Evaled` | `[[det, det * 2], [det * 3, det * 4]]` | `-2` |

### The names of functions to come are refused by name

**Not silent: these names followed by a bracket now throw `UnrecognizedFunctionParseException` where they were read as
Expand Down
9 changes: 9 additions & 0 deletions Sources/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1075,3 +1075,12 @@ file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed

[Tests/UnitTests/Common/BranchCutCompilationTest.cs]
file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n

[AngouriMath/Core/Entity/Omni/Entity.MatrixOperations.Classes.cs]
file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n

[Tests/UnitTests/Common/MatrixOperationNodesTest.cs]
file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n

[AngouriMath/Functions/Simplification/ProductsOfMatrices.cs]
file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n
46 changes: 46 additions & 0 deletions Sources/AngouriMath/Convenience/MathS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6004,6 +6004,52 @@ public static Matrix PointwiseMultiplication(Matrix m1, Matrix m2)
public static Matrix Matrix(int rows, int columns, params Entity[] values) =>
new(GenTensor.CreateMatrix(rows, columns, (x, y) => values[x * columns + y]));

/// <summary>The determinant <c>det(A)</c> of a square matrix, as a node.</summary>
/// <param name="matrix">The matrix, written out or a name that stands for one.</param>
/// <returns>The <see cref="Entity.Determinantf"/> node, computed once its argument is a matrix</returns>
/// <remarks>
/// A matrix that is not square has no determinant, and the node is left as it is.
/// https://github.com/asc-community/AngouriMath/issues/2147
/// </remarks>
/// <example>
/// <code>
/// var det = MathS.Matrices.Determinant("A");
/// Console.WriteLine(det);
/// Console.WriteLine(det.Substitute("A", "[[1, 2], [3, 4]]").Evaled);
/// </code>
/// Prints
/// <code>
/// det(A)
/// -2
/// </code>
/// </example>
public static Entity Determinant(Entity matrix) => new Determinantf(matrix);

/// <summary>The transpose <c>transpose(A)</c>, as a node. A vector is a column, and its transpose a row.</summary>
/// <param name="matrix">The matrix, written out or a name that stands for one.</param>
/// <returns>The <see cref="Entity.Transposef"/> node, computed once its argument is a matrix</returns>
/// <remarks>https://github.com/asc-community/AngouriMath/issues/2147</remarks>
public static Entity Transpose(Entity matrix) => new Transposef(matrix);

/// <summary>The trace <c>trace(A)</c> of a square matrix, the sum of its diagonal entries, as a node.</summary>
/// <param name="matrix">The matrix, written out or a name that stands for one.</param>
/// <returns>The <see cref="Entity.Tracef"/> node, computed once its argument is a matrix</returns>
/// <remarks>
/// A matrix that is not square has no trace, and the node is left as it is.
/// https://github.com/asc-community/AngouriMath/issues/2147
/// </remarks>
public static Entity Trace(Entity matrix) => new Tracef(matrix);

/// <summary>The entry <c>A[i, j]</c> of a matrix, its rows and columns counted from 1, as a node.</summary>
/// <param name="matrix">The matrix, written out or a name that stands for one.</param>
/// <param name="row">The row, from 1.</param>
/// <param name="column">The column, from 1.</param>
/// <returns>The <see cref="Entity.MatrixEntryf"/> node, computed once its argument is a matrix and its indices are whole numbers in range</returns>
/// <remarks>
/// An index out of range names no entry, and the node is left as it is.
/// https://github.com/asc-community/AngouriMath/issues/2147
/// </remarks>
public static Entity Entry(Entity matrix, Entity row, Entity column) => new MatrixEntryf(matrix, row, column);
}

/// <summary>
Expand Down
26 changes: 18 additions & 8 deletions Sources/AngouriMath/Core/Antlr/AngouriMath.g
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,22 @@ options
// So we put UPPER_CASE lexer rules after lower_case parser rules.

// atom is defined later
/* An entry of a matrix, `A[i, j]`, its row and its column counted from 1 as a_ij is. The brackets
follow what they index directly, and a bracket after an atom was not a parse before, so
nothing that parsed reads differently. https://github.com/asc-community/AngouriMath/issues/2147 */
indexed_atom returns[Entity value]
: p = atom { $value = $p.value; }
('[' args = function_arguments ']' { $value = ParsingHelpers.Entry($value, $args.list); })*
;

factorial_expression returns[Entity value]
: p = atom '!' { $value = MathS.Factorial($p.value); }
: p = indexed_atom '!' { $value = MathS.Factorial($p.value); }
/* The double factorial, n (n - 2) (n - 4) ..., which is not (n!)!: that keeps its parentheses.
Three marks are the triple factorial in the literature, which the library does not have, so
they are refused rather than read as (n!!)!. https://github.com/asc-community/AngouriMath/issues/2182 */
| p = atom '!!' { $value = MathS.DoubleFactorial($p.value); }
| p = atom '!!!' { throw new UnrecognizedFunctionParseException("n!!! is the triple factorial, which AngouriMath does not have; write (n!!)! for the factorial of a double factorial"); }
| p = atom { $value = $p.value; }
| p = indexed_atom '!!' { $value = MathS.DoubleFactorial($p.value); }
| p = indexed_atom '!!!' { throw new UnrecognizedFunctionParseException("n!!! is the triple factorial, which AngouriMath does not have; write (n!!)! for the factorial of a double factorial"); }
| p = indexed_atom { $value = $p.value; }
;

power_list returns[List<Entity> value]
Expand Down Expand Up @@ -653,12 +661,14 @@ atom returns[Entity value]
| 'Ci(' args = function_arguments ')' { Assert("Ci", 1, $args.list.Count); $value = MathS.Ci($args.list[0]); }
| 'Shi(' args = function_arguments ')' { Assert("Shi", 1, $args.list.Count); $value = MathS.Shi($args.list[0]); }
| 'Chi(' args = function_arguments ')' { Assert("Chi", 1, $args.list.Count); $value = MathS.Chi($args.list[0]); }
/* The determinant, the transpose, the scalar and the vector product, and the resultant, each
computed from what it is applied to: there is no node for any of them, so a matrix or a
vector has to be written out, or be one once its operations are carried out.
https://github.com/asc-community/AngouriMath/issues/2147 */
/* The determinant, the transpose, the trace, the scalar and the vector product, and the
resultant, each computed from what it is applied to where that is a matrix or a vector
written out, or one once its operations are carried out. det, transpose and trace of
anything else that may stand for a matrix, as a name does, are nodes, computed once it is
one. https://github.com/asc-community/AngouriMath/issues/2147 */
| 'det(' args = function_arguments ')' { Assert("det", 1, $args.list.Count); $value = ParsingHelpers.Determinant($args.list[0]); }
| 'transpose(' args = function_arguments ')' { Assert("transpose", 1, $args.list.Count); $value = ParsingHelpers.Transposed($args.list[0]); }
| 'trace(' args = function_arguments ')' { Assert("trace", 1, $args.list.Count); $value = ParsingHelpers.Trace($args.list[0]); }
| 'dot(' args = function_arguments ')' { Assert("dot", 2, $args.list.Count); $value = ParsingHelpers.Dot($args.list[0], $args.list[1]); }
| 'cross(' args = function_arguments ')' { Assert("cross", (1, 2), $args.list.Count); $value = ParsingHelpers.Cross($args.list); }
| 'laplacian(' args = function_arguments ')' { Assert("laplacian", 2, $args.list.Count); $value = ParsingHelpers.Laplacian($args.list[0], $args.list[1]); }
Expand Down
9 changes: 6 additions & 3 deletions Sources/AngouriMath/Core/Antlr/AngouriMath.interp

Large diffs are not rendered by default.

202 changes: 102 additions & 100 deletions Sources/AngouriMath/Core/Antlr/AngouriMath.tokens
Original file line number Diff line number Diff line change
Expand Up @@ -227,73 +227,74 @@ T__225=226
T__226=227
T__227=228
T__228=229
NEWLINE=230
NUMBER=231
SPECIALSET=232
BOOLEAN=233
NAN=234
VARIABLE=235
COMMENT=236
WS=237
'!'=1
'!!'=2
'!!!'=3
'^'=4
'-'=5
'+'=6
'*'=7
'...'=8
'…'=9
'/'=10
'mod'=11
'intersect'=12
'/\\'=13
'unite'=14
'\\/'=15
'setsubtract'=16
'\\'=17
'in'=18
'subset'=19
'⊆'=20
'superset'=21
'⊇'=22
'divides'=23
'|'=24
'>='=25
'<='=26
'>'=27
'<'=28
'='=29
'<>'=30
'≡'=31
'('=32
')'=33
'not'=34
'and'=35
'&'=36
'xor'=37
'or'=38
'implies'=39
'->'=40
'provided'=41
'=>'=42
':'=43
'forall'=44
'∀'=45
'exists!'=46
'∃!'=47
'exists'=48
'∃'=49
','=50
';'=51
'+oo'=52
'-oo'=53
'(|'=54
'|)'=55
'#'=56
'['=57
']T'=58
']'=59
T__229=230
NEWLINE=231
NUMBER=232
SPECIALSET=233
BOOLEAN=234
NAN=235
VARIABLE=236
COMMENT=237
WS=238
'['=1
']'=2
'!'=3
'!!'=4
'!!!'=5
'^'=6
'-'=7
'+'=8
'*'=9
'...'=10
'…'=11
'/'=12
'mod'=13
'intersect'=14
'/\\'=15
'unite'=16
'\\/'=17
'setsubtract'=18
'\\'=19
'in'=20
'subset'=21
'⊆'=22
'superset'=23
'⊇'=24
'divides'=25
'|'=26
'>='=27
'<='=28
'>'=29
'<'=30
'='=31
'<>'=32
'≡'=33
'('=34
')'=35
'not'=36
'and'=37
'&'=38
'xor'=39
'or'=40
'implies'=41
'->'=42
'provided'=43
'=>'=44
':'=45
'forall'=46
'∀'=47
'exists!'=48
'∃!'=49
'exists'=50
'∃'=51
','=52
';'=53
'+oo'=54
'-oo'=55
'(|'=56
'|)'=57
'#'=58
']T'=59
'{'=60
'}'=61
'log('=62
Expand Down Expand Up @@ -432,36 +433,37 @@ WS=237
'Chi('=195
'det('=196
'transpose('=197
'dot('=198
'cross('=199
'laplacian('=200
'forwarddifference('=201
'jacobian('=202
'hessian('=203
'resultant('=204
'conjugate('=205
'conj('=206
'arg('=207
'Re('=208
'Im('=209
're('=210
'real('=211
'im('=212
'imag('=213
'trunc('=214
'zeta('=215
'riemannzeta('=216
'hurwitzzeta('=217
'heaviside('=218
'unitstep('=219
'isprime('=220
'pochhammer('=221
'doublefactorial('=222
'factorial2('=223
'fractionalpart('=224
'quotient('=225
'domain('=226
'piecewise('=227
'apply('=228
'lambda('=229
'NaN'=234
'trace('=198
'dot('=199
'cross('=200
'laplacian('=201
'forwarddifference('=202
'jacobian('=203
'hessian('=204
'resultant('=205
'conjugate('=206
'conj('=207
'arg('=208
'Re('=209
'Im('=210
're('=211
'real('=212
'im('=213
'imag('=214
'trunc('=215
'zeta('=216
'riemannzeta('=217
'hurwitzzeta('=218
'heaviside('=219
'unitstep('=220
'isprime('=221
'pochhammer('=222
'doublefactorial('=223
'factorial2('=224
'fractionalpart('=225
'quotient('=226
'domain('=227
'piecewise('=228
'apply('=229
'lambda('=230
'NaN'=235
12 changes: 12 additions & 0 deletions Sources/AngouriMath/Core/Antlr/AngouriMathBaseListener.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ namespace AngouriMath.Core.Antlr {
[System.Diagnostics.DebuggerNonUserCode]
[System.CLSCompliant(false)]
internal partial class AngouriMathBaseListener : IAngouriMathListener {
/// <summary>
/// Enter a parse tree produced by <see cref="AngouriMathParser.indexed_atom"/>.
/// <para>The default implementation does nothing.</para>
/// </summary>
/// <param name="context">The parse tree.</param>
public virtual void EnterIndexed_atom([NotNull] AngouriMathParser.Indexed_atomContext context) { }
/// <summary>
/// Exit a parse tree produced by <see cref="AngouriMathParser.indexed_atom"/>.
/// <para>The default implementation does nothing.</para>
/// </summary>
/// <param name="context">The parse tree.</param>
public virtual void ExitIndexed_atom([NotNull] AngouriMathParser.Indexed_atomContext context) { }
/// <summary>
/// Enter a parse tree produced by <see cref="AngouriMathParser.factorial_expression"/>.
/// <para>The default implementation does nothing.</para>
Expand Down
Loading
Loading