Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Matrix operators #398

Merged
merged 12 commits into from
May 8, 2021
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,4 @@ out/
/Sources/Utils/Utils/Properties/launchSettings.json
out/
TestResults
/Sources/Wrappers/AngouriMath.CPP.Importing/AngouriMath.CPP.Importing.rar
4 changes: 2 additions & 2 deletions Sources/AngouriMath/AngouriMath.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@
</None>
<PackageReference Include="Antlr4.Runtime.Standard" Version="4.9.2" />
<PackageReference Include="FieldCache" Version="1.0.0-alpha.3" />
<PackageReference Include="GenericTensor" Version="1.0.0" />
<PackageReference Include="PeterO.Numbers" Version="1.7.4" />
<PackageReference Include="GenericTensor" Version="1.0.3" />
<PackageReference Include="PeterO.Numbers" Version="1.8.0" />
<PackageReference Include="System.Memory" Version="4.5.4" />
<PackageReference Include="Nullable" Version="1.3.0" PrivateAssets="all" />
<ProjectReference Include="../Analyzers/Analyzers/Analyzers.csproj" PrivateAssets="all" ReferenceOutputAssembly="false" OutputItemType="Analyzer" />
Expand Down
5 changes: 3 additions & 2 deletions Sources/AngouriMath/Convenience/MathS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -688,9 +688,10 @@ public static Rational CreateRational(EInteger numerator, EInteger denominator)
}

/// <summary>
/// Finds the determinant of the given matrix
/// Finds the determinant of the given matrix. If
/// the matrix is non-square, returns null
/// </summary>
public static Entity Det(Matrix m)
public static Entity? Det(Matrix m)
=> m.Determinant;

/// <summary>Creates an instance of <see cref="Entity.Matrix"/>.</summary>
Expand Down
70 changes: 28 additions & 42 deletions Sources/AngouriMath/Core/Entity/Omni/Entity.Matrix.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,35 +194,32 @@ public Entity AsScalar()
/// <summary>
/// Finds the symbolical determinant via Laplace's method
/// </summary>
public Entity Determinant => determinant.GetValue(
static @this => @this.InnerMatrix.DeterminantGaussianSafeDivision().InnerSimplified,
public Entity? Determinant => determinant.GetValue(
static @this =>
{
if ([email protected])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this check be in GenericTensor?

Copy link
Member Author

@WhiteBlackGoose WhiteBlackGoose May 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but it is exception-based. Here we want to return a null in that case. (sure we can catch exceptions, but... quack. Maybe we will after the GenericTensorBaseException implemented)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then don't throw exceptions in GenericTensor!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return null;
return @this.InnerMatrix.DeterminantGaussianSafeDivision().InnerSimplified;
},
this
);
private FieldCache<Entity> determinant;
private FieldCache<Entity?> determinant;

// The reason it's not cached is because it throws exceptions.
/// <summary>Inverts the matrix</summary>
public Matrix ComputeInverse()
/// <summary>Returns an inverse matrix if it exists</summary>
public Matrix? Inverse => inverse.GetValue(static @this =>
{
var cp = InnerMatrix.Copy(false);
try
{
cp.InvertMatrix();
}
catch (InvalidShapeException)
{
throw new InvalidMatrixOperationException("Cannot inverse a non-square matrix!");
}
catch (InvalidDeterminantException)
{
throw new InvalidMatrixOperationException("Cannot inverse a singular matrix!");
}
var cp = @this.InnerMatrix.Copy(false);
if ([email protected])
return null;
if (@this.Determinant is null)
return null;
if (@this.Determinant == 0)
Comment on lines +212 to +216
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't these checks be in GenericTensor?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing, we want to return null to avoid an exception

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then don't throw exceptions in GenericTensor!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmmmmmmmmmmmmmmmmmmmmmmmmm. We can have 1.0.1-noexcept package for it. Since if you remember, there's ALLOW_EXCEPTION flag.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MomoDeve would love this

return null;
cp.InvertMatrix();
return ToMatrix(new Matrix(cp).InnerSimplified);
}
}, this);
private FieldCache<Matrix?> inverse;

/// <summary>Inverts the matrix</summary>
[Obsolete("Use ComputeInverse() instead")]
public Matrix Inverse() => ComputeInverse();

/// <summary>
/// The Add operator. Performs an active operation
Expand Down Expand Up @@ -270,22 +267,6 @@ public Matrix ComputeInverse()
}
}

/// <summary>
/// The Multiply operator. Performs an active operation.
/// 1. Finds the inverse of the divisor
/// 2. Multiplies the dividend by the inverse of the divisor
/// and then applies inner simplification.
/// The operator only works with square matrices of the same size
/// </summary>
/// <exception cref="InvalidMatrixOperationException">
/// May be thrown if no inverse was found.
/// </exception>
public static Matrix operator /(Matrix m1, Matrix m2)
{
var inv = m2.ComputeInverse();
return m1 * inv;
}

/// <summary>
/// Performs a binary power of the matrix.
/// The matrix must be a square matrix.
Expand Down Expand Up @@ -369,11 +350,16 @@ public IEnumerator<Entity> GetEnumerator()
/// <summary>
/// Adjugate form of a matrix
/// </summary>
public Matrix Adjugate =>
public Matrix? Adjugate =>
adjugate.GetValue(static @this =>
(Matrix)new Matrix(@this.InnerMatrix.Adjoint()).InnerSimplified,
{
if ([email protected])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this check be in GenericTensor?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦆

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦆

return null;
var innerSimplified = new Matrix(@this.InnerMatrix.Adjoint()).InnerSimplified;
return ToMatrix(innerSimplified);
},
this);
private FieldCache<Matrix> adjugate;
private FieldCache<Matrix?> adjugate;

/// <summary>
/// Returns a vector, where the i-th element
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,31 +121,11 @@ protected override Entity InnerSimplify() =>
}
public partial record Divf
{
private static bool TryDivide(Matrix m1, Matrix m2, out Entity res)
{
res = 0;
if (!m1.IsSquare)
return false;
if (m1.InnerMatrix.Shape != m2.InnerMatrix.Shape)
return false;
try
{
res = m1 / m2;
return true;
}
catch (InvalidMatrixOperationException)
{
return false;
}
}

/// <inheritdoc/>
protected override Entity InnerEval() =>
ExpandOnTwoArguments(Dividend.Evaled, Divisor.Evaled,
(a, b) => (a, b) switch
{
(Matrix m1, Matrix m2) when TryDivide(m1, m2, out var res) => res.Evaled,
(var any, Matrix m) => (any / m),
(Complex n1, Complex n2) => n1 / n2,
(Integer(0), _) => 0,
(_, Integer(0)) => Real.NaN,
Expand All @@ -158,8 +138,6 @@ protected override Entity InnerSimplify() =>
ExpandOnTwoArguments(Dividend.InnerSimplified, Divisor.InnerSimplified,
(a, b) => (a, b) switch
{
(Matrix m1, Matrix m2) when TryDivide(m1, m2, out var res) => res,
(var any, Matrix m) => (any / m),
(Integer(0), _) => 0,
(_, Integer(0)) => Real.NaN,
(var n1, Integer(1)) => n1,
Expand Down
5 changes: 3 additions & 2 deletions Sources/Samples/Samples/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;


Console.WriteLine("Ducks.");
Matrix m = "[5]";
Console.WriteLine(m.Determinant);
Console.WriteLine(5);
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
</PropertyGroup>

<ItemGroup>
<Compile Include="MatrixOperatorsTest.fs" />
<None Include="coverlet.runsettings" />
<Compile Include="Functions.Sets.fs" />
<Compile Include="CoreTest.fs" />
Expand All @@ -21,8 +22,6 @@
<Compile Include="Program.fs" />
</ItemGroup>

<ItemGroup />

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0-preview-20210219-03" />
<PackageReference Include="xunit" Version="2.4.1" />
Expand Down
66 changes: 66 additions & 0 deletions Sources/Tests/FSharpWrapperUnitTests/MatrixOperatorsTest.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
module MatrixOperatorsTest

open AngouriMath
open AngouriMath.FSharp.Functions
open AngouriMath.FSharp.MatrixOperators
open Xunit

(*

Matrix and matrix operators
M = matrix

*)

[<Fact>]
let ``M+M`` () =
Assert.Equal<Entity.Matrix>(vector [11; 32], vector [10; 30] |+ vector [1; 2])

[<Fact>]
let ``M-M`` () =
Assert.Equal<Entity.Matrix>(vector [9; 28], vector [10; 30] |- vector [1; 2])

[<Fact>]
let ``M*M`` () =
Assert.Equal<Entity.Matrix>(vector [2; 4], "[[2, 0], [0, 2]]" |* "[1, 2]")

(*

Matrix and scalar operators
Scalar and matrix operators
M = matrix
S = scalar

*)

[<Fact>]
let ``M+S`` () =
Assert.Equal<Entity.Matrix>(vector [11; 31], vector [10; 30] |+ 1)

[<Fact>]
let ``S+M`` () =
Assert.Equal<Entity.Matrix>(vector [11; 31], 1 |+ vector [10; 30])

[<Fact>]
let ``M-S`` () =
Assert.Equal<Entity.Matrix>(vector [9; 29], vector [10; 30] |- 1)

[<Fact>]
let ``S-M`` () =
Assert.Equal<Entity.Matrix>(vector [90; 70], 100 |- vector [10; 30])

[<Fact>]
let ``M*S`` () =
Assert.Equal<Entity.Matrix>(vector [3; 6], vector [1; 2] |* 3)

[<Fact>]
let ``S*M`` () =
Assert.Equal<Entity.Matrix>(vector [3; 6], 3 |* vector [1; 2])

[<Fact>]
let ``M/S`` () =
Assert.Equal<Entity.Matrix>(vector [5; 15], vector [10; 30] |/ 2)

[<Fact>]
let ``M**S`` () =
Assert.Equal<Entity.Matrix>(matrix [[37; 54]; [81; 118]], matrix [[1; 2]; [3; 4]] |** 3)
Loading