diff --git a/src/Numerics/Permutation.cs b/src/Numerics/Permutation.cs new file mode 100644 index 00000000..598e46aa --- /dev/null +++ b/src/Numerics/Permutation.cs @@ -0,0 +1,211 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://numerics.mathdotnet.com +// http://github.com/mathnet/mathnet-numerics +// http://mathnetnumerics.codeplex.com +// +// Copyright (c) 2009-2010 Math.NET +// +// 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. +// + +namespace MathNet.Numerics +{ + using System; + using System.Collections.Generic; + using System.Numerics; + using System.Runtime; + using System.Runtime.InteropServices; + using System.Text; + using Properties; + + /// + /// Class to represent a permutation for a subset of the natural numbers. + /// +#if !SILVERLIGHT + [Serializable] +#endif + public class Permutation + { + #region fields + + /// + /// Entry mIndices[i] represents the location to which i is permuted to. + /// + private static int[] mIndices; + + #endregion fields + + #region Constructor + + /// + /// Initializes a new instance of the Permutation structure. + /// + /// An array which represents where each integer is permuted too: indices[i] represents that integer i + /// is permuted to location indices[i]. + public Permutation(int[] indices) + { + if (!CheckForProperPermutation(indices)) + { + throw new ArgumentException(Resources.PermutationAsIntArrayInvalid, "indices"); + } + + mIndices = (int[]) indices.Clone(); + } + + #endregion + + /// + /// The number of elements this permutation is over. + /// + public int Dimension + { + get { return mIndices.Length; } + } + + /// + /// Computes where permutes too. + /// + /// The index to permute from. + /// The index which is permuted to. + public int this[int idx] + { + get + { + return mIndices[idx]; + } + } + + /// + /// Computes the inverse of the permutation. + /// + /// + public Permutation Inverse() + { + var invIdx = new int[Dimension]; + for (int i = 0; i < Dimension; i++) + { + invIdx[this[i]] = i; + } + + return new Permutation(invIdx); + } + + /// + /// Construct an array from a sequence of inversions. + /// + /// + /// From wikipedia: the permutation 12043 has the inversions (0,2), (1,2) and (3,4). This would be + /// encoded using the array [22244]. + /// + /// The set of inversions to contruct the permutation from. + /// A permutation generated from a sequence of inversions. + public static Permutation FromInversions(int[] inv) + { + var idx = new int[inv.Length]; + for (int i = 0; i < inv.Length; i++) + { + idx[i] = i; + } + for (int i = inv.Length-1; i >= 0; i--) + { + if (idx[i] != inv[i]) + { + int t = idx[i]; + idx[i] = idx[inv[i]]; + idx[inv[i]] = t; + } + } + + return new Permutation(idx); + } + + /// + /// Construct a sequence of inversions from the permutation. + /// + /// + /// From wikipedia: the permutation 12043 has the inversions (0,2), (1,2) and (3,4). This would be + /// encoded using the array [22244]. + /// + /// A sequence of inversions. + public int[] ToInversions() + { + var idx = (int[])mIndices.Clone(); + + for (int i = 0; i < Dimension; i++) + { + if (idx[i] != i) + { +#if !SILVERLIGHT + int q = Array.FindIndex(idx, i + 1, x => x == i); +#else + int q = -1; + for(int j = i+1; j < Dimension; j++) + { + if(idx[j] == i) + { + q = j; + break; + } + } +#endif + var t = idx[i]; + idx[i] = q; + idx[q] = t; + } + } + + return idx; + } + + /// + /// Checks whether the array represents a proper permutation. + /// + /// An array which represents where each integer is permuted too: indices[i] represents that integer i + /// is permuted to location indices[i]. + /// True if represents a proper permutation, false otherwise. + static private bool CheckForProperPermutation(int[] indices) + { + var idxCheck = new bool[indices.Length]; + + for (int i = 0; i < indices.Length; i++) + { + if (indices[i] >= indices.Length || indices[i] < 0) + { + return false; + } + + idxCheck[indices[i]] = true; + } + + for (int i = 0; i < indices.Length; i++) + { + if (idxCheck[i] == false) + { + return false; + } + } + + return true; + } + } +} \ No newline at end of file diff --git a/src/UnitTests/LinearAlgebraTests/Double/Factorization/LUTests.cs b/src/UnitTests/LinearAlgebraTests/Double/Factorization/LUTests.cs index 17c8f79d..66b6a183 100644 --- a/src/UnitTests/LinearAlgebraTests/Double/Factorization/LUTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Double/Factorization/LUTests.cs @@ -158,176 +158,5 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.Factorization } } } - - /*[Test] - [Row(1)] - [Row(2)] - [Row(5)] - [Row(10)] - [Row(50)] - [Row(100)] - [MultipleAsserts] - public void CanSolveForRandomVector(int order) - { - var A = MatrixLoader.GenerateRandomPositiveDefiniteMatrix(order); - var ACopy = A.Clone(); - var chol = A.Cholesky(); - var b = MatrixLoader.GenerateRandomVector(order); - var x = chol.Solve(b); - - Assert.AreEqual(b.Count, x.Count); - - var bReconstruct = A * x; - - // Check the reconstruction. - for (int i = 0; i < order; i++) - { - Assert.AreApproximatelyEqual(b[i], bReconstruct[i], 1.0e-11); - } - - // Make sure A didn't change. - for (int i = 0; i < A.RowCount; i++) - { - for (int j = 0; j < A.ColumnCount; j++) - { - Assert.AreEqual(ACopy[i, j], A[i, j]); - } - } - } - - [Test] - [Row(1,1)] - [Row(2,4)] - [Row(5,8)] - [Row(10,3)] - [Row(50,10)] - [Row(100,100)] - [MultipleAsserts] - public void CanSolveForRandomMatrix(int row, int col) - { - var A = MatrixLoader.GenerateRandomPositiveDefiniteMatrix(row); - var ACopy = A.Clone(); - var chol = A.Cholesky(); - var B = MatrixLoader.GenerateRandomMatrix(row, col); - var X = chol.Solve(B); - - Assert.AreEqual(B.RowCount, X.RowCount); - Assert.AreEqual(B.ColumnCount, X.ColumnCount); - - var BReconstruct = A * X; - - // Check the reconstruction. - for (int i = 0; i < B.RowCount; i++) - { - for (int j = 0; j < B.ColumnCount; j++) - { - Assert.AreApproximatelyEqual(B[i, j], BReconstruct[i, j], 1.0e-11); - } - } - - // Make sure A didn't change. - for (int i = 0; i < A.RowCount; i++) - { - for (int j = 0; j < A.ColumnCount; j++) - { - Assert.AreEqual(ACopy[i, j], A[i, j]); - } - } - } - - [Test] - [Row(1)] - [Row(2)] - [Row(5)] - [Row(10)] - [Row(50)] - [Row(100)] - [MultipleAsserts] - public void CanSolveForRandomVectorWhenResultVectorGiven(int order) - { - var A = MatrixLoader.GenerateRandomPositiveDefiniteMatrix(order); - var ACopy = A.Clone(); - var chol = A.Cholesky(); - var b = MatrixLoader.GenerateRandomVector(order); - var bCopy = b.Clone(); - var x = new DenseVector(order); - chol.Solve(b, x); - - Assert.AreEqual(b.Count, x.Count); - - var bReconstruct = A * x; - - // Check the reconstruction. - for (int i = 0; i < order; i++) - { - Assert.AreApproximatelyEqual(b[i], bReconstruct[i], 1.0e-11); - } - - // Make sure A didn't change. - for (int i = 0; i < A.RowCount; i++) - { - for (int j = 0; j < A.ColumnCount; j++) - { - Assert.AreEqual(ACopy[i, j], A[i, j]); - } - } - - // Make sure b didn't change. - for (int i = 0; i < order; i++) - { - Assert.AreEqual(bCopy[i], b[i]); - } - } - - [Test] - [Row(1, 1)] - [Row(2, 4)] - [Row(5, 8)] - [Row(10, 3)] - [Row(50, 10)] - [Row(100, 100)] - [MultipleAsserts] - public void CanSolveForRandomMatrixWhenResultMatrixGiven(int row, int col) - { - var A = MatrixLoader.GenerateRandomPositiveDefiniteMatrix(row); - var ACopy = A.Clone(); - var chol = A.Cholesky(); - var B = MatrixLoader.GenerateRandomMatrix(row, col); - var BCopy = B.Clone(); - var X = new DenseMatrix(row, col); - chol.Solve(B, X); - - Assert.AreEqual(B.RowCount, X.RowCount); - Assert.AreEqual(B.ColumnCount, X.ColumnCount); - - var BReconstruct = A * X; - - // Check the reconstruction. - for (int i = 0; i < B.RowCount; i++) - { - for (int j = 0; j < B.ColumnCount; j++) - { - Assert.AreApproximatelyEqual(B[i, j], BReconstruct[i, j], 1.0e-11); - } - } - - // Make sure A didn't change. - for (int i = 0; i < A.RowCount; i++) - { - for (int j = 0; j < A.ColumnCount; j++) - { - Assert.AreEqual(ACopy[i, j], A[i, j]); - } - } - - // Make sure B didn't change. - for (int i = 0; i < B.RowCount; i++) - { - for (int j = 0; j < B.ColumnCount; j++) - { - Assert.AreEqual(BCopy[i, j], B[i, j]); - } - } - }*/ } } diff --git a/src/UnitTests/PermutationTest.cs b/src/UnitTests/PermutationTest.cs new file mode 100644 index 00000000..4c150927 --- /dev/null +++ b/src/UnitTests/PermutationTest.cs @@ -0,0 +1,121 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://numerics.mathdotnet.com +// http://github.com/mathnet/mathnet-numerics +// http://mathnetnumerics.codeplex.com +// +// Copyright (c) 2009-2010 Math.NET +// +// 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. +// + +namespace MathNet.Numerics.UnitTests +{ + using System; + using System.Numerics; + using MbUnit.Framework; + + [TestFixture] + public class PermutationTest + { + [Test] + [Row(new int[] { 0 })] + [Row(new int[] { 0, 1, 2, 3, 4, 5 })] + [Row(new int[] { 5, 4, 3, 2, 1, 0 })] + [Row(new int[] { 0, 4, 3, 2, 1, 5 })] + [Row(new int[] { 0, 3, 2, 1, 4, 5 })] + public void CanCreatePermutation(int[] idx) + { + var p = new Permutation(idx); + } + + [Test] + [Row(new int[] { -1 })] + [Row(new int[] { 0, 1, 2, 3, 4, 4 })] + [Row(new int[] { 5, 4, 3, 2, 1, 7 })] + [ExpectedArgumentException] + public void CreatePermutationFailsWhenGivenBadIndexSet(int[] idx) + { + var p = new Permutation(idx); + } + + [Test] + [Row(new int[] { 0 })] + [Row(new int[] { 0, 1, 2, 3, 4, 5 })] + [Row(new int[] { 5, 4, 3, 2, 1, 0 })] + [Row(new int[] { 0, 4, 3, 2, 1, 5 })] + [Row(new int[] { 0, 3, 2, 1, 4, 5 })] + [MultipleAsserts] + public void CanInvertPermutation(int[] idx) + { + var p = new Permutation(idx); + var pinv = p.Inverse(); + + Assert.AreEqual(p.Dimension, pinv.Dimension); + for (int i = 0; i < p.Dimension; i++) + { + Assert.AreEqual(i, pinv[p[i]]); + Assert.AreEqual(i, p[pinv[i]]); + } + } + + [Test] + [Row(new int[] { 0 }, new int[] { 0 })] + [Row(new int[] { 0, 1, 2, 3, 4, 5 }, new int[] { 0, 1, 2, 3, 4, 5 })] + [Row(new int[] { 5, 4, 3, 3, 4, 5 }, new int[] { 5, 4, 3, 2, 1, 0 })] + [Row(new int[] { 0, 4, 3, 3, 4, 5 }, new int[] { 0, 4, 3, 2, 1, 5 })] + [Row(new int[] { 0, 3, 2, 3, 4, 5 }, new int[] { 0, 3, 2, 1, 4, 5 })] + [Row(new int[] { 2, 2, 2, 4, 4 }, new int[] { 1, 2, 0, 4, 3 })] + [MultipleAsserts] + public void CanCreatePermutationFromInversions(int[] inv, int[] idx) + { + var p = Permutation.FromInversions(inv); + var q = new Permutation(idx); + + Assert.AreEqual(q.Dimension, p.Dimension); + for (int i = 0; i < q.Dimension; i++) + { + Assert.AreEqual(q[i], p[i]); + } + } + + [Test] + [Row(new int[] { 0 }, new int[] { 0 })] + [Row(new int[] { 0, 1, 2, 3, 4, 5 }, new int[] { 0, 1, 2, 3, 4, 5 })] + [Row(new int[] { 5, 4, 3, 3, 4, 5 }, new int[] { 5, 4, 3, 2, 1, 0 })] + [Row(new int[] { 0, 4, 3, 3, 4, 5 }, new int[] { 0, 4, 3, 2, 1, 5 })] + [Row(new int[] { 0, 3, 2, 3, 4, 5 }, new int[] { 0, 3, 2, 1, 4, 5 })] + [Row(new int[] { 2, 2, 2, 4, 4 }, new int[] { 1, 2, 0, 4, 3 })] + [MultipleAsserts] + public void CanCreateInversionsFromPermutation(int[] inv, int[] idx) + { + var q = new Permutation(idx); + var p = q.ToInversions(); + + Assert.AreEqual(inv.Length, p.Length); + for (int i = 0; i < q.Dimension; i++) + { + Assert.AreEqual(inv[i], p[i]); + } + } + } +} \ No newline at end of file