Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
HamletTanyavong committed Oct 19, 2023
2 parents 8c9ae8e + 83033b9 commit 8a73ac9
Show file tree
Hide file tree
Showing 10 changed files with 377 additions and 5 deletions.
4 changes: 2 additions & 2 deletions src/Mathematics.NET/Core/IComplex.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// <copyright file="IComplex.cs" company="Mathematics.NET">
// <copyright file="IComplex.cs" company="Mathematics.NET">
// Mathematics.NET
// https://github.com/HamletTanyavong/Mathematics.NET
//
Expand Down Expand Up @@ -72,7 +72,7 @@ public interface IComplex<T>
/// <summary>Compute the absolute value of a number</summary>
/// <param name="z">A complex number</param>
/// <returns>The absolute value</returns>
static abstract T Abs(T z);
static virtual Real Abs(T z) => Real.Hypot(z.Re, z.Im);

/// <summary>Compute the complex conjugate of a number</summary>
/// <param name="z">A complex number</param>
Expand Down
7 changes: 6 additions & 1 deletion src/Mathematics.NET/Core/IRational.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// <copyright file="IRational.cs" company="Mathematics.NET">
// <copyright file="IRational.cs" company="Mathematics.NET">
// Mathematics.NET
// https://github.com/HamletTanyavong/Mathematics.NET
//
Expand Down Expand Up @@ -42,6 +42,11 @@ public interface IRational<T, U> : IReal<T>
/// <summary>Get the denominator of the rational number</summary>
U Den { get; }

/// <summary>Compute the absolute value of a number</summary>
/// <param name="x">A rational number</param>
/// <returns>The absolute value</returns>
static new abstract T Abs(T x);

/// <summary>Reduce a rational number</summary>
/// <param name="x">The value to reduce</param>
/// <returns>A reduced fraction if the number was reducible; otherwise, itself</returns>
Expand Down
47 changes: 47 additions & 0 deletions src/Mathematics.NET/Core/ParallelActions/MultiplyByScalarAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// <copyright file="MultiplyByScalarAction.cs" company="Mathematics.NET">
// Mathematics.NET
// https://github.com/HamletTanyavong/Mathematics.NET
//
// MIT License
//
// Copyright (c) 2023 Hamlet Tanyavong
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// </copyright>

using System.Runtime.CompilerServices;
using CommunityToolkit.HighPerformance.Helpers;

namespace Mathematics.NET.Core.ParallelActions;

/// <summary>An action for multiplying items by a scalar</summary>
/// <typeparam name="T">A type that implements <see cref="IComplex{T}"/></typeparam>
public readonly struct MultiplyByScalarAction<T> : IRefAction<T>
where T : IComplex<T>
{
private readonly T _factor;

public MultiplyByScalarAction(T factor)
{
_factor = factor;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Invoke(ref T item) => item *= _factor;
}
62 changes: 61 additions & 1 deletion src/Mathematics.NET/Core/Precision.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@

namespace Mathematics.NET.Core;

/// <summary>A class for working with floating-point numbers</summary>
/// <summary>
/// A class for working with floating-point numbers.
///
/// The methods for computing equalities and inequalies come from <i>The Art of Computer Programming: Seminumerical Algorithms</i>, section 4.2.2, <i>Accuracy of Floating Point Arithmetic</i> by Donald Knuth.
/// </summary>
public static class Precision
{
/// <summary>Machine epsilon for single-precision floating-point numbers according to the formal definition</summary>
Expand All @@ -54,18 +58,74 @@ public static class Precision
/// <remarks>This is equivalent to <see cref="double.Epsilon"/></remarks>
public const double DblMinPositive = 4.94065645841246544e-324;

//
// Equalities and Inequalities
//

/// <summary>Check if two values are approximately equal within a specified threshold</summary>
/// <typeparam name="T">A type that implements <see cref="IBinaryFloatingPointIeee754{TSelf}"/></typeparam>
/// <param name="left">The left value</param>
/// <param name="right">The right value</param>
/// <param name="epsilon">A threshold value</param>
/// <returns><see langword="true"/> if the values are approximately equal; otherwise, <see langword="false"/></returns>
public static bool AreApproximatelyEqual<T>(T left, T right, T epsilon)
where T : IBinaryFloatingPointIeee754<T>
=> T.Abs(left - right) <= epsilon * T.Max(T.Abs(left), T.Abs(right));

/// <summary>
/// Check if two values are approximately equal within a specified threshold $ \epsilon $.
///
/// If the values are complex, the threshold represents a disk of radius $ \epsilon $ in which the two values must reside.
/// </summary>
/// <typeparam name="T">A type that implements <see cref="IComplex{T}"/></typeparam>
/// <param name="left">The left value</param>
/// <param name="right">The right value</param>
/// <param name="epsilon">The radius of threshold disk</param>
/// <returns><see langword="true"/> if the values are approximately equal; otherwise, <see langword="false"/></returns>
public static bool AreApproximatelyEqual<T>(T left, T right, Real epsilon)
where T : IComplex<T>
=> T.Abs(left - right) <= epsilon * Real.Max(T.Abs(left), T.Abs(right));

/// <summary>Check if two values are essentially equal within a specified threshold</summary>
/// <typeparam name="T">A type that implements <see cref="IBinaryFloatingPointIeee754{TSelf}"/></typeparam>
/// <param name="left">The left value</param>
/// <param name="right">The right value</param>
/// <param name="epsilon">A threshold value</param>
/// <returns><see langword="true"/> if the values are essentially equal; otherwise, <see langword="false"/></returns>
public static bool AreEssentiallyEqual<T>(T left, T right, T epsilon)
where T : IBinaryFloatingPointIeee754<T>
=> T.Abs(left - right) <= epsilon * T.Min(T.Abs(left), T.Abs(right));

/// <summary>
/// Check if two values are essentially equal within a specified threshold $ \epsilon $.
///
/// If the values are complex, the threshold represents a disk of radius $ \epsilon $ in which the two values must reside.
/// </summary>
/// <typeparam name="T">A type that implements <see cref="IComplex{T}"/></typeparam>
/// <param name="left">The left value</param>
/// <param name="right">The right value</param>
/// <param name="epsilon">The radius of threshold disk</param>
/// <returns><see langword="true"/> if the values are essentially equal; otherwise, <see langword="false"/></returns>
public static bool AreEssentiallyEqual<T>(T left, T right, Real epsilon)
where T : IComplex<T>
=> T.Abs(left - right) <= epsilon * Real.Min(T.Abs(left), T.Abs(right));

/// <summary>Check if this value is definitely greater than a specified value</summary>
/// <typeparam name="T">A type that implements <see cref="IBinaryFloatingPointIeee754{TSelf}"/></typeparam>
/// <param name="number">This number</param>
/// <param name="value">A value to which to compare this number</param>
/// <param name="epsilon">A threshold value</param>
/// <returns><see langword="true"/> if this number is definitely greater than the specified value; otherwise, <see langword="false"/></returns>
public static bool IsDefinitelyGreaterThan<T>(this T number, T value, T epsilon)
where T : IBinaryFloatingPointIeee754<T>
=> number - value > epsilon * T.Max(T.Abs(number), T.Abs(value));

/// <summary>Check if this value is definitely less than a specified value</summary>
/// <typeparam name="T">A type that implements <see cref="IBinaryFloatingPointIeee754{TSelf}"/></typeparam>
/// <param name="number">This number</param>
/// <param name="value">A value to which to compare this number</param>
/// <param name="epsilon">A threshold value</param>
/// <returns><see langword="true"/> if this number is definitely less than the specified value; otherwise, <see langword="false"/></returns>
public static bool IsDefinitelyLessThan<T>(this T number, T value, T epsilon)
where T : IBinaryFloatingPointIeee754<T>
=> value - number > epsilon * T.Max(T.Abs(number), T.Abs(value));
Expand Down
11 changes: 11 additions & 0 deletions src/Mathematics.NET/LinearAlgebra/LinAlg.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

using System.Runtime.CompilerServices;
using CommunityToolkit.HighPerformance.Enumerables;
using CommunityToolkit.HighPerformance.Helpers;
using Mathematics.NET.Core.ParallelActions;

namespace Mathematics.NET.LinearAlgebra;

Expand Down Expand Up @@ -102,6 +104,15 @@ public static Span2D<T> MatMul<T>(Span2D<T> left, Span2D<T> right)
return result;
}

/// <summary>Multiply a matrix by a scalar in parallel</summary>
/// <typeparam name="T">A type that implements <see cref="IComplex{T}"/></typeparam>
/// <param name="scalar">A scalar</param>
/// <param name="matrix">A matrix</param>
/// <remarks>This process overwrites the original matrix.</remarks>
public static void MatMulByScalarParallel<T>(T scalar, Memory2D<T> matrix)
where T : IComplex<T>
=> ParallelHelper.ForEach(matrix, new MultiplyByScalarAction<T>(scalar));

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Real Norm<T>(RefEnumerable<T> vector)
where T : IComplex<T>
Expand Down
2 changes: 1 addition & 1 deletion src/Mathematics.NET/Mathematics.NET.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<Nullable>enable</Nullable>
<Platforms>x64</Platforms>
<Title>Mathematics.NET</Title>
<Version>0.1.0-alpha.1</Version>
<Version>0.1.0-alpha.2</Version>
<Authors>Hamlet Tanyavong</Authors>
<Description>Mathematics.NET is a C# class library that provides tools for solving mathematical problems.</Description>
<PackageTags>mathematics; math; maths</PackageTags>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// <copyright file="MatrixMultiplyByScalarBenchmarks.cs" company="Mathematics.NET">
// Mathematics.NET
// https://github.com/HamletTanyavong/Mathematics.NET
//
// MIT License
//
// Copyright (c) 2023 Hamlet Tanyavong
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// </copyright>

using CommunityToolkit.HighPerformance;
using CommunityToolkit.HighPerformance.Helpers;
using Mathematics.NET.Core.ParallelActions;

namespace Mathematics.NET.Benchmarks.LinearAlgebra;

[MemoryDiagnoser]
[RankColumn]
[Orderer(SummaryOrderPolicy.FastestToSlowest)]
public class MatrixMultiplyByScalarBenchmarks
{
public int Rows { get; set; }
public int Cols { get; set; }
public required ComplexNumber[,] MatrixOne { get; set; }
public required ComplexNumber[,] MatrixTwo { get; set; }

[GlobalSetup]
public void GlobalSetup()
{
Rows = 100;
Cols = 100;

MatrixOne = new ComplexNumber[Rows, Cols];

for (int i = 0; i < Rows; i++)
{
for (int j = 0; j < Cols; j++)
{
MatrixOne[i, j] = new(Random.Shared.NextDouble(), Random.Shared.NextDouble());
MatrixTwo[i, j] = new(Random.Shared.NextDouble(), Random.Shared.NextDouble());
}
}
}

[Benchmark(Baseline = true)]
public void MultiplyByScalarNaive()
{
var matrixAsSpan = MatrixOne.AsMemory2D().Span;
for (int i = 0; i < Rows; i++)
{
for (int j = 0; j < Cols; j++)
{
matrixAsSpan[i, j] *= Real.Pi;
}
}
}

[Benchmark]
public void MultiplyByScalarParallel()
{
Memory2D<ComplexNumber> matrixAsMemory = MatrixTwo;
ParallelHelper.ForEach(matrixAsMemory, new MultiplyByScalarAction<ComplexNumber>(Real.Pi));
}
}
1 change: 1 addition & 0 deletions tests/Mathematics.NET.Benchmarks/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
{
typeof(ComplexDivisionBenchmarks),
typeof(ComplexTrigonometryBenchmarks),
typeof(MatrixMultiplyByScalarBenchmarks),
typeof(NormBenchmarks),
typeof(RealvsDouble),
typeof(SystemComplexAbsVsComplexAbsBenchmarks)
Expand Down
81 changes: 81 additions & 0 deletions tests/Mathematics.NET.Tests/Assert.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// <copyright file="Assert.cs" company="Mathematics.NET">
// Mathematics.NET
// https://github.com/HamletTanyavong/Mathematics.NET
//
// MIT License
//
// Copyright (c) 2023 Hamlet Tanyavong
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// </copyright>

using CommunityToolkit.HighPerformance;

namespace Mathematics.NET.Tests;

public sealed class Assert<T>
where T : IComplex<T>
{
public static void AreApproximatelyEqual(T expected, T actual, Real delta)
{
if (T.IsNaN(expected) && T.IsNaN(actual) || T.IsInfinity(expected) && T.IsInfinity(actual))
{
return;
}

if (!Precision.AreApproximatelyEqual(expected, actual, delta))
{
Assert.Fail($$"""
Actual value does not fall within the specifed margin of error, {{delta}}:
Expected: {{expected}}
Actual: {{actual}}
""");
}
}

public static void ElementsAreApproximatelyEqual(Span2D<T> expected, Span2D<T> actual, Real delta)
{
if (expected.Height != actual.Height || expected.Width != actual.Width)
{
Assert.Fail($"Dimensions of the actual matrix, [{actual.Height}, {actual.Width}], does not match the dimensions of the expected matrix, [{expected.Height}, {expected.Width}]");
}

for (int i = 0; i < expected.Height; i++)
{
for (int j = 0; j < expected.Width; j++)
{
if (T.IsNaN(expected[i, j]) && T.IsNaN(actual[i, j]) || T.IsInfinity(expected[i, j]) && T.IsInfinity(actual[i, j]))
{
continue;
}

if (!Precision.AreApproximatelyEqual(expected[i, j], actual[i, j], delta))
{
Assert.Fail($$"""
Actual value at row {{i}} and column {{j}} does not fall within the specifed margin of error, {{delta}}:
Expected: {{expected[i, j]}}
Actual: {{actual[i, j]}}
""");
}
}
}
}
}
Loading

0 comments on commit 8a73ac9

Please sign in to comment.