diff --git a/NumFlat/Mat/Mat.Complex.cs b/NumFlat/Mat/Mat.Complex.cs index 60acdb6..ed9612f 100644 --- a/NumFlat/Mat/Mat.Complex.cs +++ b/NumFlat/Mat/Mat.Complex.cs @@ -105,5 +105,99 @@ private static void ConjugateDiv(in Vec x, double y, in Vec de pd += destination.Stride; } } + + /// + /// Extracts the real part of each element in the complex matrix. + /// + /// + /// The complex matrix. + /// + /// + /// The destination of the real parts. + /// + /// + /// This method does not allocate managed heap memory. + /// + public static void Real(in Mat x, in Mat destination) + { + ThrowHelper.ThrowIfEmpty(x, nameof(x)); + ThrowHelper.ThrowIfEmpty(destination, nameof(destination)); + + if (destination.RowCount != x.RowCount) + { + throw new ArgumentException("'destination.RowCount' must match 'x.RowCount'."); + } + + if (destination.ColCount != x.ColCount) + { + throw new ArgumentException("'destination.ColCount' must match 'x.ColCount'."); + } + + var sx = x.Memory.Span; + var sd = destination.Memory.Span; + var ox = 0; + var od = 0; + while (od < sd.Length) + { + var px = ox; + var pd = od; + var end = od + destination.RowCount; + while (pd < end) + { + sd[pd] = sx[px].Real; + px++; + pd++; + } + ox += x.Stride; + od += destination.Stride; + } + } + + /// + /// Extracts the imaginary part of each element in the complex matrix. + /// + /// + /// The complex matrix. + /// + /// + /// The destination of the imaginary parts. + /// + /// + /// This method does not allocate managed heap memory. + /// + public static void Imaginary(in Mat x, in Mat destination) + { + ThrowHelper.ThrowIfEmpty(x, nameof(x)); + ThrowHelper.ThrowIfEmpty(destination, nameof(destination)); + + if (destination.RowCount != x.RowCount) + { + throw new ArgumentException("'destination.RowCount' must match 'x.RowCount'."); + } + + if (destination.ColCount != x.ColCount) + { + throw new ArgumentException("'destination.ColCount' must match 'x.ColCount'."); + } + + var sx = x.Memory.Span; + var sd = destination.Memory.Span; + var ox = 0; + var od = 0; + while (od < sd.Length) + { + var px = ox; + var pd = od; + var end = od + destination.RowCount; + while (pd < end) + { + sd[pd] = sx[px].Imaginary; + px++; + pd++; + } + ox += x.Stride; + od += destination.Stride; + } + } } } diff --git a/NumFlat/Mat/MatrixExtensions.cs b/NumFlat/Mat/MatrixExtensions.cs index eb11c48..32519d9 100644 --- a/NumFlat/Mat/MatrixExtensions.cs +++ b/NumFlat/Mat/MatrixExtensions.cs @@ -168,6 +168,50 @@ public static Mat ConjugateTranspose(in this Mat x) return result; } + /// + /// Extracts the real part of each element in the complex matrix. + /// + /// + /// The complex matrix. + /// + /// + /// The real parts of the complex matrix. + /// + /// + /// This method allocates a new matrix which is independent from the original matrix. + /// To avoid the allocation, use instead. + /// + public static Mat Real(in this Mat x) + { + ThrowHelper.ThrowIfEmpty(x, nameof(x)); + + var result = new Mat(x.RowCount, x.ColCount); + Mat.Real(x, result); + return result; + } + + /// + /// Extracts the imaginary part of each element in the complex matrix. + /// + /// + /// The complex matrix. + /// + /// + /// The imaginary parts of the complex matrix. + /// + /// + /// This method allocates a new matrix which is independent from the original matrix. + /// To avoid the allocation, use instead. + /// + public static Mat Imaginary(in this Mat x) + { + ThrowHelper.ThrowIfEmpty(x, nameof(x)); + + var result = new Mat(x.RowCount, x.ColCount); + Mat.Imaginary(x, result); + return result; + } + /// /// Computes a matrix inversion, X^-1. /// diff --git a/NumFlat/Vec/Vec.Complex.cs b/NumFlat/Vec/Vec.Complex.cs index 950bffe..c0c2778 100644 --- a/NumFlat/Vec/Vec.Complex.cs +++ b/NumFlat/Vec/Vec.Complex.cs @@ -34,5 +34,73 @@ public static void Conjugate(in Vec x, in Vec destination) pd += destination.Stride; } } + + /// + /// Extracts the real part of each element in the complex vector. + /// + /// + /// The complex vector. + /// + /// + /// The destination of the real parts. + /// + /// + /// This method does not allocate managed heap memory. + /// + public static void Real(in Vec x, in Vec destination) + { + ThrowHelper.ThrowIfEmpty(x, nameof(x)); + ThrowHelper.ThrowIfEmpty(destination, nameof(destination)); + + if (destination.Count != x.Count) + { + throw new ArgumentException("'destination.Count' must match 'x.Count'."); + } + + var sx = x.Memory.Span; + var sd = destination.Memory.Span; + var px = 0; + var pd = 0; + while (pd < sd.Length) + { + sd[pd] = sx[px].Real; + px += x.Stride; + pd += destination.Stride; + } + } + + /// + /// Extracts the imaginary part of each element in the complex vector. + /// + /// + /// The complex vector. + /// + /// + /// The destination of the imaginary parts. + /// + /// + /// This method does not allocate managed heap memory. + /// + public static void Imaginary(in Vec x, in Vec destination) + { + ThrowHelper.ThrowIfEmpty(x, nameof(x)); + ThrowHelper.ThrowIfEmpty(destination, nameof(destination)); + + if (destination.Count != x.Count) + { + throw new ArgumentException("'destination.Count' must match 'x.Count'."); + } + + var sx = x.Memory.Span; + var sd = destination.Memory.Span; + var px = 0; + var pd = 0; + while (pd < sd.Length) + { + sd[pd] = sx[px].Imaginary; + px += x.Stride; + pd += destination.Stride; + } + } } } diff --git a/NumFlat/Vec/VectorExtensions.cs b/NumFlat/Vec/VectorExtensions.cs index 5fba5ee..dda2593 100644 --- a/NumFlat/Vec/VectorExtensions.cs +++ b/NumFlat/Vec/VectorExtensions.cs @@ -220,6 +220,50 @@ public static Vec Conjugate(in this Vec x) return result; } + /// + /// Extracts the real part of each element in the complex vector. + /// + /// + /// The complex vector. + /// + /// + /// The real parts of the complex vector. + /// + /// + /// This method allocates a new vector which is independent from the original vector. + /// To avoid the allocation, use instead. + /// + public static Vec Real(in this Vec x) + { + ThrowHelper.ThrowIfEmpty(x, nameof(x)); + + var result = new Vec(x.Count); + Vec.Real(x, result); + return result; + } + + /// + /// Extracts the imaginary part of each element in the complex vector. + /// + /// + /// The complex vector. + /// + /// + /// The imaginary parts of the complex vector. + /// + /// + /// This method allocates a new vector which is independent from the original vector. + /// To avoid the allocation, use instead. + /// + public static Vec Imaginary(in this Vec x) + { + ThrowHelper.ThrowIfEmpty(x, nameof(x)); + + var result = new Vec(x.Count); + Vec.Imaginary(x, result); + return result; + } + /// /// Converts the vector to a single-row matrix. /// diff --git a/NumFlatTest/MatTests/ComplexTests.cs b/NumFlatTest/MatTests/ComplexTests.cs index 252cc62..f6bcf00 100644 --- a/NumFlatTest/MatTests/ComplexTests.cs +++ b/NumFlatTest/MatTests/ComplexTests.cs @@ -35,5 +35,59 @@ public void Conjugate(int rowCount, int colCount, int xStride, int dstStride) TestMatrix.FailIfOutOfRangeWrite(dst); } + + [TestCase(1, 1, 1, 1)] + [TestCase(1, 1, 3, 5)] + [TestCase(2, 2, 2, 2)] + [TestCase(2, 2, 4, 3)] + [TestCase(3, 2, 3, 3)] + [TestCase(3, 2, 4, 5)] + [TestCase(2, 3, 2, 3)] + [TestCase(2, 3, 4, 4)] + [TestCase(6, 3, 8, 7)] + [TestCase(4, 7, 5, 9)] + public void Real(int rowCount, int colCount, int xStride, int dstStride) + { + var x = TestMatrix.RandomComplex(42, rowCount, colCount, xStride); + var dst = TestMatrix.RandomDouble(0, rowCount, colCount, dstStride); + + using (x.EnsureUnchanged()) + { + Mat.Real(x, dst); + } + + var expected = x.Map(value => value.Real); + + NumAssert.AreSame(expected, dst, 0); + + TestMatrix.FailIfOutOfRangeWrite(dst); + } + + [TestCase(1, 1, 1, 1)] + [TestCase(1, 1, 3, 5)] + [TestCase(2, 2, 2, 2)] + [TestCase(2, 2, 4, 3)] + [TestCase(3, 2, 3, 3)] + [TestCase(3, 2, 4, 5)] + [TestCase(2, 3, 2, 3)] + [TestCase(2, 3, 4, 4)] + [TestCase(6, 3, 8, 7)] + [TestCase(4, 7, 5, 9)] + public void Imaginary(int rowCount, int colCount, int xStride, int dstStride) + { + var x = TestMatrix.RandomComplex(42, rowCount, colCount, xStride); + var dst = TestMatrix.RandomDouble(0, rowCount, colCount, dstStride); + + using (x.EnsureUnchanged()) + { + Mat.Imaginary(x, dst); + } + + var expected = x.Map(value => value.Imaginary); + + NumAssert.AreSame(expected, dst, 0); + + TestMatrix.FailIfOutOfRangeWrite(dst); + } } } diff --git a/NumFlatTest/MatTests/MatrixExtensionsTests.cs b/NumFlatTest/MatTests/MatrixExtensionsTests.cs index a0aea98..47f707e 100644 --- a/NumFlatTest/MatTests/MatrixExtensionsTests.cs +++ b/NumFlatTest/MatTests/MatrixExtensionsTests.cs @@ -181,6 +181,56 @@ public void ConjugateTranspose(int rowCount, int colCount, int xStride) } } + [TestCase(1, 1, 1)] + [TestCase(2, 2, 2)] + [TestCase(2, 2, 4)] + [TestCase(3, 1, 3)] + [TestCase(1, 3, 1)] + [TestCase(1, 3, 2)] + [TestCase(3, 2, 3)] + [TestCase(2, 3, 2)] + [TestCase(4, 5, 8)] + [TestCase(9, 6, 11)] + public void Real(int rowCount, int colCount, int xStride) + { + var x = TestMatrix.RandomComplex(42, rowCount, colCount, xStride); + + Mat actual; + using (x.EnsureUnchanged()) + { + actual = x.Real(); + } + + var expected = x.Map(value => value.Real); + + NumAssert.AreSame(expected, actual, 0); + } + + [TestCase(1, 1, 1)] + [TestCase(2, 2, 2)] + [TestCase(2, 2, 4)] + [TestCase(3, 1, 3)] + [TestCase(1, 3, 1)] + [TestCase(1, 3, 2)] + [TestCase(3, 2, 3)] + [TestCase(2, 3, 2)] + [TestCase(4, 5, 8)] + [TestCase(9, 6, 11)] + public void Imaginary(int rowCount, int colCount, int xStride) + { + var x = TestMatrix.RandomComplex(42, rowCount, colCount, xStride); + + Mat actual; + using (x.EnsureUnchanged()) + { + actual = x.Imaginary(); + } + + var expected = x.Map(value => value.Imaginary); + + NumAssert.AreSame(expected, actual, 0); + } + [TestCase(1, 1)] [TestCase(2, 2)] [TestCase(3, 3)] diff --git a/NumFlatTest/VecTests/ComplexTests.cs b/NumFlatTest/VecTests/ComplexTests.cs index 858d1b2..6ebe417 100644 --- a/NumFlatTest/VecTests/ComplexTests.cs +++ b/NumFlatTest/VecTests/ComplexTests.cs @@ -31,5 +31,51 @@ public void Conjugate(int count, int xStride, int dstStride) TestVector.FailIfOutOfRangeWrite(actual); } + + [TestCase(1, 1, 1)] + [TestCase(2, 2, 2)] + [TestCase(3, 3, 3)] + [TestCase(1, 3, 4)] + [TestCase(2, 5, 4)] + [TestCase(5, 7, 6)] + public void Real(int count, int xStride, int dstStride) + { + var x = TestVector.RandomComplex(42, count, xStride); + + var expected = x.Select(c => c.Real).ToVector(); + + var actual = TestVector.RandomDouble(0, count, dstStride); + using (x.EnsureUnchanged()) + { + Vec.Real(x, actual); + } + + NumAssert.AreSame(expected, actual, 0); + + TestVector.FailIfOutOfRangeWrite(actual); + } + + [TestCase(1, 1, 1)] + [TestCase(2, 2, 2)] + [TestCase(3, 3, 3)] + [TestCase(1, 3, 4)] + [TestCase(2, 5, 4)] + [TestCase(5, 7, 6)] + public void Imaginary(int count, int xStride, int dstStride) + { + var x = TestVector.RandomComplex(42, count, xStride); + + var expected = x.Select(c => c.Imaginary).ToVector(); + + var actual = TestVector.RandomDouble(0, count, dstStride); + using (x.EnsureUnchanged()) + { + Vec.Imaginary(x, actual); + } + + NumAssert.AreSame(expected, actual, 0); + + TestVector.FailIfOutOfRangeWrite(actual); + } } } diff --git a/NumFlatTest/VecTests/VectorExtensionsTests.cs b/NumFlatTest/VecTests/VectorExtensionsTests.cs index 1860321..ee3d806 100644 --- a/NumFlatTest/VecTests/VectorExtensionsTests.cs +++ b/NumFlatTest/VecTests/VectorExtensionsTests.cs @@ -236,6 +236,48 @@ public void Conjugate(int count, int xStride) NumAssert.AreSame(expected, actual, 1.0E-12); } + [TestCase(1, 1)] + [TestCase(2, 2)] + [TestCase(3, 3)] + [TestCase(1, 3)] + [TestCase(2, 5)] + [TestCase(5, 7)] + public void Real(int count, int xStride) + { + var x = TestVector.RandomComplex(42, count, xStride); + + Vec actual; + using (x.EnsureUnchanged()) + { + actual = x.Real(); + } + + var expected = x.Select(value => value.Real).ToVector(); + + NumAssert.AreSame(expected, actual, 0); + } + + [TestCase(1, 1)] + [TestCase(2, 2)] + [TestCase(3, 3)] + [TestCase(1, 3)] + [TestCase(2, 5)] + [TestCase(5, 7)] + public void Imaginary(int count, int xStride) + { + var x = TestVector.RandomComplex(42, count, xStride); + + Vec actual; + using (x.EnsureUnchanged()) + { + actual = x.Imaginary(); + } + + var expected = x.Select(value => value.Imaginary).ToVector(); + + NumAssert.AreSame(expected, actual, 0); + } + [TestCase(1, 1)] [TestCase(2, 2)] [TestCase(3, 3)]