diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index 0b8f3876c..4393a3827 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -520,6 +520,20 @@ now, which is the one silent change: a variable spelled `forall` or `exists` no | `forall + 1` | `forall + 1`, a variable named `forall` | `UnhandledParseException` | | `exists(x)` | `exists * x` | `UnhandledParseException` | +### A matrix compiles + +**Addition, not silent.** Under the LINQ compiler a `Matrix` node compiles to a +`GenTensor` over its elements' type, with the wrappers `DoubleOperations`, +`SingleOperations`, `ComplexOperations`, `Int32Operations`, `Int64Operations` and +`BigIntegerOperations` in `AngouriMath.Core.Compilation.IntoLinq`, and a sum, difference, product, +quotient or whole power with a matrix in it to GenericTensor's operation of that name +([#526](https://github.com/asc-community/AngouriMath/issues/526)). + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"[[x, 2 x], [x + 1, x^2]]".ToEntity().Compile>("x")` | `UncompilableNodeException` | a delegate; at `3` the matrix `[[3, 6], [4, 9]]` | +| `"A * B".ToEntity().Compile, GenTensor<…>, GenTensor<…>>("A", "B")` | `UncompilableNodeException` | the matrix product | +| `"[[1, 0]] * [[a, b], [c, d]] * [[0], [1]]".ToEntity().Compile("a", "b", "c", "d")` | `2` at `(1, 2, 3, 4)` — simplified to the scalar first | unchanged | ### `lcm` is a function, and a congruence is solved to a residue class `lcm(a, b, ...)` is a node, `Entity.Lcmf`, by the conventions of `gcd`: non-negative, `0` where diff --git a/Sources/.editorconfig b/Sources/.editorconfig index 110b0e774..2d05d05de 100644 --- a/Sources/.editorconfig +++ b/Sources/.editorconfig @@ -267,6 +267,12 @@ file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed [AngouriMath/Functions/Continuous/ExtremumOverSet.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/Compilation/IntoLinq/TensorCompilation.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/CompileMatrixTest.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/IntervalArithmetic.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 diff --git a/Sources/AngouriMath/Docs/Usage/Exceptions.md b/Sources/AngouriMath/Docs/Usage/Exceptions.md index 7f8ac3929..fb1045c7b 100644 --- a/Sources/AngouriMath/Docs/Usage/Exceptions.md +++ b/Sources/AngouriMath/Docs/Usage/Exceptions.md @@ -154,7 +154,7 @@ take. `Compile` was asked for a function it cannot build. Two distinct causes, and the message says which: -- a node with no compiled form — a matrix, a derivative, an integral, a limit; +- a node with no compiled form — a derivative, an integral, a limit, a function of a matrix; - a variable the expression mentions that the compilation was not given. The message names the variable and lists the ones it *was* given, since `Compile("x")` on `a * x` failing used to surface as a bare `KeyNotFoundException` about some dictionary. @@ -162,6 +162,16 @@ take. Both compilers raise it: the stack-machine one (`Compile`) and the LINQ one (`Compile`). +A matrix is not on the first list any more: under the LINQ compiler it compiles to a +`GenTensor` over its elements' type — `GenTensor`, +`GenTensor`, and the same for `float`, `int`, `long`, `BigInteger`, +the wrappers living in `AngouriMath.Core.Compilation.IntoLinq` — and that is the type to compile +to and to pass a matrix in as: `"A * B".ToEntity().Compile, +GenTensor, GenTensor>("A", "B")` is the matrix +product, `2 A + B - 1` scales, adds and shifts element by element, `A ^ 3` is the matrix power +([#526](https://github.com/asc-community/AngouriMath/issues/526)). Compiling a matrix as a +number, or a number as a matrix, or `sin(A)`, is still this exception. + ### `CannotEvalException` Something did not collapse to a single number or boolean. Overwhelmingly this is diff --git a/Sources/AngouriMath/Functions/Compilation/IntoLinq/CompilationProtocol.cs b/Sources/AngouriMath/Functions/Compilation/IntoLinq/CompilationProtocol.cs index 0a693948d..1b8a27d18 100644 --- a/Sources/AngouriMath/Functions/Compilation/IntoLinq/CompilationProtocol.cs +++ b/Sources/AngouriMath/Functions/Compilation/IntoLinq/CompilationProtocol.cs @@ -74,6 +74,10 @@ public virtual Expression ConvertType(Expression expr, Type type) if (expr.Type == type) return expr; + // A tensor converts to nothing but itself: a matrix over one element type is not + // a matrix over another, and it is not a number. + if (TensorCompilation.IsTensor(expr.Type) || TensorCompilation.IsTensor(type)) + throw new UncompilableNodeException($"A value of type {expr.Type} cannot be compiled as {type}"); bool exprNullable = (Nullable.GetUnderlyingType(expr.Type) is not null) || (expr.Type == typeof(object)); bool typeNullable = (Nullable.GetUnderlyingType(type) is not null) || (type == typeof(object)); @@ -107,7 +111,11 @@ public virtual Expression ConvertType(Expression expr, Type type) /// public virtual Expression ConvertUnaryNode(Expression e, Entity typeHolder) { - + // A function of a matrix has no compiled form here: the tensor operations are the + // arithmetic ones, and sin(A) is not an element-by-element sine of a matrix. + if (TensorCompilation.IsTensor(e.Type)) + throw new UncompilableNodeException( + $"There is no compiled form for {typeHolder.GetType().Name} of a matrix."); return typeHolder switch { Sinf when ShouldBeAtLeastDouble(e) is var newE => Expression.Call(GetDef("Sin", 1, newE.Type), newE), @@ -155,7 +163,13 @@ private Expression ShouldBeAtLeastDouble(Expression expr) /// public virtual Expression ConvertBinaryNode(Expression left, Expression right, Entity typeHolder) { - + // A matrix in either place: the tensor operation of the same name, the scalar side + // converted to the element type. https://github.com/asc-community/AngouriMath/issues/526 + if (TensorCompilation.IsTensor(left.Type) || TensorCompilation.IsTensor(right.Type)) + return TensorCompilation.Binary(typeHolder, left, right, ConvertType) + ?? throw new UncompilableNodeException( + $"The node of type {typeHolder.GetType()} has no compiled form over a matrix."); + (left, right) = EqualizeTypesIfAble(left, right); return typeHolder switch { @@ -214,6 +228,10 @@ public virtual Expression ConvertOtherNode(IEnumerable en, Entity ty return typeHolder switch { Piecewise => HandlePiecewise(en), + // A matrix is a tensor over its elements' common type, built from the compiled + // elements in row-major order, which is the order DirectChildren gives them. + // https://github.com/asc-community/AngouriMath/issues/526 + Entity.Matrix matrix => HandleMatrix(matrix, en), // TODO: finite set -> hash set _ => throw new UncompilableNodeException( $"The node of type {typeHolder.GetType()} does not support compilation.") @@ -221,6 +239,19 @@ public virtual Expression ConvertOtherNode(IEnumerable en, Entity ty }; } + private Expression HandleMatrix(Entity.Matrix matrix, IEnumerable en) + { + var elements = en.ToArray(); + if (elements.Length == 0) + throw new UncompilableNodeException("An empty matrix has no compiled form"); + var common = elements[0].Type; + foreach (var element in elements) + common = MaxType(Nullable.GetUnderlyingType(common) ?? common, Nullable.GetUnderlyingType(element.Type) ?? element.Type); + for (var i = 0; i < elements.Length; i++) + elements[i] = ConvertType(elements[i], common); + return TensorCompilation.Build(matrix, elements); + } + private Expression HandlePiecewise(IEnumerable en) { Expression[] children = en.ToArray(); diff --git a/Sources/AngouriMath/Functions/Compilation/IntoLinq/TensorCompilation.cs b/Sources/AngouriMath/Functions/Compilation/IntoLinq/TensorCompilation.cs new file mode 100644 index 000000000..287671e14 --- /dev/null +++ b/Sources/AngouriMath/Functions/Compilation/IntoLinq/TensorCompilation.cs @@ -0,0 +1,460 @@ +// +// Copyright (c) 2019-2026 Angouri. +// AngouriMath is licensed under MIT. +// Details: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md. +// Website: https://am.angouri.org. +// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Numerics; +using System.Reflection; +using AngouriMath.Core.Exceptions; +using GenericTensor.Core; +using Complex = System.Numerics.Complex; + +namespace AngouriMath.Core.Compilation.IntoLinq +{ + /// + /// The element arithmetic a compiled matrix runs on: + /// takes its operations from a struct, and these are the ones for the numeric types the + /// compiler produces. A matrix compiled over is a + /// GenTensor<double, DoubleOperations>, and that is the type to compile to + /// and to pass a matrix in as. + /// https://github.com/asc-community/AngouriMath/issues/526 + /// + public readonly struct DoubleOperations : IOperations + { + /// + public double Add(double a, double b) => a + b; + /// + public double Subtract(double a, double b) => a - b; + /// + public double Multiply(double a, double b) => a * b; + /// + public double Negate(double a) => -a; + /// + public double Divide(double a, double b) => a / b; + /// + public double CreateOne() => 1; + /// + public double CreateZero() => 0; + /// + public double Copy(double a) => a; + /// + public bool AreEqual(double a, double b) => a == b; + /// + public bool IsZero(double a) => a == 0; + /// + public string ToString(double a) => a.ToString(System.Globalization.CultureInfo.InvariantCulture); + /// + public byte[] Serialize(double a) => BitConverter.GetBytes(a); + /// + public double Deserialize(byte[] data) => BitConverter.ToDouble(data, 0); + } + + /// The element arithmetic of a matrix compiled over . See . + public readonly struct SingleOperations : IOperations + { + /// + public float Add(float a, float b) => a + b; + /// + public float Subtract(float a, float b) => a - b; + /// + public float Multiply(float a, float b) => a * b; + /// + public float Negate(float a) => -a; + /// + public float Divide(float a, float b) => a / b; + /// + public float CreateOne() => 1; + /// + public float CreateZero() => 0; + /// + public float Copy(float a) => a; + /// + public bool AreEqual(float a, float b) => a == b; + /// + public bool IsZero(float a) => a == 0; + /// + public string ToString(float a) => a.ToString(System.Globalization.CultureInfo.InvariantCulture); + /// + public byte[] Serialize(float a) => BitConverter.GetBytes(a); + /// + public float Deserialize(byte[] data) => BitConverter.ToSingle(data, 0); + } + + /// The element arithmetic of a matrix compiled over . See . + public readonly struct ComplexOperations : IOperations + { + /// + public Complex Add(Complex a, Complex b) => a + b; + /// + public Complex Subtract(Complex a, Complex b) => a - b; + /// + public Complex Multiply(Complex a, Complex b) => a * b; + /// + public Complex Negate(Complex a) => -a; + /// + public Complex Divide(Complex a, Complex b) => a / b; + /// + public Complex CreateOne() => Complex.One; + /// + public Complex CreateZero() => Complex.Zero; + /// + public Complex Copy(Complex a) => a; + /// + public bool AreEqual(Complex a, Complex b) => a == b; + /// + public bool IsZero(Complex a) => a == Complex.Zero; + /// + public string ToString(Complex a) => a.ToString(System.Globalization.CultureInfo.InvariantCulture); + /// + public byte[] Serialize(Complex a) + { + var bytes = new byte[16]; + BitConverter.GetBytes(a.Real).CopyTo(bytes, 0); + BitConverter.GetBytes(a.Imaginary).CopyTo(bytes, 8); + return bytes; + } + /// + public Complex Deserialize(byte[] data) => new(BitConverter.ToDouble(data, 0), BitConverter.ToDouble(data, 8)); + } + + /// The element arithmetic of a matrix compiled over . See . + public readonly struct Int32Operations : IOperations + { + /// + public int Add(int a, int b) => a + b; + /// + public int Subtract(int a, int b) => a - b; + /// + public int Multiply(int a, int b) => a * b; + /// + public int Negate(int a) => -a; + /// + public int Divide(int a, int b) => a / b; + /// + public int CreateOne() => 1; + /// + public int CreateZero() => 0; + /// + public int Copy(int a) => a; + /// + public bool AreEqual(int a, int b) => a == b; + /// + public bool IsZero(int a) => a == 0; + /// + public string ToString(int a) => a.ToString(System.Globalization.CultureInfo.InvariantCulture); + /// + public byte[] Serialize(int a) => BitConverter.GetBytes(a); + /// + public int Deserialize(byte[] data) => BitConverter.ToInt32(data, 0); + } + + /// The element arithmetic of a matrix compiled over . See . + public readonly struct Int64Operations : IOperations + { + /// + public long Add(long a, long b) => a + b; + /// + public long Subtract(long a, long b) => a - b; + /// + public long Multiply(long a, long b) => a * b; + /// + public long Negate(long a) => -a; + /// + public long Divide(long a, long b) => a / b; + /// + public long CreateOne() => 1; + /// + public long CreateZero() => 0; + /// + public long Copy(long a) => a; + /// + public bool AreEqual(long a, long b) => a == b; + /// + public bool IsZero(long a) => a == 0; + /// + public string ToString(long a) => a.ToString(System.Globalization.CultureInfo.InvariantCulture); + /// + public byte[] Serialize(long a) => BitConverter.GetBytes(a); + /// + public long Deserialize(byte[] data) => BitConverter.ToInt64(data, 0); + } + + /// The element arithmetic of a matrix compiled over . See . + public readonly struct BigIntegerOperations : IOperations + { + /// + public BigInteger Add(BigInteger a, BigInteger b) => a + b; + /// + public BigInteger Subtract(BigInteger a, BigInteger b) => a - b; + /// + public BigInteger Multiply(BigInteger a, BigInteger b) => a * b; + /// + public BigInteger Negate(BigInteger a) => -a; + /// + public BigInteger Divide(BigInteger a, BigInteger b) => a / b; + /// + public BigInteger CreateOne() => BigInteger.One; + /// + public BigInteger CreateZero() => BigInteger.Zero; + /// + public BigInteger Copy(BigInteger a) => a; + /// + public bool AreEqual(BigInteger a, BigInteger b) => a == b; + /// + public bool IsZero(BigInteger a) => a.IsZero; + /// + public string ToString(BigInteger a) => a.ToString(System.Globalization.CultureInfo.InvariantCulture); + /// + public byte[] Serialize(BigInteger a) => a.ToByteArray(); + /// + public BigInteger Deserialize(byte[] data) => new(data); + } + + /// + /// How a matrix and the arithmetic on it compile: a node becomes + /// a over its elements' common type, filled element by + /// element from the compiled elements, and a sum, difference, product, quotient or power + /// with a matrix in it becomes the tensor operation of the same name -- PiecewiseAdd, + /// MatrixMultiply, PiecewiseMultiply by a scalar, MatrixPower -- so + /// that the compiled delegate runs GenericTensor's arithmetic rather than a tree of scalar + /// operations. + /// + /// + /// The methods the compiled tree calls are the public generic ones below, closed over each + /// element type at compile time in the kit for that type and named through expression + /// trees, the way keeps its own tables: no generic type or + /// method is constructed at run time, which a trimmed or NativeAOT build could not do. + /// https://github.com/asc-community/AngouriMath/issues/363 + /// https://github.com/asc-community/AngouriMath/issues/526 + /// + public static class TensorCompilation + { + /// The closed methods for one element type. + private sealed class Kit + { + internal Type Tensor { get; } + internal MethodInfo Empty { get; } + internal MethodInfo Set { get; } + internal MethodInfo Add { get; } + internal MethodInfo Subtract { get; } + internal MethodInfo Multiply { get; } + internal MethodInfo AddScalar { get; } + internal MethodInfo SubtractScalar { get; } + internal MethodInfo SubtractFromScalar { get; } + internal MethodInfo MultiplyScalar { get; } + internal MethodInfo DivideScalar { get; } + internal MethodInfo DivideFromScalar { get; } + internal MethodInfo Power { get; } + + private Kit(Type tensor, MethodInfo empty, MethodInfo set, MethodInfo add, MethodInfo subtract, MethodInfo multiply, + MethodInfo addScalar, MethodInfo subtractScalar, MethodInfo subtractFromScalar, MethodInfo multiplyScalar, + MethodInfo divideScalar, MethodInfo divideFromScalar, MethodInfo power) + => (Tensor, Empty, Set, Add, Subtract, Multiply, AddScalar, SubtractScalar, SubtractFromScalar, MultiplyScalar, DivideScalar, DivideFromScalar, Power) + = (tensor, empty, set, add, subtract, multiply, addScalar, subtractScalar, subtractFromScalar, multiplyScalar, divideScalar, divideFromScalar, power); + + // An expression tree is how C# names a method without a string: the call inside it + // is resolved at compile time, closed over T and TWrapper, and rooted for the trimmer. + private static MethodInfo Named(Expression e) => ((MethodCallExpression)e.Body).Method; + + internal static Kit For() where TWrapper : struct, IOperations + => new(typeof(GenTensor), + Named>>((r, c) => TensorCompilation.Empty(r, c)), + Named, int, T, GenTensor>>((t, i, v) => TensorCompilation.Set(t, i, v)), + Named, GenTensor, GenTensor>>((a, b) => TensorCompilation.Add(a, b)), + Named, GenTensor, GenTensor>>((a, b) => TensorCompilation.Subtract(a, b)), + Named, GenTensor, GenTensor>>((a, b) => TensorCompilation.Multiply(a, b)), + Named, T, GenTensor>>((a, s) => TensorCompilation.AddScalar(a, s)), + Named, T, GenTensor>>((a, s) => TensorCompilation.SubtractScalar(a, s)), + Named, GenTensor>>((s, a) => TensorCompilation.SubtractFromScalar(s, a)), + Named, T, GenTensor>>((a, s) => TensorCompilation.MultiplyScalar(a, s)), + Named, T, GenTensor>>((a, s) => TensorCompilation.DivideScalar(a, s)), + Named, GenTensor>>((s, a) => TensorCompilation.DivideFromScalar(s, a)), + Named, int, GenTensor>>((a, n) => TensorCompilation.Power(a, n))); + } + + [ConstantField] + private static readonly Dictionary kits = new() + { + { typeof(double), Kit.For() }, + { typeof(float), Kit.For() }, + { typeof(Complex), Kit.For() }, + { typeof(int), Kit.For() }, + { typeof(long), Kit.For() }, + { typeof(BigInteger), Kit.For() }, + }; + + [ConstantField] + private static readonly Dictionary kitsByTensor = kits.Values.ToDictionary(kit => kit.Tensor); + + /// The tensor type a matrix over compiles to, or where there is none. + public static Type? TensorTypeOf(Type element) + => kits.TryGetValue(element, out var kit) ? kit.Tensor : null; + + internal static bool IsTensor(Type type) => kitsByTensor.ContainsKey(type); + + private static Type ElementOf(Type tensor) => tensor.GetGenericArguments()[0]; + + /// A matrix of the shape, to be filled. + public static GenTensor Empty(int rows, int columns) where TWrapper : struct, IOperations + => GenTensor.CreateMatrix(rows, columns); + /// The matrix with its element at a row-major index set; the same matrix, for chaining. + public static GenTensor Set(GenTensor matrix, int index, T value) where TWrapper : struct, IOperations + { + matrix[index / matrix.Shape[1], index % matrix.Shape[1]] = value; + return matrix; + } + + // The arithmetic is written as plain loops over the elements rather than handed to + // GenericTensor's PiecewiseAdd, MatrixMultiply and the rest: those build and compile an + // expression tree for their loops, which is exactly what a NativeAOT build cannot do, + // and the AOT smoke test refuses the assembly the moment they are reachable. A loop over + // a wrapper's Add is what they compile to anyway. + + private static void SameShape(GenTensor a, GenTensor b) where TWrapper : struct, IOperations + { + if (a.Shape[0] != b.Shape[0] || a.Shape[1] != b.Shape[1]) + throw new InvalidMatrixOperationException($"Matrices of shapes {a.Shape} and {b.Shape} cannot be combined element by element"); + } + + private static GenTensor Elementwise(GenTensor a, Func map) where TWrapper : struct, IOperations + { + var result = GenTensor.CreateMatrix(a.Shape[0], a.Shape[1]); + for (var i = 0; i < a.Shape[0]; i++) + for (var j = 0; j < a.Shape[1]; j++) + result[i, j] = map(a[i, j]); + return result; + } + + private static GenTensor Elementwise(GenTensor a, GenTensor b, Func combine) where TWrapper : struct, IOperations + { + SameShape(a, b); + var result = GenTensor.CreateMatrix(a.Shape[0], a.Shape[1]); + for (var i = 0; i < a.Shape[0]; i++) + for (var j = 0; j < a.Shape[1]; j++) + result[i, j] = combine(a[i, j], b[i, j]); + return result; + } + + /// Element by element. + public static GenTensor Add(GenTensor a, GenTensor b) where TWrapper : struct, IOperations + => Elementwise(a, b, static (x, y) => default(TWrapper).Add(x, y)); + /// Element by element. + public static GenTensor Subtract(GenTensor a, GenTensor b) where TWrapper : struct, IOperations + => Elementwise(a, b, static (x, y) => default(TWrapper).Subtract(x, y)); + /// The matrix product. + public static GenTensor Multiply(GenTensor a, GenTensor b) where TWrapper : struct, IOperations + { + if (a.Shape[1] != b.Shape[0]) + throw new InvalidMatrixOperationException($"Matrices of shapes {a.Shape} and {b.Shape} cannot be multiplied"); + var ops = default(TWrapper); + var result = GenTensor.CreateMatrix(a.Shape[0], b.Shape[1]); + for (var i = 0; i < a.Shape[0]; i++) + for (var j = 0; j < b.Shape[1]; j++) + { + var sum = ops.CreateZero(); + for (var k = 0; k < a.Shape[1]; k++) + sum = ops.Add(sum, ops.Multiply(a[i, k], b[k, j])); + result[i, j] = sum; + } + return result; + } + /// The scalar added to every element. + public static GenTensor AddScalar(GenTensor a, T scalar) where TWrapper : struct, IOperations + => Elementwise(a, x => default(TWrapper).Add(x, scalar)); + /// The scalar subtracted from every element. + public static GenTensor SubtractScalar(GenTensor a, T scalar) where TWrapper : struct, IOperations + => Elementwise(a, x => default(TWrapper).Subtract(x, scalar)); + /// Every element subtracted from the scalar. + public static GenTensor SubtractFromScalar(T scalar, GenTensor a) where TWrapper : struct, IOperations + => Elementwise(a, x => default(TWrapper).Subtract(scalar, x)); + /// Every element times the scalar. + public static GenTensor MultiplyScalar(GenTensor a, T scalar) where TWrapper : struct, IOperations + => Elementwise(a, x => default(TWrapper).Multiply(x, scalar)); + /// Every element over the scalar. + public static GenTensor DivideScalar(GenTensor a, T scalar) where TWrapper : struct, IOperations + => Elementwise(a, x => default(TWrapper).Divide(x, scalar)); + /// The scalar over every element. + public static GenTensor DivideFromScalar(T scalar, GenTensor a) where TWrapper : struct, IOperations + => Elementwise(a, x => default(TWrapper).Divide(scalar, x)); + /// The matrix power, a non-negative whole exponent, by repeated squaring. + public static GenTensor Power(GenTensor a, int exponent) where TWrapper : struct, IOperations + { + if (a.Shape[0] != a.Shape[1]) + throw new InvalidMatrixOperationException($"A matrix of shape {a.Shape} is not square and has no power"); + if (exponent < 0) + throw new NotSupportedException("A negative matrix power is not compiled: invert the matrix first"); + var result = GenTensor.CreateIdentityMatrix(a.Shape[0]); + var square = a; + for (var remaining = exponent; remaining > 0; remaining >>= 1) + { + if ((remaining & 1) == 1) + result = Multiply(result, square); + if (remaining > 1) + square = Multiply(square, square); + } + return result; + } + + /// The compiled form of a matrix node over the given compiled elements, all of one type. + internal static Expression Build(Entity.Matrix matrix, IReadOnlyList elements) + { + var element = elements[0].Type; + if (!kits.TryGetValue(element, out var kit)) + throw new UncompilableNodeException($"A matrix over {element} has no compiled form; the elements compile to double, float, Complex, int, long or BigInteger"); + Expression tensor = Expression.Call(kit.Empty, Expression.Constant(matrix.RowCount), Expression.Constant(matrix.ColumnCount)); + for (var i = 0; i < elements.Count; i++) + tensor = Expression.Call(kit.Set, tensor, Expression.Constant(i), elements[i]); + return tensor; + } + + /// + /// The compiled form of a binary node one of whose operands is a tensor, or + /// where the node has none. + /// + internal static Expression? Binary(Entity node, Expression left, Expression right, Func convert) + { + var leftTensor = kitsByTensor.TryGetValue(left.Type, out var leftKit); + var rightTensor = kitsByTensor.TryGetValue(right.Type, out var rightKit); + if (leftTensor && rightTensor) + { + if (left.Type != right.Type) + throw new UncompilableNodeException($"Two matrices over different element types, {ElementOf(left.Type)} and {ElementOf(right.Type)}, cannot be combined"); + return node switch + { + Entity.Sumf => Expression.Call(leftKit!.Add, left, right), + Entity.Minusf => Expression.Call(leftKit!.Subtract, left, right), + Entity.Mulf => Expression.Call(leftKit!.Multiply, left, right), + _ => null, + }; + } + if (leftTensor) + { + var scalar = node is Entity.Powf ? convert(right, typeof(int)) : convert(right, ElementOf(left.Type)); + return node switch + { + Entity.Sumf => Expression.Call(leftKit!.AddScalar, left, scalar), + Entity.Minusf => Expression.Call(leftKit!.SubtractScalar, left, scalar), + Entity.Mulf => Expression.Call(leftKit!.MultiplyScalar, left, scalar), + Entity.Divf => Expression.Call(leftKit!.DivideScalar, left, scalar), + Entity.Powf => Expression.Call(leftKit!.Power, left, scalar), + _ => null, + }; + } + var value = convert(left, ElementOf(right.Type)); + return node switch + { + Entity.Sumf => Expression.Call(rightKit!.AddScalar, right, value), + Entity.Minusf => Expression.Call(rightKit!.SubtractFromScalar, value, right), + Entity.Mulf => Expression.Call(rightKit!.MultiplyScalar, right, value), + Entity.Divf => Expression.Call(rightKit!.DivideFromScalar, value, right), + _ => null, + }; + } + } +} diff --git a/Sources/Tests/AotSmokeTest/Program.cs b/Sources/Tests/AotSmokeTest/Program.cs index d4f6a41ec..d57b80b36 100644 --- a/Sources/Tests/AotSmokeTest/Program.cs +++ b/Sources/Tests/AotSmokeTest/Program.cs @@ -11,7 +11,9 @@ using System.Linq; using System.Numerics; using AngouriMath; +using AngouriMath.Core.Compilation.IntoLinq; using AngouriMath.Extensions; +using GenericTensor.Core; namespace AngouriMath.AotSmokeTest { @@ -238,6 +240,14 @@ private static void Compilation() var piecewise = "piecewise(x provided x > 0, 0 provided x <= 0)".Compile(x); Check("compiled piecewise at -3 and 3", piecewise(-3) == 0 && piecewise(3) == 3, $"{piecewise(-3)}, {piecewise(3)}"); + + // A matrix, which compiles to a tensor: the arithmetic on it must stay ahead-of-time + // compilable, which GenericTensor's own expression-compiled loops are not. + // https://github.com/asc-community/AngouriMath/issues/526 + var matrix = "[[x, 2 x], [x + 1, x^2]] * [[1, 0], [0, 1]] + 1".Compile>(x); + var at3 = matrix(3); + Check("compiled matrix at 3", at3[0, 0] == 4 && at3[0, 1] == 7 && at3[1, 0] == 5 && at3[1, 1] == 10, + $"[[{at3[0, 0]}, {at3[0, 1]}], [{at3[1, 0]}, {at3[1, 1]}]]"); } } } diff --git a/Sources/Tests/UnitTests/Common/CompileMatrixTest.cs b/Sources/Tests/UnitTests/Common/CompileMatrixTest.cs new file mode 100644 index 000000000..e0804436b --- /dev/null +++ b/Sources/Tests/UnitTests/Common/CompileMatrixTest.cs @@ -0,0 +1,106 @@ +// +// Copyright (c) 2019-2026 Angouri. +// AngouriMath is licensed under MIT. +// Details: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md. +// Website: https://am.angouri.org. +// + +using System; +using System.Numerics; +using AngouriMath; +using AngouriMath.Core.Compilation.IntoLinq; +using AngouriMath.Core.Exceptions; +using AngouriMath.Extensions; +using GenericTensor.Core; +using Xunit; + +namespace AngouriMath.Tests.Common +{ + /// + /// A matrix compiles to a over its elements' type, and + /// the arithmetic on it to GenericTensor's operations: matrices are values a compiled + /// delegate takes and returns. #526 + /// + [Trait("Area", "Common")] + public sealed class CompileMatrixTest + { + private static GenTensor Doubles(params double[][] rows) + => GenTensor.CreateMatrix(rows.Length, rows[0].Length, (r, c) => rows[r][c]); + + private static void AssertSame(GenTensor expected, GenTensor actual) + { + Assert.Equal(expected.Shape, actual.Shape); + for (var r = 0; r < expected.Shape[0]; r++) + for (var c = 0; c < expected.Shape[1]; c++) + Assert.Equal(expected[r, c], actual[r, c], 12); + } + + [Fact] + public void AMatrixOfExpressionsCompilesToATensor() + { + var f = "[[x, 2 x], [x + 1, x^2]]".ToEntity().Compile>("x"); + AssertSame(Doubles(new[] { 3.0, 6.0 }, new[] { 4.0, 9.0 }), f(3)); + } + + [Fact] + public void TheProductOfTwoMatrixArgumentsIsTheMatrixProduct() + { + var f = "A * B".ToEntity().Compile, GenTensor, GenTensor>("A", "B"); + var a = Doubles(new[] { 1.0, 2.0 }, new[] { 3.0, 4.0 }); + var b = Doubles(new[] { 0.0, 1.0 }, new[] { 1.0, 0.0 }); + AssertSame(Doubles(new[] { 2.0, 1.0 }, new[] { 4.0, 3.0 }), f(a, b)); + } + + [Fact] + public void ScalarsAndMatricesMix() + { + var f = "2 A + B - 1".ToEntity().Compile, GenTensor, GenTensor>("A", "B"); + var a = Doubles(new[] { 1.0, 2.0 }, new[] { 3.0, 4.0 }); + var b = Doubles(new[] { 10.0, 10.0 }, new[] { 10.0, 10.0 }); + AssertSame(Doubles(new[] { 11.0, 13.0 }, new[] { 15.0, 17.0 }), f(a, b)); + } + + [Fact] + public void AScalarVariableScalesALiteralMatrix() + { + var f = "x * [[1, 2], [3, 4]] / 2".ToEntity().Compile>("x"); + AssertSame(Doubles(new[] { 2.0, 4.0 }, new[] { 6.0, 8.0 }), f(4)); + } + + [Fact] + public void AMatrixPowerIsTheMatrixPower() + { + var f = "A ^ 3".ToEntity().Compile, GenTensor>("A"); + var a = Doubles(new[] { 1.0, 1.0 }, new[] { 0.0, 1.0 }); + AssertSame(Doubles(new[] { 1.0, 3.0 }, new[] { 0.0, 1.0 }), f(a)); + } + + [Fact] + public void AnExpressionOfMatricesWithAScalarValueStillCompilesToTheScalar() + { + // #425's case: the matrix is simplified away before compiling, as before. + var f = "[[1, 0]] * [[a, b], [c, d]] * [[0], [1]]".ToEntity().Compile("a", "b", "c", "d"); + Assert.Equal(2.0, f(1, 2, 3, 4)); + } + + [Fact] + public void ComplexAndIntegerElementsHaveTheirOwnTensors() + { + var g = "[[x, i x]]".ToEntity().Compile>("x"); + var z = g(new Complex(1, 1)); + Assert.Equal(new Complex(1, 1), z[0, 0]); + Assert.Equal(new Complex(-1, 1), z[0, 1]); + var h = "[[n, 2 n]]".ToEntity().Compile>("n"); + Assert.Equal(6L, h(3)[0, 1]); + Assert.Equal(typeof(GenTensor), TensorCompilation.TensorTypeOf(typeof(double))); + Assert.Null(TensorCompilation.TensorTypeOf(typeof(string))); + } + + [Fact] + public void AMatrixIsNotANumberAndSaysSo() + { + Assert.Throws(() => "[[x, 2 x]]".ToEntity().Compile("x")); + Assert.Throws(() => "sin(A)".ToEntity().Compile, GenTensor>("A")); + } + } +} diff --git a/Sources/Tests/UnitTests/Common/MatrixCompilationTest.cs b/Sources/Tests/UnitTests/Common/MatrixCompilationTest.cs index 10b7d0b93..b638a31ca 100644 --- a/Sources/Tests/UnitTests/Common/MatrixCompilationTest.cs +++ b/Sources/Tests/UnitTests/Common/MatrixCompilationTest.cs @@ -13,8 +13,10 @@ namespace AngouriMath.Tests.Common { /// - /// Compiling expressions that mention matrices. A matrix has no compiled form, but an - /// expression built out of matrices often has a value that is an ordinary number. + /// Compiling expressions that mention matrices. An expression built out of matrices often + /// has a value that is an ordinary number, and is simplified to it before compiling; one + /// whose value is a matrix compiles to a tensor (CompileMatrixTest) and not to a + /// number, and says so. /// [Trait("Area", "Common")] public sealed class MatrixCompilationTest @@ -38,15 +40,18 @@ public void ScalarProductOfVectorsCompiles() Assert.Equal(1 * 3 + 2 * 4, compiled(3, 4), 9); } + // A matrix-valued expression asked for as a number: the value is a tensor, and the + // message names the type asked for. https://github.com/asc-community/AngouriMath/issues/526 [Theory] - [InlineData("[[a, b], [c, d]]")] - [InlineData("[[a, b], [c, d]] * [1, 0]")] - [InlineData("[a, b]")] - public void MatrixValuedExpressionsSayTheyCannotBeCompiled(string expression) + [InlineData("[[a, 2 a], [3 a, 4 a]]")] + [InlineData("[[a, a], [a, a]] * [1, 0]")] + [InlineData("[a, 2 a]")] + public void MatrixValuedExpressionsSayTheyCannotBeCompiledAsANumber(string expression) { var thrown = Assert.Throws( () => expression.ToEntity().Compile("a")); - Assert.Contains("Matrix", thrown.Message); + Assert.Contains("GenTensor", thrown.Message); + Assert.Contains("Double", thrown.Message); } // Expressions with no matrix in them must be unaffected -- they do not go anywhere diff --git a/Sources/Tests/UnitTests/Common/PublicApi.txt b/Sources/Tests/UnitTests/Common/PublicApi.txt index 7fe950160..ad07ba4fc 100644 --- a/Sources/Tests/UnitTests/Common/PublicApi.txt +++ b/Sources/Tests/UnitTests/Common/PublicApi.txt @@ -41,6 +41,19 @@ AngouriMath.Core.Budgets.WorkBudget.Unlimited { } : AngouriMath.Core.Budgets.Wor AngouriMath.Core.Budgets.WorkBudget.ctor() AngouriMath.Core.Budgets.WorkBudget.op_Equality(AngouriMath.Core.Budgets.WorkBudget, AngouriMath.Core.Budgets.WorkBudget) : System.Boolean AngouriMath.Core.Budgets.WorkBudget.op_Inequality(AngouriMath.Core.Budgets.WorkBudget, AngouriMath.Core.Budgets.WorkBudget) : System.Boolean +AngouriMath.Core.Compilation.IntoLinq.BigIntegerOperations.Add(System.Numerics.BigInteger, System.Numerics.BigInteger) : System.Numerics.BigInteger +AngouriMath.Core.Compilation.IntoLinq.BigIntegerOperations.AreEqual(System.Numerics.BigInteger, System.Numerics.BigInteger) : System.Boolean +AngouriMath.Core.Compilation.IntoLinq.BigIntegerOperations.Copy(System.Numerics.BigInteger) : System.Numerics.BigInteger +AngouriMath.Core.Compilation.IntoLinq.BigIntegerOperations.CreateOne() : System.Numerics.BigInteger +AngouriMath.Core.Compilation.IntoLinq.BigIntegerOperations.CreateZero() : System.Numerics.BigInteger +AngouriMath.Core.Compilation.IntoLinq.BigIntegerOperations.Deserialize(System.Byte[]) : System.Numerics.BigInteger +AngouriMath.Core.Compilation.IntoLinq.BigIntegerOperations.Divide(System.Numerics.BigInteger, System.Numerics.BigInteger) : System.Numerics.BigInteger +AngouriMath.Core.Compilation.IntoLinq.BigIntegerOperations.IsZero(System.Numerics.BigInteger) : System.Boolean +AngouriMath.Core.Compilation.IntoLinq.BigIntegerOperations.Multiply(System.Numerics.BigInteger, System.Numerics.BigInteger) : System.Numerics.BigInteger +AngouriMath.Core.Compilation.IntoLinq.BigIntegerOperations.Negate(System.Numerics.BigInteger) : System.Numerics.BigInteger +AngouriMath.Core.Compilation.IntoLinq.BigIntegerOperations.Serialize(System.Numerics.BigInteger) : System.Byte[] +AngouriMath.Core.Compilation.IntoLinq.BigIntegerOperations.Subtract(System.Numerics.BigInteger, System.Numerics.BigInteger) : System.Numerics.BigInteger +AngouriMath.Core.Compilation.IntoLinq.BigIntegerOperations.ToString(System.Numerics.BigInteger) : System.String AngouriMath.Core.Compilation.IntoLinq.CompilationProtocol.$() : AngouriMath.Core.Compilation.IntoLinq.CompilationProtocol AngouriMath.Core.Compilation.IntoLinq.CompilationProtocol.ConvertBinaryNode(System.Linq.Expressions.Expression, System.Linq.Expressions.Expression, AngouriMath.Entity) : System.Linq.Expressions.Expression AngouriMath.Core.Compilation.IntoLinq.CompilationProtocol.ConvertConstant(AngouriMath.Entity) : System.Linq.Expressions.Expression @@ -58,6 +71,84 @@ AngouriMath.Core.Compilation.IntoLinq.CompilationProtocol.ctor() AngouriMath.Core.Compilation.IntoLinq.CompilationProtocol.ctor(AngouriMath.Core.Compilation.IntoLinq.CompilationProtocol) AngouriMath.Core.Compilation.IntoLinq.CompilationProtocol.op_Equality(AngouriMath.Core.Compilation.IntoLinq.CompilationProtocol, AngouriMath.Core.Compilation.IntoLinq.CompilationProtocol) : System.Boolean AngouriMath.Core.Compilation.IntoLinq.CompilationProtocol.op_Inequality(AngouriMath.Core.Compilation.IntoLinq.CompilationProtocol, AngouriMath.Core.Compilation.IntoLinq.CompilationProtocol) : System.Boolean +AngouriMath.Core.Compilation.IntoLinq.ComplexOperations.Add(System.Numerics.Complex, System.Numerics.Complex) : System.Numerics.Complex +AngouriMath.Core.Compilation.IntoLinq.ComplexOperations.AreEqual(System.Numerics.Complex, System.Numerics.Complex) : System.Boolean +AngouriMath.Core.Compilation.IntoLinq.ComplexOperations.Copy(System.Numerics.Complex) : System.Numerics.Complex +AngouriMath.Core.Compilation.IntoLinq.ComplexOperations.CreateOne() : System.Numerics.Complex +AngouriMath.Core.Compilation.IntoLinq.ComplexOperations.CreateZero() : System.Numerics.Complex +AngouriMath.Core.Compilation.IntoLinq.ComplexOperations.Deserialize(System.Byte[]) : System.Numerics.Complex +AngouriMath.Core.Compilation.IntoLinq.ComplexOperations.Divide(System.Numerics.Complex, System.Numerics.Complex) : System.Numerics.Complex +AngouriMath.Core.Compilation.IntoLinq.ComplexOperations.IsZero(System.Numerics.Complex) : System.Boolean +AngouriMath.Core.Compilation.IntoLinq.ComplexOperations.Multiply(System.Numerics.Complex, System.Numerics.Complex) : System.Numerics.Complex +AngouriMath.Core.Compilation.IntoLinq.ComplexOperations.Negate(System.Numerics.Complex) : System.Numerics.Complex +AngouriMath.Core.Compilation.IntoLinq.ComplexOperations.Serialize(System.Numerics.Complex) : System.Byte[] +AngouriMath.Core.Compilation.IntoLinq.ComplexOperations.Subtract(System.Numerics.Complex, System.Numerics.Complex) : System.Numerics.Complex +AngouriMath.Core.Compilation.IntoLinq.ComplexOperations.ToString(System.Numerics.Complex) : System.String +AngouriMath.Core.Compilation.IntoLinq.DoubleOperations.Add(System.Double, System.Double) : System.Double +AngouriMath.Core.Compilation.IntoLinq.DoubleOperations.AreEqual(System.Double, System.Double) : System.Boolean +AngouriMath.Core.Compilation.IntoLinq.DoubleOperations.Copy(System.Double) : System.Double +AngouriMath.Core.Compilation.IntoLinq.DoubleOperations.CreateOne() : System.Double +AngouriMath.Core.Compilation.IntoLinq.DoubleOperations.CreateZero() : System.Double +AngouriMath.Core.Compilation.IntoLinq.DoubleOperations.Deserialize(System.Byte[]) : System.Double +AngouriMath.Core.Compilation.IntoLinq.DoubleOperations.Divide(System.Double, System.Double) : System.Double +AngouriMath.Core.Compilation.IntoLinq.DoubleOperations.IsZero(System.Double) : System.Boolean +AngouriMath.Core.Compilation.IntoLinq.DoubleOperations.Multiply(System.Double, System.Double) : System.Double +AngouriMath.Core.Compilation.IntoLinq.DoubleOperations.Negate(System.Double) : System.Double +AngouriMath.Core.Compilation.IntoLinq.DoubleOperations.Serialize(System.Double) : System.Byte[] +AngouriMath.Core.Compilation.IntoLinq.DoubleOperations.Subtract(System.Double, System.Double) : System.Double +AngouriMath.Core.Compilation.IntoLinq.DoubleOperations.ToString(System.Double) : System.String +AngouriMath.Core.Compilation.IntoLinq.Int32Operations.Add(System.Int32, System.Int32) : System.Int32 +AngouriMath.Core.Compilation.IntoLinq.Int32Operations.AreEqual(System.Int32, System.Int32) : System.Boolean +AngouriMath.Core.Compilation.IntoLinq.Int32Operations.Copy(System.Int32) : System.Int32 +AngouriMath.Core.Compilation.IntoLinq.Int32Operations.CreateOne() : System.Int32 +AngouriMath.Core.Compilation.IntoLinq.Int32Operations.CreateZero() : System.Int32 +AngouriMath.Core.Compilation.IntoLinq.Int32Operations.Deserialize(System.Byte[]) : System.Int32 +AngouriMath.Core.Compilation.IntoLinq.Int32Operations.Divide(System.Int32, System.Int32) : System.Int32 +AngouriMath.Core.Compilation.IntoLinq.Int32Operations.IsZero(System.Int32) : System.Boolean +AngouriMath.Core.Compilation.IntoLinq.Int32Operations.Multiply(System.Int32, System.Int32) : System.Int32 +AngouriMath.Core.Compilation.IntoLinq.Int32Operations.Negate(System.Int32) : System.Int32 +AngouriMath.Core.Compilation.IntoLinq.Int32Operations.Serialize(System.Int32) : System.Byte[] +AngouriMath.Core.Compilation.IntoLinq.Int32Operations.Subtract(System.Int32, System.Int32) : System.Int32 +AngouriMath.Core.Compilation.IntoLinq.Int32Operations.ToString(System.Int32) : System.String +AngouriMath.Core.Compilation.IntoLinq.Int64Operations.Add(System.Int64, System.Int64) : System.Int64 +AngouriMath.Core.Compilation.IntoLinq.Int64Operations.AreEqual(System.Int64, System.Int64) : System.Boolean +AngouriMath.Core.Compilation.IntoLinq.Int64Operations.Copy(System.Int64) : System.Int64 +AngouriMath.Core.Compilation.IntoLinq.Int64Operations.CreateOne() : System.Int64 +AngouriMath.Core.Compilation.IntoLinq.Int64Operations.CreateZero() : System.Int64 +AngouriMath.Core.Compilation.IntoLinq.Int64Operations.Deserialize(System.Byte[]) : System.Int64 +AngouriMath.Core.Compilation.IntoLinq.Int64Operations.Divide(System.Int64, System.Int64) : System.Int64 +AngouriMath.Core.Compilation.IntoLinq.Int64Operations.IsZero(System.Int64) : System.Boolean +AngouriMath.Core.Compilation.IntoLinq.Int64Operations.Multiply(System.Int64, System.Int64) : System.Int64 +AngouriMath.Core.Compilation.IntoLinq.Int64Operations.Negate(System.Int64) : System.Int64 +AngouriMath.Core.Compilation.IntoLinq.Int64Operations.Serialize(System.Int64) : System.Byte[] +AngouriMath.Core.Compilation.IntoLinq.Int64Operations.Subtract(System.Int64, System.Int64) : System.Int64 +AngouriMath.Core.Compilation.IntoLinq.Int64Operations.ToString(System.Int64) : System.String +AngouriMath.Core.Compilation.IntoLinq.SingleOperations.Add(System.Single, System.Single) : System.Single +AngouriMath.Core.Compilation.IntoLinq.SingleOperations.AreEqual(System.Single, System.Single) : System.Boolean +AngouriMath.Core.Compilation.IntoLinq.SingleOperations.Copy(System.Single) : System.Single +AngouriMath.Core.Compilation.IntoLinq.SingleOperations.CreateOne() : System.Single +AngouriMath.Core.Compilation.IntoLinq.SingleOperations.CreateZero() : System.Single +AngouriMath.Core.Compilation.IntoLinq.SingleOperations.Deserialize(System.Byte[]) : System.Single +AngouriMath.Core.Compilation.IntoLinq.SingleOperations.Divide(System.Single, System.Single) : System.Single +AngouriMath.Core.Compilation.IntoLinq.SingleOperations.IsZero(System.Single) : System.Boolean +AngouriMath.Core.Compilation.IntoLinq.SingleOperations.Multiply(System.Single, System.Single) : System.Single +AngouriMath.Core.Compilation.IntoLinq.SingleOperations.Negate(System.Single) : System.Single +AngouriMath.Core.Compilation.IntoLinq.SingleOperations.Serialize(System.Single) : System.Byte[] +AngouriMath.Core.Compilation.IntoLinq.SingleOperations.Subtract(System.Single, System.Single) : System.Single +AngouriMath.Core.Compilation.IntoLinq.SingleOperations.ToString(System.Single) : System.String +AngouriMath.Core.Compilation.IntoLinq.TensorCompilation.Add(GenTensor, GenTensor) : GenTensor +AngouriMath.Core.Compilation.IntoLinq.TensorCompilation.AddScalar(GenTensor, T) : GenTensor +AngouriMath.Core.Compilation.IntoLinq.TensorCompilation.DivideFromScalar(T, GenTensor) : GenTensor +AngouriMath.Core.Compilation.IntoLinq.TensorCompilation.DivideScalar(GenTensor, T) : GenTensor +AngouriMath.Core.Compilation.IntoLinq.TensorCompilation.Empty(System.Int32, System.Int32) : GenTensor +AngouriMath.Core.Compilation.IntoLinq.TensorCompilation.Multiply(GenTensor, GenTensor) : GenTensor +AngouriMath.Core.Compilation.IntoLinq.TensorCompilation.MultiplyScalar(GenTensor, T) : GenTensor +AngouriMath.Core.Compilation.IntoLinq.TensorCompilation.Power(GenTensor, System.Int32) : GenTensor +AngouriMath.Core.Compilation.IntoLinq.TensorCompilation.Set(GenTensor, System.Int32, T) : GenTensor +AngouriMath.Core.Compilation.IntoLinq.TensorCompilation.Subtract(GenTensor, GenTensor) : GenTensor +AngouriMath.Core.Compilation.IntoLinq.TensorCompilation.SubtractFromScalar(T, GenTensor) : GenTensor +AngouriMath.Core.Compilation.IntoLinq.TensorCompilation.SubtractScalar(GenTensor, T) : GenTensor +AngouriMath.Core.Compilation.IntoLinq.TensorCompilation.TensorTypeOf(System.Type) : System.Type AngouriMath.Core.CostModel.$() : AngouriMath.Core.CostModel AngouriMath.Core.CostModel.All { } : System.Collections.Generic.IReadOnlyList AngouriMath.Core.CostModel.Cost { } : System.Func @@ -2770,6 +2861,7 @@ class AngouriMath.Core.Budgets.BudgetOutcome class AngouriMath.Core.Budgets.BudgetRecording class AngouriMath.Core.Budgets.WorkBudget class AngouriMath.Core.Compilation.IntoLinq.CompilationProtocol +class AngouriMath.Core.Compilation.IntoLinq.TensorCompilation class AngouriMath.Core.CostModel class AngouriMath.Core.EquationSystem class AngouriMath.Core.Exceptions.AngouriBugException @@ -2939,6 +3031,12 @@ interface AngouriMath.Core.ILatexizeable interface AngouriMath.Core.IScalarClosedArithmetics interface AngouriMath.Core.IUnaryNode struct AngouriMath.Convenience.Setting +struct AngouriMath.Core.Compilation.IntoLinq.BigIntegerOperations +struct AngouriMath.Core.Compilation.IntoLinq.ComplexOperations +struct AngouriMath.Core.Compilation.IntoLinq.DoubleOperations +struct AngouriMath.Core.Compilation.IntoLinq.Int32Operations +struct AngouriMath.Core.Compilation.IntoLinq.Int64Operations +struct AngouriMath.Core.Compilation.IntoLinq.SingleOperations struct AngouriMath.Core.Transformations.DerivationStep struct AngouriMath.Core.Transformations.RewriteStep struct AngouriMath.Core.Transformations.TransformationResult