From 1b888c4d289b5ce1d749ef0ad90f76ae3a0c4f7e Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sat, 16 Nov 2013 12:58:02 +0100 Subject: [PATCH 001/109] Tests: disable de-CH related number format tests because of Win 8.1 bug --- src/UnitTests/ComplexTests/Complex32Test.TextHandling.cs | 2 +- src/UnitTests/ComplexTests/ComplexTest.TextHandling.cs | 2 +- .../LinearAlgebraTests/Complex/DenseVectorTest.TextHandling.cs | 2 +- .../LinearAlgebraTests/Complex/SparseVectorTest.TextHandling.cs | 2 +- .../Complex32/DenseVectorTest.TextHandling.cs | 2 +- .../Complex32/SparseVectorTest.TextHandling.cs | 2 +- .../LinearAlgebraTests/Double/DenseVectorTest.TextHandling.cs | 2 +- .../LinearAlgebraTests/Double/SparseVectorTest.TextHandling.cs | 2 +- .../LinearAlgebraTests/Single/DenseVectorTest.TextHandling.cs | 2 +- .../LinearAlgebraTests/Single/SparseVectorTest.TextHandling.cs | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/UnitTests/ComplexTests/Complex32Test.TextHandling.cs b/src/UnitTests/ComplexTests/Complex32Test.TextHandling.cs index 155118c6..56df452d 100644 --- a/src/UnitTests/ComplexTests/Complex32Test.TextHandling.cs +++ b/src/UnitTests/ComplexTests/Complex32Test.TextHandling.cs @@ -81,7 +81,7 @@ namespace MathNet.Numerics.UnitTests.ComplexTests [TestCase("en-US", "1.1")] [TestCase("tr-TR", "1,1")] [TestCase("de-DE", "1,1")] - [TestCase("de-CH", "1.1")] + //[TestCase("de-CH", "1.1")] Windows 8.1 issue, see http://bit.ly/W81deCH [TestCase("he-IL", "1.1")] public void CanFormatComplexToStringWithCulture(string cultureName, string number) { diff --git a/src/UnitTests/ComplexTests/ComplexTest.TextHandling.cs b/src/UnitTests/ComplexTests/ComplexTest.TextHandling.cs index a38eae29..d8495a21 100644 --- a/src/UnitTests/ComplexTests/ComplexTest.TextHandling.cs +++ b/src/UnitTests/ComplexTests/ComplexTest.TextHandling.cs @@ -50,7 +50,7 @@ namespace MathNet.Numerics.UnitTests.ComplexTests /// Expected imaginary part. /// Culture ID. [TestCase("-1 -2i", -1, -2, "en-US")] - [TestCase("-1 - 2i ", -1, -2, "de-CH")] + //[TestCase("-1 - 2i ", -1, -2, "de-CH")] Windows 8.1 issue, see http://bit.ly/W81deCH public void CanParseStringToComplexWithCulture(string text, double expectedReal, double expectedImaginary, string cultureName) { var parsed = text.ToComplex(new CultureInfo(cultureName)); diff --git a/src/UnitTests/LinearAlgebraTests/Complex/DenseVectorTest.TextHandling.cs b/src/UnitTests/LinearAlgebraTests/Complex/DenseVectorTest.TextHandling.cs index 54a4287f..17485761 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex/DenseVectorTest.TextHandling.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex/DenseVectorTest.TextHandling.cs @@ -71,7 +71,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex /// Expected result. /// Culture name. [TestCase(" 1.2 + 1i , 3.4 + 1i , 5.6 + 1i ", "(1.2, 1) (3.4, 1) (5.6, 1)", "en-US")] - [TestCase(" 1.2 + 1i ; 3.4 + 1i ; 5.6 + 1i ", "(1.2, 1) (3.4, 1) (5.6, 1)", "de-CH")] + //[TestCase(" 1.2 + 1i ; 3.4 + 1i ; 5.6 + 1i ", "(1.2, 1) (3.4, 1) (5.6, 1)", "de-CH")] Windows 8.1 issue, see http://bit.ly/W81deCH #if !PORTABLE [TestCase(" 1,2 + 1i ; 3,4 + 1i ; 5,6 + 1i ", "(1,2, 1) (3,4, 1) (5,6, 1)", "de-DE")] #endif diff --git a/src/UnitTests/LinearAlgebraTests/Complex/SparseVectorTest.TextHandling.cs b/src/UnitTests/LinearAlgebraTests/Complex/SparseVectorTest.TextHandling.cs index 128c5f32..5bbded70 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex/SparseVectorTest.TextHandling.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex/SparseVectorTest.TextHandling.cs @@ -70,7 +70,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex /// Expected result. /// Culture name. [TestCase(" 1.2 + 1i , 3.4 + 1i , 5.6 + 1i ", "(1.2, 1) (3.4, 1) (5.6, 1)", "en-US")] - [TestCase(" 1.2 + 1i ; 3.4 + 1i ; 5.6 + 1i ", "(1.2, 1) (3.4, 1) (5.6, 1)", "de-CH")] + //[TestCase(" 1.2 + 1i ; 3.4 + 1i ; 5.6 + 1i ", "(1.2, 1) (3.4, 1) (5.6, 1)", "de-CH")] Windows 8.1 issue, see http://bit.ly/W81deCH #if !PORTABLE [TestCase(" 1,2 + 1i ; 3,4 + 1i ; 5,6 + 1i ", "(1,2, 1) (3,4, 1) (5,6, 1)", "de-DE")] #endif diff --git a/src/UnitTests/LinearAlgebraTests/Complex32/DenseVectorTest.TextHandling.cs b/src/UnitTests/LinearAlgebraTests/Complex32/DenseVectorTest.TextHandling.cs index 02495a7e..f498c466 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex32/DenseVectorTest.TextHandling.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex32/DenseVectorTest.TextHandling.cs @@ -71,7 +71,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32 /// Expected result. /// Culture name. [TestCase(" 1.2 + 1i , 3.4 + 1i , 5.6 + 1i ", "(1.2, 1) (3.4, 1) (5.6, 1)", "en-US")] - [TestCase(" 1.2 + 1i ; 3.4 + 1i ; 5.6 + 1i ", "(1.2, 1) (3.4, 1) (5.6, 1)", "de-CH")] + //[TestCase(" 1.2 + 1i ; 3.4 + 1i ; 5.6 + 1i ", "(1.2, 1) (3.4, 1) (5.6, 1)", "de-CH")] Windows 8.1 issue, see http://bit.ly/W81deCH #if !PORTABLE [TestCase(" 1,2 + 1i ; 3,4 + 1i ; 5,6 + 1i ", "(1,2, 1) (3,4, 1) (5,6, 1)", "de-DE")] #endif diff --git a/src/UnitTests/LinearAlgebraTests/Complex32/SparseVectorTest.TextHandling.cs b/src/UnitTests/LinearAlgebraTests/Complex32/SparseVectorTest.TextHandling.cs index c2060a02..ac0dc3ae 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex32/SparseVectorTest.TextHandling.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex32/SparseVectorTest.TextHandling.cs @@ -70,7 +70,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32 /// Expected result. /// Culture name. [TestCase(" 1.2 + 1i , 3.4 + 1i , 5.6 + 1i ", "(1.2, 1) (3.4, 1) (5.6, 1)", "en-US")] - [TestCase(" 1.2 + 1i ; 3.4 + 1i ; 5.6 + 1i ", "(1.2, 1) (3.4, 1) (5.6, 1)", "de-CH")] + //[TestCase(" 1.2 + 1i ; 3.4 + 1i ; 5.6 + 1i ", "(1.2, 1) (3.4, 1) (5.6, 1)", "de-CH")] Windows 8.1 issue, see http://bit.ly/W81deCH #if !PORTABLE [TestCase(" 1,2 + 1i ; 3,4 + 1i ; 5,6 + 1i ", "(1,2, 1) (3,4, 1) (5,6, 1)", "de-DE")] #endif diff --git a/src/UnitTests/LinearAlgebraTests/Double/DenseVectorTest.TextHandling.cs b/src/UnitTests/LinearAlgebraTests/Double/DenseVectorTest.TextHandling.cs index 61417d5f..700e870e 100644 --- a/src/UnitTests/LinearAlgebraTests/Double/DenseVectorTest.TextHandling.cs +++ b/src/UnitTests/LinearAlgebraTests/Double/DenseVectorTest.TextHandling.cs @@ -67,7 +67,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double /// Expected result. /// Culture name. [TestCase(" 1.2,3.4 , 5.6 ", "1.2 3.4 5.6", "en-US")] - [TestCase(" 1.2;3.4 ; 5.6 ", "1.2 3.4 5.6", "de-CH")] + //[TestCase(" 1.2;3.4 ; 5.6 ", "1.2 3.4 5.6", "de-CH")] Windows 8.1 issue, see http://bit.ly/W81deCH [TestCase(" 1,2;3,4 ; 5,6 ", "1,2 3,4 5,6", "de-DE")] public void CanParseDoubleDenseVectorsWithCulture(string stringToParse, string expectedToString, string culture) { diff --git a/src/UnitTests/LinearAlgebraTests/Double/SparseVectorTest.TextHandling.cs b/src/UnitTests/LinearAlgebraTests/Double/SparseVectorTest.TextHandling.cs index 04858361..b63a82c4 100644 --- a/src/UnitTests/LinearAlgebraTests/Double/SparseVectorTest.TextHandling.cs +++ b/src/UnitTests/LinearAlgebraTests/Double/SparseVectorTest.TextHandling.cs @@ -66,7 +66,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double /// Expected result. /// Culture name. [TestCase(" 1.2,3.4 , 5.6 ", "1.2 3.4 5.6", "en-US")] - [TestCase(" 1.2;3.4 ; 5.6 ", "1.2 3.4 5.6", "de-CH")] + //[TestCase(" 1.2;3.4 ; 5.6 ", "1.2 3.4 5.6", "de-CH")] Windows 8.1 issue, see http://bit.ly/W81deCH [TestCase(" 1,2;3,4 ; 5,6 ", "1,2 3,4 5,6", "de-DE")] public void CanParseDoubleSparseVectorsWithCulture(string stringToParse, string expectedToString, string culture) { diff --git a/src/UnitTests/LinearAlgebraTests/Single/DenseVectorTest.TextHandling.cs b/src/UnitTests/LinearAlgebraTests/Single/DenseVectorTest.TextHandling.cs index 889a8432..aa8ddd40 100644 --- a/src/UnitTests/LinearAlgebraTests/Single/DenseVectorTest.TextHandling.cs +++ b/src/UnitTests/LinearAlgebraTests/Single/DenseVectorTest.TextHandling.cs @@ -67,7 +67,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single /// Expected result. /// Culture name. [TestCase(" 1.2,3.4 , 5.6 ", "1.2 3.4 5.6", "en-US")] - [TestCase(" 1.2;3.4 ; 5.6 ", "1.2 3.4 5.6", "de-CH")] + //[TestCase(" 1.2;3.4 ; 5.6 ", "1.2 3.4 5.6", "de-CH")] Windows 8.1 issue, see http://bit.ly/W81deCH [TestCase(" 1,2;3,4 ; 5,6 ", "1,2 3,4 5,6", "de-DE")] public void CanParseSingleDenseVectorsWithCulture(string stringToParse, string expectedToString, string culture) { diff --git a/src/UnitTests/LinearAlgebraTests/Single/SparseVectorTest.TextHandling.cs b/src/UnitTests/LinearAlgebraTests/Single/SparseVectorTest.TextHandling.cs index 32d5c7bd..8ae0c3cb 100644 --- a/src/UnitTests/LinearAlgebraTests/Single/SparseVectorTest.TextHandling.cs +++ b/src/UnitTests/LinearAlgebraTests/Single/SparseVectorTest.TextHandling.cs @@ -66,7 +66,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single /// Expected result. /// Culture name. [TestCase(" 1.2,3.4 , 5.6 ", "1.2 3.4 5.6", "en-US")] - [TestCase(" 1.2;3.4 ; 5.6 ", "1.2 3.4 5.6", "de-CH")] + //[TestCase(" 1.2;3.4 ; 5.6 ", "1.2 3.4 5.6", "de-CH")] Windows 8.1 issue, see http://bit.ly/W81deCH [TestCase(" 1,2;3,4 ; 5,6 ", "1,2 3,4 5,6", "de-DE")] public void CanParseSingleSparseVectorsWithCulture(string stringToParse, string expectedToString, string culture) { From 8334454fad9f6fe2a30a669b798e5032297c14ae Mon Sep 17 00:00:00 2001 From: Ethar Alali Date: Tue, 5 Nov 2013 00:05:14 +0000 Subject: [PATCH 002/109] Added RSquared class (and test - in TD like you mean it style) to the project. --- src/Numerics/GoodnessOfFit/RSquared.cs | 45 +++++++++++++ src/Numerics/Numerics.csproj | 2 + src/UnitTests/GoodnessOfFit/RSquaredTest.cs | 73 +++++++++++++++++++++ src/UnitTests/UnitTests.csproj | 1 + 4 files changed, 121 insertions(+) create mode 100644 src/Numerics/GoodnessOfFit/RSquared.cs create mode 100644 src/UnitTests/GoodnessOfFit/RSquaredTest.cs diff --git a/src/Numerics/GoodnessOfFit/RSquared.cs b/src/Numerics/GoodnessOfFit/RSquared.cs new file mode 100644 index 00000000..4414b69f --- /dev/null +++ b/src/Numerics/GoodnessOfFit/RSquared.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using MathNet.Numerics.LinearAlgebra.Double; + +namespace MathNet.Numerics.GoodnessOfFit +{ + public class RSquared + { + /// + /// Calculated the R-Squared value given modelled and observed values + /// + /// The values expected from the modelled + /// The actual data set values obtained + /// + public static double RSqr(IEnumerable modelledValues, IEnumerable observedValues) + { + var modelledData = modelledValues as double[] ?? modelledValues.ToArray(); + var observedData = observedValues as double[] ?? observedValues.ToArray(); + var observedDataCount = observedData.Count(); + if ( modelledData.Count() != observedDataCount) + { + throw new ArgumentException("Dataset length mismatch"); + } + + var observedSum = observedData.Sum(); + var modelledSum = modelledData.Sum(); + + var sumObservedByModelled = 0d; + + for (var itemIndex = 0; itemIndex < observedDataCount; itemIndex++) + { + sumObservedByModelled += (observedData[itemIndex] * modelledData[itemIndex]); + } + + var sumObservedSquared = observedData.Sum(item => item * item); + var sumModelledSquared = modelledData.Sum(item => item * item); + + return Math.Pow(( observedDataCount * sumObservedByModelled - observedSum * modelledSum ) / + Math.Sqrt((observedDataCount * sumObservedSquared - Math.Pow(observedSum, 2)) + * (observedDataCount * sumModelledSquared - Math.Pow(modelledSum, 2))), 2); + } + } +} diff --git a/src/Numerics/Numerics.csproj b/src/Numerics/Numerics.csproj index 00054cff..ff06c46e 100644 --- a/src/Numerics/Numerics.csproj +++ b/src/Numerics/Numerics.csproj @@ -87,6 +87,7 @@ + @@ -439,5 +440,6 @@ Designer + \ No newline at end of file diff --git a/src/UnitTests/GoodnessOfFit/RSquaredTest.cs b/src/UnitTests/GoodnessOfFit/RSquaredTest.cs new file mode 100644 index 00000000..33efb930 --- /dev/null +++ b/src/UnitTests/GoodnessOfFit/RSquaredTest.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MathNet.Numerics.GoodnessOfFit; +using NUnit.Framework; + +namespace MathNet.Numerics.UnitTests.GoodnessOfFit +{ + [TestFixture] + public class RSquaredTest + { + /// + /// Test the R-squared value of a values with itself + /// + [Test] + public void WhenCalculatingRSquaredOfLinearDistributionWithItselfThenRSquaredIsOne() + { + var data = new List(); + for (int i = 1; i <= 10; i++) + data.Add(i); + + Assert.That(RSquared.RSqr(data, data), Is.EqualTo(1)); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void WhenGivenTwoDatasetsOfDifferentSizeThenThrowsArgumentException() + { + var observedData = new List() + { + 23 + ,9 + ,5 + ,7 + ,10 + ,5 + ,4 + ,1 + ,2 + ,1 + }; + + var modelledData = new List() + { + 8 + ,9 + ,10 + }; + + RSquared.RSqr(modelledData, observedData); + + Assert.Fail("Expected ArgumentException exception wasn't thrown"); + } + + [Test] + public void WhenCalculatingRSquaredOfUnevenDistributionWithLInearDistributionThenRSquaredIsCalculated() + { + var observedData = new List() + { + 1, 2.3, 3.1, 4.8, 5.6, 6.3 + }; + + var modelledData = new List() + { + 2.6, 2.8, 3.1, 4.7, 5.1, 5.3 + }; + + Assert.That(Math.Round(RSquared.RSqr(modelledData, observedData), 11), Is.EqualTo(Math.Round(0.94878520708673d, 11))); + } + } +} diff --git a/src/UnitTests/UnitTests.csproj b/src/UnitTests/UnitTests.csproj index 271014d2..e0868287 100644 --- a/src/UnitTests/UnitTests.csproj +++ b/src/UnitTests/UnitTests.csproj @@ -133,6 +133,7 @@ + From 6dda2ef725f5e62297413726c524f6c17fd7cd27 Mon Sep 17 00:00:00 2001 From: Ethar Alali Date: Thu, 7 Nov 2013 08:29:00 +0000 Subject: [PATCH 003/109] Added license comments to file. --- src/Numerics/GoodnessOfFit/RSquared.cs | 34 +++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/Numerics/GoodnessOfFit/RSquared.cs b/src/Numerics/GoodnessOfFit/RSquared.cs index 4414b69f..251eff31 100644 --- a/src/Numerics/GoodnessOfFit/RSquared.cs +++ b/src/Numerics/GoodnessOfFit/RSquared.cs @@ -1,12 +1,38 @@ -using System; +// +// Math.NET Numerics, part of the Math.NET Project +// http://numerics.mathdotnet.com +// http://github.com/mathnet/mathnet-numerics +// http://mathnetnumerics.codeplex.com +// +// 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. +// + +using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using MathNet.Numerics.LinearAlgebra.Double; namespace MathNet.Numerics.GoodnessOfFit { - public class RSquared + public static class RSquared { /// /// Calculated the R-Squared value given modelled and observed values From 3ba8773b305b0c2a5fe034cb9603e3ab89065591 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sat, 16 Nov 2013 14:30:29 +0100 Subject: [PATCH 004/109] GoodnessOfFit: benchmarking indicates pearson correlation is twice as fast --- .../RSquared.cs => GoodnessOfFit.cs} | 37 ++++--------------- src/Numerics/Numerics.csproj | 2 +- src/UnitTests/GoodnessOfFit/RSquaredTest.cs | 12 ++---- 3 files changed, 12 insertions(+), 39 deletions(-) rename src/Numerics/{GoodnessOfFit/RSquared.cs => GoodnessOfFit.cs} (53%) diff --git a/src/Numerics/GoodnessOfFit/RSquared.cs b/src/Numerics/GoodnessOfFit.cs similarity index 53% rename from src/Numerics/GoodnessOfFit/RSquared.cs rename to src/Numerics/GoodnessOfFit.cs index 251eff31..1908d8ec 100644 --- a/src/Numerics/GoodnessOfFit/RSquared.cs +++ b/src/Numerics/GoodnessOfFit.cs @@ -1,4 +1,4 @@ -// +// // Math.NET Numerics, part of the Math.NET Project // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics @@ -26,13 +26,12 @@ // OTHER DEALINGS IN THE SOFTWARE. // -using System; using System.Collections.Generic; -using System.Linq; +using MathNet.Numerics.Statistics; -namespace MathNet.Numerics.GoodnessOfFit +namespace MathNet.Numerics { - public static class RSquared + public static class GoodnessOfFit { /// /// Calculated the R-Squared value given modelled and observed values @@ -40,32 +39,10 @@ namespace MathNet.Numerics.GoodnessOfFit /// The values expected from the modelled /// The actual data set values obtained /// - public static double RSqr(IEnumerable modelledValues, IEnumerable observedValues) + public static double RSquared(IEnumerable modelledValues, IEnumerable observedValues) { - var modelledData = modelledValues as double[] ?? modelledValues.ToArray(); - var observedData = observedValues as double[] ?? observedValues.ToArray(); - var observedDataCount = observedData.Count(); - if ( modelledData.Count() != observedDataCount) - { - throw new ArgumentException("Dataset length mismatch"); - } - - var observedSum = observedData.Sum(); - var modelledSum = modelledData.Sum(); - - var sumObservedByModelled = 0d; - - for (var itemIndex = 0; itemIndex < observedDataCount; itemIndex++) - { - sumObservedByModelled += (observedData[itemIndex] * modelledData[itemIndex]); - } - - var sumObservedSquared = observedData.Sum(item => item * item); - var sumModelledSquared = modelledData.Sum(item => item * item); - - return Math.Pow(( observedDataCount * sumObservedByModelled - observedSum * modelledSum ) / - Math.Sqrt((observedDataCount * sumObservedSquared - Math.Pow(observedSum, 2)) - * (observedDataCount * sumModelledSquared - Math.Pow(modelledSum, 2))), 2); + var corr = Correlation.Pearson(modelledValues, observedValues); + return corr * corr; } } } diff --git a/src/Numerics/Numerics.csproj b/src/Numerics/Numerics.csproj index ff06c46e..001ec730 100644 --- a/src/Numerics/Numerics.csproj +++ b/src/Numerics/Numerics.csproj @@ -87,7 +87,7 @@ - + diff --git a/src/UnitTests/GoodnessOfFit/RSquaredTest.cs b/src/UnitTests/GoodnessOfFit/RSquaredTest.cs index 33efb930..9dfe8a72 100644 --- a/src/UnitTests/GoodnessOfFit/RSquaredTest.cs +++ b/src/UnitTests/GoodnessOfFit/RSquaredTest.cs @@ -1,9 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using MathNet.Numerics.GoodnessOfFit; using NUnit.Framework; namespace MathNet.Numerics.UnitTests.GoodnessOfFit @@ -21,11 +17,11 @@ namespace MathNet.Numerics.UnitTests.GoodnessOfFit for (int i = 1; i <= 10; i++) data.Add(i); - Assert.That(RSquared.RSqr(data, data), Is.EqualTo(1)); + Assert.That(Numerics.GoodnessOfFit.RSquared(data, data), Is.EqualTo(1)); } [Test] - [ExpectedException(typeof(ArgumentException))] + [ExpectedException(typeof(ArgumentOutOfRangeException))] public void WhenGivenTwoDatasetsOfDifferentSizeThenThrowsArgumentException() { var observedData = new List() @@ -49,7 +45,7 @@ namespace MathNet.Numerics.UnitTests.GoodnessOfFit ,10 }; - RSquared.RSqr(modelledData, observedData); + Numerics.GoodnessOfFit.RSquared(modelledData, observedData); Assert.Fail("Expected ArgumentException exception wasn't thrown"); } @@ -67,7 +63,7 @@ namespace MathNet.Numerics.UnitTests.GoodnessOfFit 2.6, 2.8, 3.1, 4.7, 5.1, 5.3 }; - Assert.That(Math.Round(RSquared.RSqr(modelledData, observedData), 11), Is.EqualTo(Math.Round(0.94878520708673d, 11))); + Assert.That(Math.Round(Numerics.GoodnessOfFit.RSquared(modelledData, observedData), 11), Is.EqualTo(Math.Round(0.94878520708673d, 11))); } } } From 08ca8301b8c167bf2b0fc9d025e03adc00c9dedf Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sat, 16 Nov 2013 15:47:13 +0100 Subject: [PATCH 005/109] Distance: add Pearson's distance --- src/Numerics/Distance.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Numerics/Distance.cs b/src/Numerics/Distance.cs index 8dfca7a3..3fcfc6a3 100644 --- a/src/Numerics/Distance.cs +++ b/src/Numerics/Distance.cs @@ -29,8 +29,10 @@ // using System; +using System.Collections.Generic; using MathNet.Numerics.LinearAlgebra; using MathNet.Numerics.Properties; +using MathNet.Numerics.Statistics; namespace MathNet.Numerics { @@ -277,5 +279,13 @@ namespace MathNet.Numerics } return count; } + + /// + /// Pearson's distance, i.e. 1 - the person correlation coefficient. + /// + public static double Pearson(IEnumerable a, IEnumerable b) + { + return 1.0 - Correlation.Pearson(a, b); + } } } From 0d2a5b096c226e4e64c8dcf23e932d35f63ebde6 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sat, 16 Nov 2013 15:51:37 +0100 Subject: [PATCH 006/109] GoodnessOfFit: docs, add R value --- src/Numerics/GoodnessOfFit.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Numerics/GoodnessOfFit.cs b/src/Numerics/GoodnessOfFit.cs index 1908d8ec..e675773a 100644 --- a/src/Numerics/GoodnessOfFit.cs +++ b/src/Numerics/GoodnessOfFit.cs @@ -34,15 +34,28 @@ namespace MathNet.Numerics public static class GoodnessOfFit { /// - /// Calculated the R-Squared value given modelled and observed values + /// Calculated the R-Squared value, also known as coefficient of determination, + /// given modelled and observed values /// /// The values expected from the modelled /// The actual data set values obtained - /// + /// Squared Person product-momentum correlation coefficient. public static double RSquared(IEnumerable modelledValues, IEnumerable observedValues) { var corr = Correlation.Pearson(modelledValues, observedValues); return corr * corr; } + + /// + /// Calculated the R value, also known as linear correlation coefficient, + /// given modelled and observed values + /// + /// The values expected from the modelled + /// The actual data set values obtained + /// Person product-momentum correlation coefficient. + public static double R(IEnumerable modelledValues, IEnumerable observedValues) + { + return Correlation.Pearson(modelledValues, observedValues); + } } } From 4f3a9a4f6dcc6ffff9c821181fa9ab670c5ecc36 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sat, 16 Nov 2013 17:45:39 +0100 Subject: [PATCH 007/109] Statistics: comment in Correlation.Person on robustness --- src/Numerics/Statistics/Correlation.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Numerics/Statistics/Correlation.cs b/src/Numerics/Statistics/Correlation.cs index 1faf845f..397dd24d 100644 --- a/src/Numerics/Statistics/Correlation.cs +++ b/src/Numerics/Statistics/Correlation.cs @@ -57,6 +57,9 @@ namespace MathNet.Numerics.Statistics double varA = 0; double varB = 0; + // WARNING: do not try to "optimize" by summing up products instead of using differences. + // It would indeed be faster, but numerically much less robust if large mean + low variance. + using (IEnumerator ieA = dataA.GetEnumerator()) using (IEnumerator ieB = dataB.GetEnumerator()) { From 7133c1448b9eb1ec7b259f4f12a6911be3d99aee Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sun, 17 Nov 2013 00:54:05 +0100 Subject: [PATCH 008/109] Correlation: optimize Spearman ranked correlation algorithm #171 --- src/Numerics/Statistics/Correlation.cs | 36 ++++++++++++++------------ 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/Numerics/Statistics/Correlation.cs b/src/Numerics/Statistics/Correlation.cs index 397dd24d..c331b34c 100644 --- a/src/Numerics/Statistics/Correlation.cs +++ b/src/Numerics/Statistics/Correlation.cs @@ -160,37 +160,39 @@ namespace MathNet.Numerics.Statistics return new double[0]; } - var rankedSamples = series.Select((sample, index) => new {Sample = sample, RankIndex = index}).OrderBy(s => s.Sample).ToArray(); - if (rankedSamples.Length == 0) + // WARNING: do not try to cast series to an array and use it directly, + // as we need to sort it (and thus modify id) + + double[] samples = series.ToArray(); + int[] index = new int[samples.Length]; + for (int i = 0; i < index.Length; i++) { - return new double[0]; + index[i] = i; } + Sorting.Sort(samples, index); - var rankedArray = new double[rankedSamples.Length]; - - var previousSample = rankedSamples.Select((sampleIndex, index) => new { SampleIndex = sampleIndex, LoopIndex = index }).First(); - foreach (var rankedSampleIndex in rankedSamples.Select((sampleIndex, index) => new { SampleIndex = sampleIndex, LoopIndex = index })) + double[] rankedArray = new double[samples.Length]; + int previousIndex = 0; + for (int i = 1; i < samples.Length; i++) { - var currentSample = rankedSampleIndex; - - if (Math.Abs(currentSample.SampleIndex.Sample - previousSample.SampleIndex.Sample) <= 0) + if (Math.Abs(samples[i] - samples[previousIndex]) <= 0d) { continue; } - var rankedValue = (currentSample.LoopIndex + previousSample.LoopIndex - 1) / 2d + 1; - foreach (var index in Enumerable.Range(previousSample.LoopIndex, currentSample.LoopIndex - previousSample.LoopIndex)) + var rankedValue = (i + previousIndex - 1) / 2d + 1; + for (int k = previousIndex; k < i; k++) { - rankedArray[rankedSamples[index].RankIndex] = rankedValue; + rankedArray[index[k]] = rankedValue; } - previousSample = currentSample; + previousIndex = i; } - var finalValue = (rankedSamples.Length + previousSample.LoopIndex - 1) / 2d + 1; - foreach (var index in Enumerable.Range(previousSample.LoopIndex, rankedSamples.Length - previousSample.LoopIndex)) + var finalValue = (samples.Length + previousIndex - 1) / 2d + 1; + for (int k = previousIndex; k < index.Length; k++) { - rankedArray[rankedSamples[index].RankIndex] = finalValue; + rankedArray[index[k]] = finalValue; } return rankedArray; From df9948b48f02cb10ce27cb644d274bdfd0041da4 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sun, 17 Nov 2013 13:31:51 +0100 Subject: [PATCH 009/109] Distance: add Canberra distance --- src/Numerics/Distance.cs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/Numerics/Distance.cs b/src/Numerics/Distance.cs index 3fcfc6a3..b5819f12 100644 --- a/src/Numerics/Distance.cs +++ b/src/Numerics/Distance.cs @@ -202,6 +202,36 @@ namespace MathNet.Numerics return SAD(a, b); } + /// + /// Canberra Distance, a weighted version of the L1-norm of the difference. + /// + public static double Canberra(double[] a, double[] b) + { + if (a.Length != b.Length) throw new ArgumentException(Resources.ArgumentVectorsSameLength); + + double sum = 0d; + for (var i = 0; i < a.Length; i++) + { + sum += Math.Abs(a[i] - b[i])/(Math.Abs(a[i]) + Math.Abs(b[i])); + } + return sum; + } + + /// + /// Canberra Distance, a weighted version of the L1-norm of the difference. + /// + public static double Canberra(float[] a, float[] b) + { + if (a.Length != b.Length) throw new ArgumentException(Resources.ArgumentVectorsSameLength); + + float sum = 0f; + for (var i = 0; i < a.Length; i++) + { + sum += Math.Abs(a[i] - b[i]) / (Math.Abs(a[i]) + Math.Abs(b[i])); + } + return sum; + } + /// /// Chebyshev Distance, i.e. the Infinity-norm of the difference. /// From 233b4d4bcc3409ee8d90ef99b5a64c001963db93 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sun, 17 Nov 2013 13:47:43 +0100 Subject: [PATCH 010/109] Distance: add Minkowski distance (generalized p-norm) --- src/Numerics/Distance.cs | 106 ++++++++++++++++++++++++++++----------- 1 file changed, 76 insertions(+), 30 deletions(-) diff --git a/src/Numerics/Distance.cs b/src/Numerics/Distance.cs index b5819f12..da1b5082 100644 --- a/src/Numerics/Distance.cs +++ b/src/Numerics/Distance.cs @@ -202,36 +202,6 @@ namespace MathNet.Numerics return SAD(a, b); } - /// - /// Canberra Distance, a weighted version of the L1-norm of the difference. - /// - public static double Canberra(double[] a, double[] b) - { - if (a.Length != b.Length) throw new ArgumentException(Resources.ArgumentVectorsSameLength); - - double sum = 0d; - for (var i = 0; i < a.Length; i++) - { - sum += Math.Abs(a[i] - b[i])/(Math.Abs(a[i]) + Math.Abs(b[i])); - } - return sum; - } - - /// - /// Canberra Distance, a weighted version of the L1-norm of the difference. - /// - public static double Canberra(float[] a, float[] b) - { - if (a.Length != b.Length) throw new ArgumentException(Resources.ArgumentVectorsSameLength); - - float sum = 0f; - for (var i = 0; i < a.Length; i++) - { - sum += Math.Abs(a[i] - b[i]) / (Math.Abs(a[i]) + Math.Abs(b[i])); - } - return sum; - } - /// /// Chebyshev Distance, i.e. the Infinity-norm of the difference. /// @@ -276,6 +246,82 @@ namespace MathNet.Numerics return max; } + /// + /// Minkowski Distance, i.e. the generalized p-norm of the difference. + /// + public static double Minkowski(double p, Vector a, Vector b) where T : struct, IEquatable, IFormattable + { + return (a - b).Norm(p); + } + + /// + /// Minkowski Distance, i.e. the generalized p-norm of the difference. + /// + public static double Minkowski(double p, double[] a, double[] b) + { + if (a.Length != b.Length) throw new ArgumentException(Resources.ArgumentVectorsSameLength); + if (p < 0d) throw new ArgumentOutOfRangeException("p"); + if (p == 1d) return Manhattan(a, b); + if (p == 2d) return Euclidean(a, b); + if (double.IsPositiveInfinity(p)) return Chebyshev(a, b); + + double sum = 0d; + for (var i = 0; i < a.Length; i++) + { + sum += Math.Pow(Math.Abs(a[i] - b[i]), p); + } + return Math.Pow(sum, 1.0 / p); + } + + /// + /// Minkowski Distance, i.e. the generalized p-norm of the difference. + /// + public static float Minkowski(double p, float[] a, float[] b) + { + if (a.Length != b.Length) throw new ArgumentException(Resources.ArgumentVectorsSameLength); + if (p < 0d) throw new ArgumentOutOfRangeException("p"); + if (p == 1d) return Manhattan(a, b); + if (p == 2d) return Euclidean(a, b); + if (double.IsPositiveInfinity(p)) return Chebyshev(a, b); + + double sum = 0d; + for (var i = 0; i < a.Length; i++) + { + sum += Math.Pow(Math.Abs(a[i] - b[i]), p); + } + return (float) Math.Pow(sum, 1.0/p); + } + + /// + /// Canberra Distance, a weighted version of the L1-norm of the difference. + /// + public static double Canberra(double[] a, double[] b) + { + if (a.Length != b.Length) throw new ArgumentException(Resources.ArgumentVectorsSameLength); + + double sum = 0d; + for (var i = 0; i < a.Length; i++) + { + sum += Math.Abs(a[i] - b[i]) / (Math.Abs(a[i]) + Math.Abs(b[i])); + } + return sum; + } + + /// + /// Canberra Distance, a weighted version of the L1-norm of the difference. + /// + public static float Canberra(float[] a, float[] b) + { + if (a.Length != b.Length) throw new ArgumentException(Resources.ArgumentVectorsSameLength); + + float sum = 0f; + for (var i = 0; i < a.Length; i++) + { + sum += Math.Abs(a[i] - b[i]) / (Math.Abs(a[i]) + Math.Abs(b[i])); + } + return sum; + } + /// /// Hamming Distance, i.e. the number of positions that have different values in the vectors. /// From 2853fe4b9c62587403c98cf84b850b9aba81e11f Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Wed, 27 Nov 2013 20:19:05 +0100 Subject: [PATCH 011/109] Fit: weighted MultiDim and weighted Polynomial --- MathNet.Numerics.sln.DotSettings | 3 +++ src/Numerics/Fit.cs | 19 +++++++++++++++++++ src/UnitTests/FitTests.cs | 22 ++++++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/MathNet.Numerics.sln.DotSettings b/MathNet.Numerics.sln.DotSettings index a97eba6c..5787163f 100644 --- a/MathNet.Numerics.sln.DotSettings +++ b/MathNet.Numerics.sln.DotSettings @@ -15,6 +15,8 @@ True False False + False + True True False <copyright file="$FILENAME$" company="Math.NET"> @@ -66,5 +68,6 @@ OTHER DEALINGS IN THE SOFTWARE. SVD TFQMR WH + True <data /> <data><IncludeFilters /><ExcludeFilters /></data> \ No newline at end of file diff --git a/src/Numerics/Fit.cs b/src/Numerics/Fit.cs index 48a08efc..b16026cd 100644 --- a/src/Numerics/Fit.cs +++ b/src/Numerics/Fit.cs @@ -70,6 +70,15 @@ namespace MathNet.Numerics return MultipleRegression.NormalEquations(x, y); } + /// + /// Weighted Least-Squares fitting the points (X,y) = ((x0,x1,..,xk),y) and weights w to a linear surface y : X -> p0*x0 + p1*x1 + ... + pk*xk, + /// returning its best fitting parameters as [p0, p1, p2, ..., pk] array. + /// + public static double[] MultiDimWeighted(double[][] x, double[] y, double[] w) + { + return WeightedRegression.Weighted(x, y, w); + } + /// /// Least-Squares fitting the points (X,y) = ((x0,x1,..,xk),y) to a linear surface y : X -> p0*x0 + p1*x1 + ... + pk*xk, /// returning a function y' for the best fitting combination. @@ -90,6 +99,16 @@ namespace MathNet.Numerics return MultipleRegression.QR(design, Vector.Build.Dense(y)).ToArray(); } + /// + /// Weighted Least-Squares fitting the points (x,y) and weights w to a k-order polynomial y : x -> p0 + p1*x + p2*x^2 + ... + pk*x^k, + /// returning its best fitting parameters as [p0, p1, p2, ..., pk] array, compatible with Evaluate.Polynomial. + /// + public static double[] PolynomialWeighted(double[] x, double[] y, double[] w, int order) + { + var design = Matrix.Build.Dense(x.Length, order + 1, (i, j) => Math.Pow(x[i], j)); + return WeightedRegression.Weighted(design, Vector.Build.Dense(y), Matrix.Build.DenseOfDiagonalArray(w)).ToArray(); + } + /// /// Least-Squares fitting the points (x,y) to a k-order polynomial y : x -> p0 + p1*x + p2*x^2 + ... + pk*x^k, /// returning a function y' for the best fitting polynomial. diff --git a/src/UnitTests/FitTests.cs b/src/UnitTests/FitTests.cs index c2444ff6..542c0eda 100644 --- a/src/UnitTests/FitTests.cs +++ b/src/UnitTests/FitTests.cs @@ -133,6 +133,28 @@ namespace MathNet.Numerics.UnitTests } } + [Test] + public void FitsToOrder2PolynomialWeighted() + { + // Mathematica: LinearModelFit[{{1,4.986},{2,2.347},{3,2.061},{4,-2.995},{5,-2.352},{6,-5.782}}, {1, x, x^2}, x, Weights -> {0.5, 1.0, 1.0, 1.0, 1.0, 0.5}] + // -> 7.01451 - 2.12819 x + 0.0115 x^2 + + var x = Enumerable.Range(1, 6).Select(Convert.ToDouble).ToArray(); + var y = new[] { 4.986, 2.347, 2.061, -2.995, -2.352, -5.782 }; + + var respUnweighted = Fit.PolynomialWeighted(x, y, new[] { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 }, 2); + Assert.AreEqual(3, respUnweighted.Length); + Assert.AreEqual(6.9703, respUnweighted[0], 1e-4); + Assert.AreEqual(-2.05564, respUnweighted[1], 1e-4); + Assert.AreEqual(-0.00426786, respUnweighted[2], 1e-6); + + var resp = Fit.PolynomialWeighted(x, y, new[] { 0.5, 1.0, 1.0, 1.0, 1.0, 0.5 }, 2); + Assert.AreEqual(3, resp.Length); + Assert.AreEqual(7.01451, resp[0], 1e-4); + Assert.AreEqual(-2.12819, resp[1], 1e-4); + Assert.AreEqual(0.0115, resp[2], 1e-6); + } + [Test] public void FitsToTrigonometricLinearCombination() { From 13ede7dc3ad9a07761271d82d9552d3d721168a1 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Thu, 28 Nov 2013 09:34:05 +0100 Subject: [PATCH 012/109] Linear Regression: use more efficient LA routines (via tibel) --- src/Numerics/LinearRegression/MultipleRegression.cs | 6 +++--- src/Numerics/LinearRegression/WeightedRegression.cs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Numerics/LinearRegression/MultipleRegression.cs b/src/Numerics/LinearRegression/MultipleRegression.cs index c215f28d..f33419d3 100644 --- a/src/Numerics/LinearRegression/MultipleRegression.cs +++ b/src/Numerics/LinearRegression/MultipleRegression.cs @@ -45,7 +45,7 @@ namespace MathNet.Numerics.LinearRegression /// Best fitting vector for model parameters β public static Vector NormalEquations(Matrix x, Vector y) where T : struct, IEquatable, IFormattable { - return x.TransposeThisAndMultiply(x).Cholesky().Solve(x.Transpose()*y); + return x.TransposeThisAndMultiply(x).Cholesky().Solve(x.TransposeThisAndMultiply(y)); } /// @@ -57,7 +57,7 @@ namespace MathNet.Numerics.LinearRegression /// Best fitting vector for model parameters β public static Matrix NormalEquations(Matrix x, Matrix y) where T : struct, IEquatable, IFormattable { - return x.TransposeThisAndMultiply(x).Cholesky().Solve(x.Transpose() * y); + return x.TransposeThisAndMultiply(x).Cholesky().Solve(x.TransposeThisAndMultiply(y)); } /// @@ -76,7 +76,7 @@ namespace MathNet.Numerics.LinearRegression predictor = predictor.InsertColumn(0, Vector.Build.Dense(predictor.RowCount, Vector.One)); } var response = Vector.Build.Dense(y); - return predictor.TransposeThisAndMultiply(predictor).Cholesky().Solve(predictor.Transpose()*response).ToArray(); + return predictor.TransposeThisAndMultiply(predictor).Cholesky().Solve(predictor.TransposeThisAndMultiply(response)).ToArray(); } /// diff --git a/src/Numerics/LinearRegression/WeightedRegression.cs b/src/Numerics/LinearRegression/WeightedRegression.cs index 15409756..3a6b80a9 100644 --- a/src/Numerics/LinearRegression/WeightedRegression.cs +++ b/src/Numerics/LinearRegression/WeightedRegression.cs @@ -42,7 +42,7 @@ namespace MathNet.Numerics.LinearRegression /// public static Vector Weighted(Matrix x, Vector y, Matrix w) where T : struct, IEquatable, IFormattable { - return x.TransposeThisAndMultiply(w*x).Cholesky().Solve(x.Transpose()*(w*y)); + return x.TransposeThisAndMultiply(w*x).Cholesky().Solve(x.TransposeThisAndMultiply(w*y)); } /// @@ -50,7 +50,7 @@ namespace MathNet.Numerics.LinearRegression /// public static Matrix Weighted(Matrix x, Matrix y, Matrix w) where T : struct, IEquatable, IFormattable { - return x.TransposeThisAndMultiply(w*x).Cholesky().Solve(x.Transpose()*(w*y)); + return x.TransposeThisAndMultiply(w*x).Cholesky().Solve(x.TransposeThisAndMultiply(w*y)); } /// @@ -65,8 +65,8 @@ namespace MathNet.Numerics.LinearRegression predictor = predictor.InsertColumn(0, Vector.Build.Dense(predictor.RowCount, Vector.One)); } var response = Vector.Build.Dense(y); - var weights = Matrix.Build.Diagonal(new DiagonalMatrixStorage(predictor.RowCount, predictor.RowCount, w)); - return predictor.TransposeThisAndMultiply(weights*predictor).Cholesky().Solve(predictor.Transpose()*(weights*response)).ToArray(); + var weights = Matrix.Build.DenseOfDiagonalArray(w); + return predictor.TransposeThisAndMultiply(weights*predictor).Cholesky().Solve(predictor.TransposeThisAndMultiply(weights*response)).ToArray(); } /// From d6a2a2b7ffe3e8235d9c493d372b51864b853641 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sat, 30 Nov 2013 11:32:39 +0100 Subject: [PATCH 013/109] Meta: update Vagrantfile, DotSettings, git attributes --- MathNet.Numerics.Portable.sln.DotSettings | 1 + Vagrantfile | 8 +- packages/.gitattributes | 3 + .../FSharp.Formatting.1.0.15.nuspec | 36 +- .../literate/StringParsing.fs | 320 +++++++++--------- .../literate/content/tips.js | 92 ++--- .../literate/templates/template-file.html | 68 ++-- .../literate/templates/template-project.html | 122 +++---- packages/FsUnit.1.2.1.0/FsUnit.1.2.1.0.nuspec | 42 +-- packages/NUnit.2.6.3/NUnit.2.6.3.nuspec | 52 +-- 10 files changed, 374 insertions(+), 370 deletions(-) create mode 100644 packages/.gitattributes diff --git a/MathNet.Numerics.Portable.sln.DotSettings b/MathNet.Numerics.Portable.sln.DotSettings index a97eba6c..cd49821b 100644 --- a/MathNet.Numerics.Portable.sln.DotSettings +++ b/MathNet.Numerics.Portable.sln.DotSettings @@ -66,5 +66,6 @@ OTHER DEALINGS IN THE SOFTWARE. SVD TFQMR WH + True <data /> <data><IncludeFilters /><ExcludeFilters /></data> \ No newline at end of file diff --git a/Vagrantfile b/Vagrantfile index 11f013ec..1029786a 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -2,14 +2,14 @@ Vagrant.configure("2") do |config| - config.vm.box = "wheezy64-mono3.0.10-fsharp3.0.27" - config.vm.box_url = "https://dl.dropboxusercontent.com/s/uelesklqouaw1gl/wheezy64-mono3.0.10-fsharp3.0.27-virtualbox.box" + config.vm.box = "wheezy64-mono3.2.5-fsharp3.1-dev" + config.vm.box_url = "https://skydrive.live.com/redir.aspx?cid=84f3672f8cda3e91&resid=84F3672F8CDA3E91!29372&parid=84F3672F8CDA3E91!28978&authkey=!ABWHPlJFVtdF4bA" # default is only 384 MB RAM, 1 CPU in that box - we need some more config.vm.provider :virtualbox do |vb| vb.gui = false - vb.customize ["modifyvm", :id, "--memory", "1024"] - vb.customize ["modifyvm", :id, "--cpus", "2"] + vb.customize ["modifyvm", :id, "--memory", "4096"] + vb.customize ["modifyvm", :id, "--cpus", "4"] end # Shell Provisioning external: diff --git a/packages/.gitattributes b/packages/.gitattributes new file mode 100644 index 00000000..374c9471 --- /dev/null +++ b/packages/.gitattributes @@ -0,0 +1,3 @@ +repositories.config text +* -text + diff --git a/packages/FSharp.Formatting.1.0.15/FSharp.Formatting.1.0.15.nuspec b/packages/FSharp.Formatting.1.0.15/FSharp.Formatting.1.0.15.nuspec index 9a63739e..f1132e74 100644 --- a/packages/FSharp.Formatting.1.0.15/FSharp.Formatting.1.0.15.nuspec +++ b/packages/FSharp.Formatting.1.0.15/FSharp.Formatting.1.0.15.nuspec @@ -1,19 +1,19 @@ - - - - FSharp.Formatting - 1.0.15 - FSharp.Formatting - Tomas Petricek, Oleg Pestov, Anh-Dung Phan - Tomas Petricek, Oleg Pestov, Anh-Dung Phan - http://github.com/tpetricek/FSharp.Formatting/blob/master/LICENSE.md - http://github.com/tpetricek/FSharp.Formatting - https://raw.github.com/tpetricek/FSharp.Formatting/master/docs/misc/logo.png - false - Provides an F# implementation of Markdown parser and F# code formatter that can used to tokenize F# code and obtain information about tokens including tool tips with type information. The package comes with a sample that implements literate programming for F#. - Added latex support, tables and better formatting with line numbers - Copyright 2013 - - F# fsharp formatting markdown code fssnip literate programming - + + + + FSharp.Formatting + 1.0.15 + FSharp.Formatting + Tomas Petricek, Oleg Pestov, Anh-Dung Phan + Tomas Petricek, Oleg Pestov, Anh-Dung Phan + http://github.com/tpetricek/FSharp.Formatting/blob/master/LICENSE.md + http://github.com/tpetricek/FSharp.Formatting + https://raw.github.com/tpetricek/FSharp.Formatting/master/docs/misc/logo.png + false + Provides an F# implementation of Markdown parser and F# code formatter that can used to tokenize F# code and obtain information about tokens including tool tips with type information. The package comes with a sample that implements literate programming for F#. + Added latex support, tables and better formatting with line numbers + Copyright 2013 + + F# fsharp formatting markdown code fssnip literate programming + \ No newline at end of file diff --git a/packages/FSharp.Formatting.1.0.15/literate/StringParsing.fs b/packages/FSharp.Formatting.1.0.15/literate/StringParsing.fs index 4e5e2956..9cb1747a 100644 --- a/packages/FSharp.Formatting.1.0.15/literate/StringParsing.fs +++ b/packages/FSharp.Formatting.1.0.15/literate/StringParsing.fs @@ -1,160 +1,160 @@ -// -------------------------------------------------------------------------------------- -// F# Markdown (StringParsing.fs) -// (c) Tomas Petricek, 2012, Available under Apache 2.0 license. -// -------------------------------------------------------------------------------------- - -module FSharp.Patterns - -open System -open FSharp.Collections - -// -------------------------------------------------------------------------------------- -// Active patterns that simplify parsing of strings and lists of strings (lines) -// -------------------------------------------------------------------------------------- - -module String = - /// Matches when a string is a whitespace or null - let (|WhiteSpace|_|) s = - if String.IsNullOrWhiteSpace(s) then Some() else None - - /// Matches when a string does starts with non-whitespace - let (|Unindented|_|) (s:string) = - if not (String.IsNullOrWhiteSpace(s)) && s.TrimStart() = s then Some() else None - - /// Returns a string trimmed from both start and end - let (|TrimBoth|) (text:string) = text.Trim() - /// Returns a string trimmed from the end - let (|TrimEnd|) (text:string) = text.TrimEnd() - /// Returns a string trimmed from the start - let (|TrimStart|) (text:string) = text.TrimStart() - - /// Retrusn a string trimmed from the end using characters given as a parameter - let (|TrimEndUsing|) chars (text:string) = text.TrimEnd(Array.ofSeq chars) - - /// Returns a string trimmed from the start together with - /// the number of skipped whitespace characters - let (|TrimStartAndCount|) (text:string) = - let trimmed = text.TrimStart() - text.Length - trimmed.Length, trimmed - - /// Matches when a string starts with any of the specified sub-strings - let (|StartsWithAny|_|) (starts:seq) (text:string) = - if starts |> Seq.exists (text.StartsWith) then Some() else None - /// Matches when a string starts with the specified sub-string - let (|StartsWith|_|) (start:string) (text:string) = - if text.StartsWith(start) then Some(text.Substring(start.Length)) else None - /// Matches when a string starts with the specified sub-string - /// The matched string is trimmed from all whitespace. - let (|StartsWithTrim|_|) (start:string) (text:string) = - if text.StartsWith(start) then Some(text.Substring(start.Length).Trim()) else None - - /// Matches when a string starts with the given value and ends - /// with a given value (and returns the rest of it) - let (|StartsAndEndsWith|_|) (starts, ends) (s:string) = - if s.StartsWith(starts) && s.EndsWith(ends) && - s.Length >= starts.Length + ends.Length then - Some(s.Substring(starts.Length, s.Length - starts.Length - ends.Length)) - else None - - /// Matches when a string starts with the given value and ends - /// with a given value (and returns trimmed body) - let (|StartsAndEndsWithTrim|_|) args = function - | StartsAndEndsWith args (TrimBoth res) -> Some res - | _ -> None - - /// Matches when a string starts with a non-zero number of complete - /// repetitions of the specified parameter (and returns the number - /// of repetitions, together with the rest of the string) - /// - /// let (StartsWithRepeated "/\" (2, " abc")) = "/\/\ abc" - /// - let (|StartsWithRepeated|_|) (repeated:string) (text:string) = - let rec loop i = - if i = text.Length then i - elif text.[i] <> repeated.[i % repeated.Length] then i - else loop (i + 1) - - let n = loop 0 - if n = 0 || n % repeated.Length <> 0 then None - else Some(n/repeated.Length, text.Substring(n, text.Length - n)) - - /// Matches when a string starts with a sub-string wrapped using the - /// opening and closing sub-string specified in the parameter. - /// For example "[aa]bc" is wrapped in [ and ] pair. Returns the wrapped - /// text together with the rest. - let (|StartsWithWrapped|_|) (starts:string, ends:string) (text:string) = - if text.StartsWith(starts) then - let id = text.IndexOf(ends, starts.Length) - if id >= 0 then - let wrapped = text.Substring(starts.Length, id - starts.Length) - let rest = text.Substring(id + ends.Length, text.Length - id - ends.Length) - Some(wrapped, rest) - else None - else None - - /// Matches when a string consists of some number of - /// complete repetitions of a specified sub-string. - let (|EqualsRepeated|_|) repeated = function - | StartsWithRepeated repeated (n, "") -> Some() - | _ -> None - -module List = - /// Matches a list if it starts with a sub-list that is delimited - /// using the specified delimiters. Returns a wrapped list and the rest. - let inline (|DelimitedWith|_|) startl endl input = - if List.startsWith startl input then - match List.partitionUntilEquals endl (List.skip startl.Length input) with - | Some(pre, post) -> Some(pre, List.skip endl.Length post) - | None -> None - else None - - /// Matches a list if it starts with a sub-list that is delimited - /// using the specified delimiter. Returns a wrapped list and the rest. - let inline (|Delimited|_|) str = (|DelimitedWith|_|) str str - - /// Matches a list if it starts with a bracketed list. Nested brackets - /// are skipped (by counting opening and closing brackets) and can be - /// escaped using the '\' symbol. - let (|BracketDelimited|_|) startc endc input = - let rec loop acc count = function - | '\\'::x::xs when x = endc -> loop (x::acc) count xs - | x::xs when x = endc && count = 0 -> Some(List.rev acc, xs) - | x::xs when x = endc -> loop (x::acc) (count - 1) xs - | x::xs when x = startc -> loop (x::acc) (count + 1) xs - | x::xs -> loop (x::acc) count xs - | [] -> None - match input with - | x::xs when x = startc -> loop [] 0 xs - | _ -> None - - /// Retruns a list of characters as a string. - let (|AsString|) chars = String(Array.ofList chars) - -module Lines = - /// Removes blank lines from the start and the end of a list - let (|TrimBlank|) lines = - lines - |> List.skipWhile String.IsNullOrWhiteSpace |> List.rev - |> List.skipWhile String.IsNullOrWhiteSpace |> List.rev - - /// Matches when there are some lines at the beginning that are - /// either empty (or whitespace) or start with the specified string. - /// Returns all such lines from the beginning until a different line. - let (|TakeStartingWithOrBlank|_|) start input = - match List.partitionWhile (fun s -> - String.IsNullOrWhiteSpace s || s.StartsWith(start)) input with - | matching, rest when matching <> [] -> Some(matching, rest) - | _ -> None - - /// Removes whitespace lines from the beginning of the list - let (|TrimBlankStart|) = List.skipWhile (String.IsNullOrWhiteSpace) - - -/// Parameterized pattern that assigns the specified value to the -/// first component of a tuple. Usage: -/// -/// match str with -/// | Let 1 (n, "one") | Let 2 (n, "two") -> n -/// -let (|Let|) a b = (a, b) - +// -------------------------------------------------------------------------------------- +// F# Markdown (StringParsing.fs) +// (c) Tomas Petricek, 2012, Available under Apache 2.0 license. +// -------------------------------------------------------------------------------------- + +module FSharp.Patterns + +open System +open FSharp.Collections + +// -------------------------------------------------------------------------------------- +// Active patterns that simplify parsing of strings and lists of strings (lines) +// -------------------------------------------------------------------------------------- + +module String = + /// Matches when a string is a whitespace or null + let (|WhiteSpace|_|) s = + if String.IsNullOrWhiteSpace(s) then Some() else None + + /// Matches when a string does starts with non-whitespace + let (|Unindented|_|) (s:string) = + if not (String.IsNullOrWhiteSpace(s)) && s.TrimStart() = s then Some() else None + + /// Returns a string trimmed from both start and end + let (|TrimBoth|) (text:string) = text.Trim() + /// Returns a string trimmed from the end + let (|TrimEnd|) (text:string) = text.TrimEnd() + /// Returns a string trimmed from the start + let (|TrimStart|) (text:string) = text.TrimStart() + + /// Retrusn a string trimmed from the end using characters given as a parameter + let (|TrimEndUsing|) chars (text:string) = text.TrimEnd(Array.ofSeq chars) + + /// Returns a string trimmed from the start together with + /// the number of skipped whitespace characters + let (|TrimStartAndCount|) (text:string) = + let trimmed = text.TrimStart() + text.Length - trimmed.Length, trimmed + + /// Matches when a string starts with any of the specified sub-strings + let (|StartsWithAny|_|) (starts:seq) (text:string) = + if starts |> Seq.exists (text.StartsWith) then Some() else None + /// Matches when a string starts with the specified sub-string + let (|StartsWith|_|) (start:string) (text:string) = + if text.StartsWith(start) then Some(text.Substring(start.Length)) else None + /// Matches when a string starts with the specified sub-string + /// The matched string is trimmed from all whitespace. + let (|StartsWithTrim|_|) (start:string) (text:string) = + if text.StartsWith(start) then Some(text.Substring(start.Length).Trim()) else None + + /// Matches when a string starts with the given value and ends + /// with a given value (and returns the rest of it) + let (|StartsAndEndsWith|_|) (starts, ends) (s:string) = + if s.StartsWith(starts) && s.EndsWith(ends) && + s.Length >= starts.Length + ends.Length then + Some(s.Substring(starts.Length, s.Length - starts.Length - ends.Length)) + else None + + /// Matches when a string starts with the given value and ends + /// with a given value (and returns trimmed body) + let (|StartsAndEndsWithTrim|_|) args = function + | StartsAndEndsWith args (TrimBoth res) -> Some res + | _ -> None + + /// Matches when a string starts with a non-zero number of complete + /// repetitions of the specified parameter (and returns the number + /// of repetitions, together with the rest of the string) + /// + /// let (StartsWithRepeated "/\" (2, " abc")) = "/\/\ abc" + /// + let (|StartsWithRepeated|_|) (repeated:string) (text:string) = + let rec loop i = + if i = text.Length then i + elif text.[i] <> repeated.[i % repeated.Length] then i + else loop (i + 1) + + let n = loop 0 + if n = 0 || n % repeated.Length <> 0 then None + else Some(n/repeated.Length, text.Substring(n, text.Length - n)) + + /// Matches when a string starts with a sub-string wrapped using the + /// opening and closing sub-string specified in the parameter. + /// For example "[aa]bc" is wrapped in [ and ] pair. Returns the wrapped + /// text together with the rest. + let (|StartsWithWrapped|_|) (starts:string, ends:string) (text:string) = + if text.StartsWith(starts) then + let id = text.IndexOf(ends, starts.Length) + if id >= 0 then + let wrapped = text.Substring(starts.Length, id - starts.Length) + let rest = text.Substring(id + ends.Length, text.Length - id - ends.Length) + Some(wrapped, rest) + else None + else None + + /// Matches when a string consists of some number of + /// complete repetitions of a specified sub-string. + let (|EqualsRepeated|_|) repeated = function + | StartsWithRepeated repeated (n, "") -> Some() + | _ -> None + +module List = + /// Matches a list if it starts with a sub-list that is delimited + /// using the specified delimiters. Returns a wrapped list and the rest. + let inline (|DelimitedWith|_|) startl endl input = + if List.startsWith startl input then + match List.partitionUntilEquals endl (List.skip startl.Length input) with + | Some(pre, post) -> Some(pre, List.skip endl.Length post) + | None -> None + else None + + /// Matches a list if it starts with a sub-list that is delimited + /// using the specified delimiter. Returns a wrapped list and the rest. + let inline (|Delimited|_|) str = (|DelimitedWith|_|) str str + + /// Matches a list if it starts with a bracketed list. Nested brackets + /// are skipped (by counting opening and closing brackets) and can be + /// escaped using the '\' symbol. + let (|BracketDelimited|_|) startc endc input = + let rec loop acc count = function + | '\\'::x::xs when x = endc -> loop (x::acc) count xs + | x::xs when x = endc && count = 0 -> Some(List.rev acc, xs) + | x::xs when x = endc -> loop (x::acc) (count - 1) xs + | x::xs when x = startc -> loop (x::acc) (count + 1) xs + | x::xs -> loop (x::acc) count xs + | [] -> None + match input with + | x::xs when x = startc -> loop [] 0 xs + | _ -> None + + /// Retruns a list of characters as a string. + let (|AsString|) chars = String(Array.ofList chars) + +module Lines = + /// Removes blank lines from the start and the end of a list + let (|TrimBlank|) lines = + lines + |> List.skipWhile String.IsNullOrWhiteSpace |> List.rev + |> List.skipWhile String.IsNullOrWhiteSpace |> List.rev + + /// Matches when there are some lines at the beginning that are + /// either empty (or whitespace) or start with the specified string. + /// Returns all such lines from the beginning until a different line. + let (|TakeStartingWithOrBlank|_|) start input = + match List.partitionWhile (fun s -> + String.IsNullOrWhiteSpace s || s.StartsWith(start)) input with + | matching, rest when matching <> [] -> Some(matching, rest) + | _ -> None + + /// Removes whitespace lines from the beginning of the list + let (|TrimBlankStart|) = List.skipWhile (String.IsNullOrWhiteSpace) + + +/// Parameterized pattern that assigns the specified value to the +/// first component of a tuple. Usage: +/// +/// match str with +/// | Let 1 (n, "one") | Let 2 (n, "two") -> n +/// +let (|Let|) a b = (a, b) + diff --git a/packages/FSharp.Formatting.1.0.15/literate/content/tips.js b/packages/FSharp.Formatting.1.0.15/literate/content/tips.js index 28ed18f6..1cd162f9 100644 --- a/packages/FSharp.Formatting.1.0.15/literate/content/tips.js +++ b/packages/FSharp.Formatting.1.0.15/literate/content/tips.js @@ -1,47 +1,47 @@ -var currentTip = null; -var currentTipElement = null; - -function hideTip(evt, name, unique) -{ - var el = document.getElementById(name); - el.style.display = "none"; - currentTip = null; -} - -function findPos(obj) -{ - var curleft = 0; - var curtop = obj.offsetHeight; - while (obj) - { - curleft += obj.offsetLeft; - curtop += obj.offsetTop; - obj = obj.offsetParent; - }; - return [curleft, curtop]; -} - -function hideUsingEsc(e) -{ - if (!e) { e = event; } - hideTip(e, currentTipElement, currentTip); -} - -function showTip(evt, name, unique, owner) -{ - document.onkeydown = hideUsingEsc; - if (currentTip == unique) return; - currentTip = unique; - currentTipElement = name; - - var pos = findPos(owner ? owner : (evt.srcElement ? evt.srcElement : evt.target)); - var posx = pos[0]; - var posy = pos[1]; - - var el = document.getElementById(name); - var parent = (document.documentElement == null) ? document.body : document.documentElement; - el.style.position = "absolute"; - el.style.left = posx + "px"; - el.style.top = posy + "px"; - el.style.display = "block"; +var currentTip = null; +var currentTipElement = null; + +function hideTip(evt, name, unique) +{ + var el = document.getElementById(name); + el.style.display = "none"; + currentTip = null; +} + +function findPos(obj) +{ + var curleft = 0; + var curtop = obj.offsetHeight; + while (obj) + { + curleft += obj.offsetLeft; + curtop += obj.offsetTop; + obj = obj.offsetParent; + }; + return [curleft, curtop]; +} + +function hideUsingEsc(e) +{ + if (!e) { e = event; } + hideTip(e, currentTipElement, currentTip); +} + +function showTip(evt, name, unique, owner) +{ + document.onkeydown = hideUsingEsc; + if (currentTip == unique) return; + currentTip = unique; + currentTipElement = name; + + var pos = findPos(owner ? owner : (evt.srcElement ? evt.srcElement : evt.target)); + var posx = pos[0]; + var posy = pos[1]; + + var el = document.getElementById(name); + var parent = (document.documentElement == null) ? document.body : document.documentElement; + el.style.position = "absolute"; + el.style.left = posx + "px"; + el.style.top = posy + "px"; + el.style.display = "block"; } \ No newline at end of file diff --git a/packages/FSharp.Formatting.1.0.15/literate/templates/template-file.html b/packages/FSharp.Formatting.1.0.15/literate/templates/template-file.html index c1a7e77c..d4d7897c 100644 --- a/packages/FSharp.Formatting.1.0.15/literate/templates/template-file.html +++ b/packages/FSharp.Formatting.1.0.15/literate/templates/template-file.html @@ -1,35 +1,35 @@ - - - - - - {page-title} - - - - - - - - - - - -
-
-
-
- {document} - {tooltips} -
-
-
-
- + + + + + + {page-title} + + + + + + + + + + + +
+
+
+
+ {document} + {tooltips} +
+
+
+
+ \ No newline at end of file diff --git a/packages/FSharp.Formatting.1.0.15/literate/templates/template-project.html b/packages/FSharp.Formatting.1.0.15/literate/templates/template-project.html index f6803c9a..dbc3f7ba 100644 --- a/packages/FSharp.Formatting.1.0.15/literate/templates/template-project.html +++ b/packages/FSharp.Formatting.1.0.15/literate/templates/template-project.html @@ -1,62 +1,62 @@ - - - - - - {page-title} - - - - - - - - - - - - - -
-
- -

{project-name}

-
-
-
-
- {document} - {tooltips} -
-
- - -
-
-
- Fork me on GitHub - + + + + + + {page-title} + + + + + + + + + + + + + +
+
+ +

{project-name}

+
+
+
+
+ {document} + {tooltips} +
+
+ + +
+
+
+ Fork me on GitHub + \ No newline at end of file diff --git a/packages/FsUnit.1.2.1.0/FsUnit.1.2.1.0.nuspec b/packages/FsUnit.1.2.1.0/FsUnit.1.2.1.0.nuspec index 0964d434..bfbde7ef 100644 --- a/packages/FsUnit.1.2.1.0/FsUnit.1.2.1.0.nuspec +++ b/packages/FsUnit.1.2.1.0/FsUnit.1.2.1.0.nuspec @@ -1,22 +1,22 @@ - - - - FsUnit - 1.2.1.0 - FsUnit - Ray Vernagus and Daniel Mohl - Ray Vernagus and Daniel Mohl - http://fsunit.codeplex.com/license - http://fsunit.codeplex.com/ - false - FsUnit is a set of extensions that add special testing syntax to NUnit. - The goals of FsUnit are to make unit-testing feel more functional while leverage existing testing frameworks. - - - en-US - F# fsharp NUnit FsUnit - - - - + + + + FsUnit + 1.2.1.0 + FsUnit + Ray Vernagus and Daniel Mohl + Ray Vernagus and Daniel Mohl + http://fsunit.codeplex.com/license + http://fsunit.codeplex.com/ + false + FsUnit is a set of extensions that add special testing syntax to NUnit. + The goals of FsUnit are to make unit-testing feel more functional while leverage existing testing frameworks. + + + en-US + F# fsharp NUnit FsUnit + + + + \ No newline at end of file diff --git a/packages/NUnit.2.6.3/NUnit.2.6.3.nuspec b/packages/NUnit.2.6.3/NUnit.2.6.3.nuspec index a5773095..6532fdd8 100644 --- a/packages/NUnit.2.6.3/NUnit.2.6.3.nuspec +++ b/packages/NUnit.2.6.3/NUnit.2.6.3.nuspec @@ -1,27 +1,27 @@ - - - - NUnit - 2.6.3 - NUnit - Charlie Poole - Charlie Poole - http://nunit.org/nuget/license.html - http://nunit.org/ - http://nunit.org/nuget/nunit_32x32.png - false - NUnit features a fluent assert syntax, parameterized, generic and theory tests and is user-extensible. A number of runners, both from the NUnit project and by third parties, are able to execute NUnit tests. - -Version 2.6 is the seventh major release of this well-known and well-tested programming tool. - -This package includes only the framework assembly. You will need to install the NUnit.Runners package unless you are using a third-party runner. - NUnit is a unit-testing framework for all .Net languages with a strong TDD focus. - Version 2.6 is the seventh major release of NUnit. - -Unlike earlier versions, this package includes only the framework assembly. You will need to install the NUnit.Runners package unless you are using a third-party runner. - -The nunit.mocks assembly is now provided by the NUnit.Mocks package. The pnunit.framework assembly is provided by the pNUnit package. - en-US - nunit test testing tdd framework fluent assert theory plugin addin - + + + + NUnit + 2.6.3 + NUnit + Charlie Poole + Charlie Poole + http://nunit.org/nuget/license.html + http://nunit.org/ + http://nunit.org/nuget/nunit_32x32.png + false + NUnit features a fluent assert syntax, parameterized, generic and theory tests and is user-extensible. A number of runners, both from the NUnit project and by third parties, are able to execute NUnit tests. + +Version 2.6 is the seventh major release of this well-known and well-tested programming tool. + +This package includes only the framework assembly. You will need to install the NUnit.Runners package unless you are using a third-party runner. + NUnit is a unit-testing framework for all .Net languages with a strong TDD focus. + Version 2.6 is the seventh major release of NUnit. + +Unlike earlier versions, this package includes only the framework assembly. You will need to install the NUnit.Runners package unless you are using a third-party runner. + +The nunit.mocks assembly is now provided by the NUnit.Mocks package. The pnunit.framework assembly is provided by the pNUnit package. + en-US + nunit test testing tdd framework fluent assert theory plugin addin + \ No newline at end of file From 8b921173567b9ad6b037f96e555d9b6307d96d59 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sun, 1 Dec 2013 14:27:55 +0100 Subject: [PATCH 014/109] LA: Matrix.ClearSubMatrix should nop-return instead of fail on empty/negative selection --- src/Numerics/LinearAlgebra/Matrix.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/Numerics/LinearAlgebra/Matrix.cs b/src/Numerics/LinearAlgebra/Matrix.cs index 118f6302..b6c18723 100644 --- a/src/Numerics/LinearAlgebra/Matrix.cs +++ b/src/Numerics/LinearAlgebra/Matrix.cs @@ -177,14 +177,10 @@ namespace MathNet.Numerics.LinearAlgebra ///
public void ClearSubMatrix(int rowIndex, int rowCount, int columnIndex, int columnCount) { - if (rowCount < 1) + if (rowCount < 1 || columnCount < 1) { - throw new ArgumentOutOfRangeException("rowCount", Resources.ArgumentMustBePositive); - } - - if (columnCount < 1) - { - throw new ArgumentOutOfRangeException("columnCount", Resources.ArgumentMustBePositive); + // nothing to do (but no need to fail either) + return; } if (rowIndex + rowCount > RowCount || rowIndex < 0) From 3259ce6ba28ac24c229066e13468402906ffc0a5 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sun, 1 Dec 2013 15:53:48 +0100 Subject: [PATCH 015/109] LA: extend diagonal matrix builder --- src/Numerics/LinearAlgebra/Builder.cs | 54 +++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/Numerics/LinearAlgebra/Builder.cs b/src/Numerics/LinearAlgebra/Builder.cs index 0a8907e0..e668e7e3 100644 --- a/src/Numerics/LinearAlgebra/Builder.cs +++ b/src/Numerics/LinearAlgebra/Builder.cs @@ -1101,6 +1101,60 @@ namespace MathNet.Numerics.LinearAlgebra ///
public abstract Matrix Diagonal(DiagonalMatrixStorage storage); + /// + /// Create a new diagonal matrix with the given number of rows and columns. + /// All cells of the matrix will be initialized to zero. + /// Zero-length matrices are not supported. + /// + public Matrix Diagonal(int rows, int columns) + { + return Diagonal(new DiagonalMatrixStorage(rows, columns)); + } + + /// + /// Create a new diagonal matrix with the given number of rows and columns directly binding to a raw array. + /// The array is assumed to represent the diagonal values and is used directly without copying. + /// Very efficient, but changes to the array and the matrix will affect each other. + /// + /// + public Matrix Diagonal(int rows, int columns, T[] storage) + { + return Diagonal(new DiagonalMatrixStorage(rows, columns, storage)); + } + + /// + /// Create a new diagonal matrix and initialize each diagonal value to the same provided value. + /// + public Matrix Diagonal(int rows, int columns, T value) + { + if (Zero.Equals(value)) return Diagonal(rows, columns); + return Diagonal(DiagonalMatrixStorage.OfInit(rows, columns, i => value)); + } + + /// + /// Create a new diagonal matrix and initialize each diagonal value using the provided init function. + /// + public Matrix Diagonal(int rows, int columns, Func init) + { + return Diagonal(DiagonalMatrixStorage.OfInit(rows, columns, init)); + } + + /// + /// Create a new diagonal identity matrix with a one-diagonal. + /// + public Matrix DiagonalIdentity(int rows, int columns) + { + return Diagonal(DiagonalMatrixStorage.OfInit(rows, columns, i => One)); + } + + /// + /// Create a new diagonal identity matrix with a one-diagonal. + /// + public Matrix DiagonalIdentity(int order) + { + return Diagonal(DiagonalMatrixStorage.OfInit(order, order, i => One)); + } + public abstract IIterationStopCriterium[] IterativeSolverStopCriteria(int maxIterations = 1000); } From 06be8470b6a8944ea582e909feec71bf8ab6f28b Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sun, 1 Dec 2013 16:01:55 +0100 Subject: [PATCH 016/109] LA: dense-diagonal Add Subtract Multiply --- .../LinearAlgebra/Complex/DenseMatrix.cs | 293 ++++++++++-------- .../LinearAlgebra/Complex/DiagonalMatrix.cs | 31 +- src/Numerics/LinearAlgebra/Complex/Matrix.cs | 43 ++- .../LinearAlgebra/Complex32/DenseMatrix.cs | 293 ++++++++++-------- .../LinearAlgebra/Complex32/DiagonalMatrix.cs | 29 +- .../LinearAlgebra/Complex32/Matrix.cs | 43 ++- .../LinearAlgebra/Double/DenseMatrix.cs | 146 ++++++--- .../LinearAlgebra/Double/DiagonalMatrix.cs | 31 +- src/Numerics/LinearAlgebra/Double/Matrix.cs | 53 ++-- .../LinearAlgebra/Matrix.Arithmetic.cs | 2 +- .../LinearAlgebra/Single/DenseMatrix.cs | 143 ++++++--- .../LinearAlgebra/Single/DiagonalMatrix.cs | 29 +- src/Numerics/LinearAlgebra/Single/Matrix.cs | 43 ++- .../Complex/DiagonalMatrixTests.cs | 20 ++ .../Complex32/DiagonalMatrixTests.cs | 19 ++ .../Double/DiagonalMatrixTests.cs | 20 ++ .../Single/DiagonalMatrixTests.cs | 20 ++ 17 files changed, 692 insertions(+), 566 deletions(-) diff --git a/src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs b/src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs index 7142e826..43adc606 100644 --- a/src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs @@ -407,6 +407,27 @@ namespace MathNet.Numerics.LinearAlgebra.Complex get { return _values; } } + /// Calculates the induced L1 norm of this matrix. + /// The maximum absolute column sum of the matrix. + public override double L1Norm() + { + return Control.LinearAlgebraProvider.MatrixNorm(Norm.OneNorm, _rowCount, _columnCount, _values); + } + + /// Calculates the induced infinity norm of this matrix. + /// The maximum absolute row sum of the matrix. + public override double InfinityNorm() + { + return Control.LinearAlgebraProvider.MatrixNorm(Norm.InfinityNorm, _rowCount, _columnCount, _values); + } + + /// Calculates the entry-wise Frobenius norm of this matrix. + /// The square root of the sum of the squared values. + public override double FrobeniusNorm() + { + return Control.LinearAlgebraProvider.MatrixNorm(Norm.FrobeniusNorm, _rowCount, _columnCount, _values); + } + /// /// Returns the transpose of this matrix. /// @@ -422,29 +443,139 @@ namespace MathNet.Numerics.LinearAlgebra.Complex ret._values[(i * _columnCount) + j] = _values[index + i]; } } + return ret; + } + /// + /// Returns the conjugate transpose of this matrix. + /// + /// The conjugate transpose of this matrix. + public override Matrix ConjugateTranspose() + { + var ret = new DenseMatrix(_columnCount, _rowCount); + for (var j = 0; j < _columnCount; j++) + { + var index = j * _rowCount; + for (var i = 0; i < _rowCount; i++) + { + ret._values[(i * _columnCount) + j] = _values[index + i].Conjugate(); + } + } return ret; } - /// Calculates the induced L1 norm of this matrix. - /// The maximum absolute column sum of the matrix. - public override double L1Norm() + /// + /// Add a scalar to each element of the matrix and stores the result in the result vector. + /// + /// The scalar to add. + /// The matrix to store the result of the addition. + protected override void DoAdd(Complex scalar, Matrix result) { - return Control.LinearAlgebraProvider.MatrixNorm(Norm.OneNorm, _rowCount, _columnCount, _values); + var denseResult = result as DenseMatrix; + if (denseResult == null) + { + base.DoAdd(scalar, result); + return; + } + + CommonParallel.For(0, _values.Length, 4096, (a, b) => + { + var v = denseResult._values; + for (int i = a; i < b; i++) + { + v[i] = _values[i] + scalar; + } + }); } - /// Calculates the induced infinity norm of this matrix. - /// The maximum absolute row sum of the matrix. - public override double InfinityNorm() + /// + /// Adds another matrix to this matrix. + /// + /// The matrix to add to this matrix. + /// The matrix to store the result of add + /// If the other matrix is . + /// If the two matrices don't have the same dimensions. + protected override void DoAdd(Matrix other, Matrix result) { - return Control.LinearAlgebraProvider.MatrixNorm(Norm.InfinityNorm, _rowCount, _columnCount, _values); + // dense + dense = dense + var denseOther = other.Storage as DenseColumnMajorMatrixStorage; + var denseResult = result.Storage as DenseColumnMajorMatrixStorage; + if (denseOther != null && denseResult != null) + { + Control.LinearAlgebraProvider.AddArrays(_values, denseOther.Data, denseResult.Data); + return; + } + + // dense + diagonal = matrix + var diagonalOther = other.Storage as DiagonalMatrixStorage; + if (diagonalOther != null) + { + CopyTo(result); + var diagonal = diagonalOther.Data; + for (int i = 0; i < diagonal.Length; i++) + { + result.At(i, i, result.At(i, i) + diagonal[i]); + } + return; + } + + base.DoAdd(other, result); } - /// Calculates the entry-wise Frobenius norm of this matrix. - /// The square root of the sum of the squared values. - public override double FrobeniusNorm() + /// + /// Subtracts a scalar from each element of the matrix and stores the result in the result vector. + /// + /// The scalar to subtract. + /// The matrix to store the result of the subtraction. + protected override void DoSubtract(Complex scalar, Matrix result) { - return Control.LinearAlgebraProvider.MatrixNorm(Norm.FrobeniusNorm, _rowCount, _columnCount, _values); + var denseResult = result as DenseMatrix; + if (denseResult == null) + { + base.DoSubtract(scalar, result); + return; + } + + CommonParallel.For(0, _values.Length, 4096, (a, b) => + { + var v = denseResult._values; + for (int i = a; i < b; i++) + { + v[i] = _values[i] - scalar; + } + }); + } + + /// + /// Subtracts another matrix from this matrix. + /// + /// The matrix to subtract. + /// The matrix to store the result of the subtraction. + protected override void DoSubtract(Matrix other, Matrix result) + { + // dense + dense = dense + var denseOther = other.Storage as DenseColumnMajorMatrixStorage; + var denseResult = result.Storage as DenseColumnMajorMatrixStorage; + if (denseOther != null && denseResult != null) + { + Control.LinearAlgebraProvider.SubtractArrays(_values, denseOther.Data, denseResult.Data); + return; + } + + // dense + diagonal = matrix + var diagonalOther = other.Storage as DiagonalMatrixStorage; + if (diagonalOther != null) + { + CopyTo(result); + var diagonal = diagonalOther.Data; + for (int i = 0; i < diagonal.Length; i++) + { + result.At(i, i, result.At(i, i) - diagonal[i]); + } + return; + } + + base.DoSubtract(other, result); } /// @@ -505,12 +636,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex { var denseOther = other as DenseMatrix; var denseResult = result as DenseMatrix; - - if (denseOther == null || denseResult == null) - { - base.DoMultiply(other, result); - } - else + if (denseOther != null && denseResult != null) { Control.LinearAlgebraProvider.MatrixMultiplyWithUpdate( Providers.LinearAlgebra.Transpose.DontTranspose, @@ -524,7 +650,31 @@ namespace MathNet.Numerics.LinearAlgebra.Complex denseOther._columnCount, 0.0, denseResult._values); + return; } + + var diagonalOther = other.Storage as DiagonalMatrixStorage; + if (diagonalOther != null) + { + var diagonal = diagonalOther.Data; + var d = Math.Min(ColumnCount, other.ColumnCount); + if (d < other.ColumnCount) + { + result.ClearSubMatrix(0, RowCount, ColumnCount, other.ColumnCount - ColumnCount); + } + int index = 0; + for (int j = 0; j < d; j++) + { + for (int i = 0; i < RowCount; i++) + { + result.At(i, j, _values[index]*diagonal[j]); + index++; + } + } + return; + } + + base.DoMultiply(other, result); } /// @@ -696,113 +846,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex } } - /// - /// Add a scalar to each element of the matrix and stores the result in the result vector. - /// - /// The scalar to add. - /// The matrix to store the result of the addition. - protected override void DoAdd(Complex scalar, Matrix result) - { - var denseResult = result as DenseMatrix; - if (denseResult == null) - { - base.DoAdd(scalar, result); - return; - } - - CommonParallel.For(0, _values.Length, 4096, (a, b) => - { - var v = denseResult._values; - for (int i = a; i < b; i++) - { - v[i] = _values[i] + scalar; - } - }); - } - - /// - /// Adds another matrix to this matrix. - /// - /// The matrix to add to this matrix. - /// The matrix to store the result of add - /// If the other matrix is . - /// If the two matrices don't have the same dimensions. - protected override void DoAdd(Matrix other, Matrix result) - { - var denseOther = other as DenseMatrix; - var denseResult = result as DenseMatrix; - if (denseOther == null || denseResult == null) - { - base.DoAdd(other, result); - } - else - { - Control.LinearAlgebraProvider.AddArrays(_values, denseOther._values, denseResult._values); - } - } - - /// - /// Subtracts a scalar from each element of the matrix and stores the result in the result vector. - /// - /// The scalar to subtract. - /// The matrix to store the result of the subtraction. - protected override void DoSubtract(Complex scalar, Matrix result) - { - var denseResult = result as DenseMatrix; - if (denseResult == null) - { - base.DoSubtract(scalar, result); - return; - } - - CommonParallel.For(0, _values.Length, 4096, (a, b) => - { - var v = denseResult._values; - for (int i = a; i < b; i++) - { - v[i] = _values[i] - scalar; - } - }); - } - - /// - /// Subtracts another matrix from this matrix. - /// - /// The matrix to subtract. - /// The matrix to store the result of the subtraction. - protected override void DoSubtract(Matrix other, Matrix result) - { - var denseOther = other as DenseMatrix; - var denseResult = result as DenseMatrix; - if (denseOther == null || denseResult == null) - { - base.DoSubtract(other, result); - } - else - { - Control.LinearAlgebraProvider.SubtractArrays(_values, denseOther._values, denseResult._values); - } - } - - /// - /// Returns the conjugate transpose of this matrix. - /// - /// The conjugate transpose of this matrix. - public override Matrix ConjugateTranspose() - { - var ret = new DenseMatrix(_columnCount, _rowCount); - for (var j = 0; j < _columnCount; j++) - { - var index = j * _rowCount; - for (var i = 0; i < _rowCount; i++) - { - ret._values[(i * _columnCount) + j] = _values[index + i].Conjugate(); - } - } - - return ret; - } - /// /// Computes the trace of this matrix. /// diff --git a/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs index d6a3e968..c89e3e5b 100644 --- a/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs @@ -588,9 +588,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// i == j (i is the row index, and j is the column index). public override Vector Diagonal() { - // TODO: Should we return reference to array? In current implementation we return copy of array, so changes in DenseVector will - // not influence onto diagonal elements - return new DenseVector((Complex[])_data.Clone()); + return new DenseVector(_data).Clone(); } /// @@ -616,31 +614,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex Multiply(otherDiagonal.Transpose(), result); } - /// - /// Multiplies this matrix with transpose of another matrix and returns the result. - /// - /// The matrix to multiply with. - /// If this.Columns != other.Rows. - /// If the other matrix is . - /// The result of multiplication. - public override Matrix TransposeAndMultiply(Matrix other) - { - var otherDiagonal = other as DiagonalMatrix; - if (otherDiagonal == null) - { - return base.TransposeAndMultiply(other); - } - - if (ColumnCount != otherDiagonal.ColumnCount) - { - throw DimensionsDontMatch(this, otherDiagonal); - } - - var result = other.CreateMatrix(RowCount, other.RowCount); - TransposeAndMultiply(other, result); - return result; - } - /// /// Returns the transpose of this matrix. /// @@ -703,7 +676,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex { if (RowCount != ColumnCount) { - throw new ArgumentException(Resources.ArgumentMatrixSquare); + throw new ArgumentException(Resources.ArgumentMatrixSquare); } var inverse = (DiagonalMatrix)Clone(); diff --git a/src/Numerics/LinearAlgebra/Complex/Matrix.cs b/src/Numerics/LinearAlgebra/Complex/Matrix.cs index 75c1412d..a15cd12c 100644 --- a/src/Numerics/LinearAlgebra/Complex/Matrix.cs +++ b/src/Numerics/LinearAlgebra/Complex/Matrix.cs @@ -41,6 +41,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex using Complex = Numerics.Complex; #else using Complex = System.Numerics.Complex; + #endif /// @@ -48,7 +49,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// [Serializable] public abstract class Matrix : Matrix - { + { /// /// Initializes a new instance of the Matrix class. /// @@ -96,7 +97,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex public override double FrobeniusNorm() { var transpose = ConjugateTranspose(); - var aat = this * transpose; + var aat = this*transpose; var norm = 0d; for (var i = 0; i < RowCount; i++) { @@ -119,7 +120,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex ret.At(j, i, At(i, j).Conjugate()); } } - return ret; } @@ -202,7 +202,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex { for (var j = 0; j < ColumnCount; j++) { - result.At(i, j, At(i, j) * scalar); + result.At(i, j, At(i, j)*scalar); } } } @@ -213,18 +213,17 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// The vector to multiply with. /// The result of the multiplication. protected override void DoMultiply(Vector rightSide, Vector result) - { + { for (var i = 0; i < RowCount; i++) { var s = Complex.Zero; - for (var j = 0; j != ColumnCount; j++) + for (var j = 0; j < ColumnCount; j++) { - s += At(i, j) * rightSide[j]; + s += At(i, j)*rightSide[j]; } - result[i] = s; } - } + } /// /// Multiplies this matrix with another matrix and places the results into the result matrix. @@ -240,9 +239,8 @@ namespace MathNet.Numerics.LinearAlgebra.Complex var s = Complex.Zero; for (var l = 0; l < ColumnCount; l++) { - s += At(j, l) * other.At(l, i); + s += At(j, l)*other.At(l, i); } - result.At(j, i, s); } } @@ -255,7 +253,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// The matrix to store the result of the division. protected override void DoDivide(Complex divisor, Matrix result) { - DoMultiply(1.0 / divisor, result); + DoMultiply(1.0/divisor, result); } /// @@ -269,7 +267,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex { for (var j = 0; j < ColumnCount; j++) { - result.At(i, j, dividend / At(i, j)); + result.At(i, j, dividend/At(i, j)); } } } @@ -288,9 +286,8 @@ namespace MathNet.Numerics.LinearAlgebra.Complex var s = Complex.Zero; for (var l = 0; l < ColumnCount; l++) { - s += At(i, l) * other.At(j, l); + s += At(i, l)*other.At(j, l); } - result.At(i, j, s); } } @@ -310,9 +307,8 @@ namespace MathNet.Numerics.LinearAlgebra.Complex var s = Complex.Zero; for (var l = 0; l < RowCount; l++) { - s += At(l, i) * other.At(l, j); + s += At(l, i)*other.At(l, j); } - result.At(i, j, s); } } @@ -328,11 +324,10 @@ namespace MathNet.Numerics.LinearAlgebra.Complex for (var i = 0; i < ColumnCount; i++) { var s = Complex.Zero; - for (var j = 0; j != RowCount; j++) + for (var j = 0; j < RowCount; j++) { - s += At(j, i) * rightSide[j]; + s += At(j, i)*rightSide[j]; } - result[i] = s; } } @@ -345,7 +340,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex { for (var i = 0; i < RowCount; i++) { - for (var j = 0; j != ColumnCount; j++) + for (var j = 0; j < ColumnCount; j++) { result.At(i, j, -At(i, j)); } @@ -360,7 +355,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex { for (var i = 0; i < RowCount; i++) { - for (var j = 0; j != ColumnCount; j++) + for (var j = 0; j < ColumnCount; j++) { result.At(i, j, At(i, j).Conjugate()); } @@ -378,7 +373,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex { for (var i = 0; i < RowCount; i++) { - result.At(i, j, At(i, j) * other.At(i, j)); + result.At(i, j, At(i, j)*other.At(i, j)); } } } @@ -394,7 +389,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex { for (var i = 0; i < RowCount; i++) { - result.At(i, j, At(i, j) / divisor.At(i, j)); + result.At(i, j, At(i, j)/divisor.At(i, j)); } } } diff --git a/src/Numerics/LinearAlgebra/Complex32/DenseMatrix.cs b/src/Numerics/LinearAlgebra/Complex32/DenseMatrix.cs index df9a2192..37721c1a 100644 --- a/src/Numerics/LinearAlgebra/Complex32/DenseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex32/DenseMatrix.cs @@ -402,6 +402,27 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 get { return _values; } } + /// Calculates the induced L1 norm of this matrix. + /// The maximum absolute column sum of the matrix. + public override double L1Norm() + { + return Control.LinearAlgebraProvider.MatrixNorm(Norm.OneNorm, _rowCount, _columnCount, _values); + } + + /// Calculates the induced infinity norm of this matrix. + /// The maximum absolute row sum of the matrix. + public override double InfinityNorm() + { + return Control.LinearAlgebraProvider.MatrixNorm(Norm.InfinityNorm, _rowCount, _columnCount, _values); + } + + /// Calculates the entry-wise Frobenius norm of this matrix. + /// The square root of the sum of the squared values. + public override double FrobeniusNorm() + { + return Control.LinearAlgebraProvider.MatrixNorm(Norm.FrobeniusNorm, _rowCount, _columnCount, _values); + } + /// /// Returns the transpose of this matrix. /// @@ -417,29 +438,139 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 ret._values[(i * _columnCount) + j] = _values[index + i]; } } + return ret; + } + /// + /// Returns the conjugate transpose of this matrix. + /// + /// The conjugate transpose of this matrix. + public override Matrix ConjugateTranspose() + { + var ret = new DenseMatrix(_columnCount, _rowCount); + for (var j = 0; j < _columnCount; j++) + { + var index = j * _rowCount; + for (var i = 0; i < _rowCount; i++) + { + ret._values[(i * _columnCount) + j] = _values[index + i].Conjugate(); + } + } return ret; } - /// Calculates the induced L1 norm of this matrix. - /// The maximum absolute column sum of the matrix. - public override double L1Norm() + /// + /// Add a scalar to each element of the matrix and stores the result in the result vector. + /// + /// The scalar to add. + /// The matrix to store the result of the addition. + protected override void DoAdd(Complex32 scalar, Matrix result) { - return Control.LinearAlgebraProvider.MatrixNorm(Norm.OneNorm, _rowCount, _columnCount, _values); + var denseResult = result as DenseMatrix; + if (denseResult == null) + { + base.DoAdd(scalar, result); + return; + } + + CommonParallel.For(0, _values.Length, 4096, (a, b) => + { + var v = denseResult._values; + for (int i = a; i < b; i++) + { + v[i] = _values[i] + scalar; + } + }); } - /// Calculates the induced infinity norm of this matrix. - /// The maximum absolute row sum of the matrix. - public override double InfinityNorm() + /// + /// Adds another matrix to this matrix. + /// + /// The matrix to add to this matrix. + /// The matrix to store the result of add + /// If the other matrix is . + /// If the two matrices don't have the same dimensions. + protected override void DoAdd(Matrix other, Matrix result) { - return Control.LinearAlgebraProvider.MatrixNorm(Norm.InfinityNorm, _rowCount, _columnCount, _values); + // dense + dense = dense + var denseOther = other.Storage as DenseColumnMajorMatrixStorage; + var denseResult = result.Storage as DenseColumnMajorMatrixStorage; + if (denseOther != null && denseResult != null) + { + Control.LinearAlgebraProvider.AddArrays(_values, denseOther.Data, denseResult.Data); + return; + } + + // dense + diagonal = matrix + var diagonalOther = other.Storage as DiagonalMatrixStorage; + if (diagonalOther != null) + { + CopyTo(result); + var diagonal = diagonalOther.Data; + for (int i = 0; i < diagonal.Length; i++) + { + result.At(i, i, result.At(i, i) + diagonal[i]); + } + return; + } + + base.DoAdd(other, result); } - /// Calculates the entry-wise Frobenius norm of this matrix. - /// The square root of the sum of the squared values. - public override double FrobeniusNorm() + /// + /// Subtracts a scalar from each element of the matrix and stores the result in the result vector. + /// + /// The scalar to subtract. + /// The matrix to store the result of the subtraction. + protected override void DoSubtract(Complex32 scalar, Matrix result) { - return Control.LinearAlgebraProvider.MatrixNorm(Norm.FrobeniusNorm, _rowCount, _columnCount, _values); + var denseResult = result as DenseMatrix; + if (denseResult == null) + { + base.DoSubtract(scalar, result); + return; + } + + CommonParallel.For(0, _values.Length, 4096, (a, b) => + { + var v = denseResult._values; + for (int i = a; i < b; i++) + { + v[i] = _values[i] - scalar; + } + }); + } + + /// + /// Subtracts another matrix from this matrix. + /// + /// The matrix to subtract. + /// The matrix to store the result of the subtraction. + protected override void DoSubtract(Matrix other, Matrix result) + { + // dense + dense = dense + var denseOther = other.Storage as DenseColumnMajorMatrixStorage; + var denseResult = result.Storage as DenseColumnMajorMatrixStorage; + if (denseOther != null && denseResult != null) + { + Control.LinearAlgebraProvider.SubtractArrays(_values, denseOther.Data, denseResult.Data); + return; + } + + // dense + diagonal = matrix + var diagonalOther = other.Storage as DiagonalMatrixStorage; + if (diagonalOther != null) + { + CopyTo(result); + var diagonal = diagonalOther.Data; + for (int i = 0; i < diagonal.Length; i++) + { + result.At(i, i, result.At(i, i) - diagonal[i]); + } + return; + } + + base.DoSubtract(other, result); } /// @@ -500,12 +631,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 { var denseOther = other as DenseMatrix; var denseResult = result as DenseMatrix; - - if (denseOther == null || denseResult == null) - { - base.DoMultiply(other, result); - } - else + if (denseOther != null && denseResult != null) { Control.LinearAlgebraProvider.MatrixMultiplyWithUpdate( Providers.LinearAlgebra.Transpose.DontTranspose, @@ -519,7 +645,31 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 denseOther._columnCount, 0.0f, denseResult._values); + return; } + + var diagonalOther = other.Storage as DiagonalMatrixStorage; + if (diagonalOther != null) + { + var diagonal = diagonalOther.Data; + var d = Math.Min(ColumnCount, other.ColumnCount); + if (d < other.ColumnCount) + { + result.ClearSubMatrix(0, RowCount, ColumnCount, other.ColumnCount - ColumnCount); + } + int index = 0; + for (int j = 0; j < d; j++) + { + for (int i = 0; i < RowCount; i++) + { + result.At(i, j, _values[index]*diagonal[j]); + index++; + } + } + return; + } + + base.DoMultiply(other, result); } /// @@ -691,113 +841,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 } } - /// - /// Add a scalar to each element of the matrix and stores the result in the result vector. - /// - /// The scalar to add. - /// The matrix to store the result of the addition. - protected override void DoAdd(Complex32 scalar, Matrix result) - { - var denseResult = result as DenseMatrix; - if (denseResult == null) - { - base.DoAdd(scalar, result); - return; - } - - CommonParallel.For(0, _values.Length, 4096, (a, b) => - { - var v = denseResult._values; - for (int i = a; i < b; i++) - { - v[i] = _values[i] + scalar; - } - }); - } - - /// - /// Adds another matrix to this matrix. - /// - /// The matrix to add to this matrix. - /// The matrix to store the result of add - /// If the other matrix is . - /// If the two matrices don't have the same dimensions. - protected override void DoAdd(Matrix other, Matrix result) - { - var denseOther = other as DenseMatrix; - var denseResult = result as DenseMatrix; - if (denseOther == null || denseResult == null) - { - base.DoAdd(other, result); - } - else - { - Control.LinearAlgebraProvider.AddArrays(_values, denseOther._values, denseResult._values); - } - } - - /// - /// Subtracts a scalar from each element of the matrix and stores the result in the result vector. - /// - /// The scalar to subtract. - /// The matrix to store the result of the subtraction. - protected override void DoSubtract(Complex32 scalar, Matrix result) - { - var denseResult = result as DenseMatrix; - if (denseResult == null) - { - base.DoSubtract(scalar, result); - return; - } - - CommonParallel.For(0, _values.Length, 4096, (a, b) => - { - var v = denseResult._values; - for (int i = a; i < b; i++) - { - v[i] = _values[i] - scalar; - } - }); - } - - /// - /// Subtracts another matrix from this matrix. - /// - /// The matrix to subtract. - /// The matrix to store the result of the subtraction. - protected override void DoSubtract(Matrix other, Matrix result) - { - var denseOther = other as DenseMatrix; - var denseResult = result as DenseMatrix; - if (denseOther == null || denseResult == null) - { - base.DoSubtract(other, result); - } - else - { - Control.LinearAlgebraProvider.SubtractArrays(_values, denseOther._values, denseResult._values); - } - } - - /// - /// Returns the conjugate transpose of this matrix. - /// - /// The conjugate transpose of this matrix. - public override Matrix ConjugateTranspose() - { - var ret = new DenseMatrix(_columnCount, _rowCount); - for (var j = 0; j < _columnCount; j++) - { - var index = j * _rowCount; - for (var i = 0; i < _rowCount; i++) - { - ret._values[(i * _columnCount) + j] = _values[index + i].Conjugate(); - } - } - - return ret; - } - /// /// Computes the trace of this matrix. /// diff --git a/src/Numerics/LinearAlgebra/Complex32/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Complex32/DiagonalMatrix.cs index f1f04d3b..2213a10e 100644 --- a/src/Numerics/LinearAlgebra/Complex32/DiagonalMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex32/DiagonalMatrix.cs @@ -583,9 +583,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// i == j (i is the row index, and j is the column index). public override Vector Diagonal() { - // TODO: Should we return reference to array? In current implementation we return copy of array, so changes in DenseVector will - // not influence onto diagonal elements - return new DenseVector((Complex32[])_data.Clone()); + return new DenseVector(_data).Clone(); } /// @@ -611,31 +609,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 Multiply(otherDiagonal.Transpose(), result); } - /// - /// Multiplies this matrix with transpose of another matrix and returns the result. - /// - /// The matrix to multiply with. - /// If this.Columns != other.Rows. - /// If the other matrix is . - /// The result of multiplication. - public override Matrix TransposeAndMultiply(Matrix other) - { - var otherDiagonal = other as DiagonalMatrix; - if (otherDiagonal == null) - { - return base.TransposeAndMultiply(other); - } - - if (ColumnCount != otherDiagonal.ColumnCount) - { - throw DimensionsDontMatch(this, otherDiagonal); - } - - var result = other.CreateMatrix(RowCount, other.RowCount); - TransposeAndMultiply(other, result); - return result; - } - /// /// Returns the transpose of this matrix. /// diff --git a/src/Numerics/LinearAlgebra/Complex32/Matrix.cs b/src/Numerics/LinearAlgebra/Complex32/Matrix.cs index ad6a3346..dae6f558 100644 --- a/src/Numerics/LinearAlgebra/Complex32/Matrix.cs +++ b/src/Numerics/LinearAlgebra/Complex32/Matrix.cs @@ -28,11 +28,11 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using System; using MathNet.Numerics.LinearAlgebra.Complex32.Factorization; using MathNet.Numerics.LinearAlgebra.Factorization; using MathNet.Numerics.LinearAlgebra.Storage; using MathNet.Numerics.Properties; -using System; namespace MathNet.Numerics.LinearAlgebra.Complex32 { @@ -91,7 +91,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 public override double FrobeniusNorm() { var transpose = ConjugateTranspose(); - var aat = this * transpose; + var aat = this*transpose; var norm = 0d; for (var i = 0; i < RowCount; i++) { @@ -196,29 +196,28 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 { for (var j = 0; j < ColumnCount; j++) { - result.At(i, j, At(i, j) * scalar); + result.At(i, j, At(i, j)*scalar); } } } - /// + /// /// Multiplies this matrix with a vector and places the results into the result vector. /// /// The vector to multiply with. /// The result of the multiplication. protected override void DoMultiply(Vector rightSide, Vector result) - { + { for (var i = 0; i < RowCount; i++) { var s = Complex32.Zero; - for (var j = 0; j != ColumnCount; j++) + for (var j = 0; j < ColumnCount; j++) { - s += At(i, j) * rightSide[j]; + s += At(i, j)*rightSide[j]; } - result[i] = s; } - } + } /// /// Divides each element of the matrix by a scalar and places results into the result matrix. @@ -227,7 +226,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// The matrix to store the result of the division. protected override void DoDivide(Complex32 divisor, Matrix result) { - DoMultiply(1.0f / divisor, result); + DoMultiply(1.0f/divisor, result); } /// @@ -241,7 +240,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 { for (var j = 0; j < ColumnCount; j++) { - result.At(i, j, dividend / At(i, j)); + result.At(i, j, dividend/At(i, j)); } } } @@ -260,9 +259,8 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 var s = Complex32.Zero; for (var l = 0; l < ColumnCount; l++) { - s += At(j, l) * other.At(l, i); + s += At(j, l)*other.At(l, i); } - result.At(j, i, s); } } @@ -282,9 +280,8 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 var s = Complex32.Zero; for (var l = 0; l < ColumnCount; l++) { - s += At(i, l) * other.At(j, l); + s += At(i, l)*other.At(j, l); } - result.At(i, j, s); } } @@ -304,9 +301,8 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 var s = Complex32.Zero; for (var l = 0; l < RowCount; l++) { - s += At(l, i) * other.At(l, j); + s += At(l, i)*other.At(l, j); } - result.At(i, j, s); } } @@ -322,11 +318,10 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 for (var i = 0; i < ColumnCount; i++) { var s = Complex32.Zero; - for (var j = 0; j != RowCount; j++) + for (var j = 0; j < RowCount; j++) { - s += At(j, i) * rightSide[j]; + s += At(j, i)*rightSide[j]; } - result[i] = s; } } @@ -339,7 +334,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 { for (var i = 0; i < RowCount; i++) { - for (var j = 0; j != ColumnCount; j++) + for (var j = 0; j < ColumnCount; j++) { result.At(i, j, -At(i, j)); } @@ -354,7 +349,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 { for (var i = 0; i < RowCount; i++) { - for (var j = 0; j != ColumnCount; j++) + for (var j = 0; j < ColumnCount; j++) { result.At(i, j, At(i, j).Conjugate()); } @@ -372,7 +367,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 { for (var i = 0; i < RowCount; i++) { - result.At(i, j, At(i, j) * other.At(i, j)); + result.At(i, j, At(i, j)*other.At(i, j)); } } } @@ -388,7 +383,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 { for (var i = 0; i < RowCount; i++) { - result.At(i, j, At(i, j) / divisor.At(i, j)); + result.At(i, j, At(i, j)/divisor.At(i, j)); } } } diff --git a/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs b/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs index 30198a63..f9370001 100644 --- a/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs @@ -399,25 +399,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double get { return _values; } } - /// - /// Returns the transpose of this matrix. - /// - /// The transpose of this matrix. - public override Matrix Transpose() - { - var ret = new DenseMatrix(_columnCount, _rowCount); - for (var j = 0; j < _columnCount; j++) - { - var index = j * _rowCount; - for (var i = 0; i < _rowCount; i++) - { - ret._values[(i * _columnCount) + j] = _values[index + i]; - } - } - - return ret; - } - /// Calculates the induced L1 norm of this matrix. /// The maximum absolute column sum of the matrix. public override double L1Norm() @@ -439,6 +420,25 @@ namespace MathNet.Numerics.LinearAlgebra.Double return Control.LinearAlgebraProvider.MatrixNorm(Norm.FrobeniusNorm, _rowCount, _columnCount, _values); } + /// + /// Returns the transpose of this matrix. + /// + /// The transpose of this matrix. + public override Matrix Transpose() + { + var ret = new DenseMatrix(_columnCount, _rowCount); + for (var j = 0; j < _columnCount; j++) + { + var index = j * _rowCount; + for (var i = 0; i < _rowCount; i++) + { + ret._values[(i * _columnCount) + j] = _values[index + i]; + } + } + + return ret; + } + /// /// Add a scalar to each element of the matrix and stores the result in the result vector. /// @@ -454,13 +454,13 @@ namespace MathNet.Numerics.LinearAlgebra.Double } CommonParallel.For(0, _values.Length, 4096, (a, b) => + { + var v = denseResult._values; + for (int i = a; i < b; i++) { - var v = denseResult._values; - for (int i = a; i < b; i++) - { - v[i] = _values[i] + scalar; - } - }); + v[i] = _values[i] + scalar; + } + }); } /// @@ -472,16 +472,29 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// If the two matrices don't have the same dimensions. protected override void DoAdd(Matrix other, Matrix result) { - var denseOther = other as DenseMatrix; - var denseResult = result as DenseMatrix; - if (denseOther == null || denseResult == null) + // dense + dense = dense + var denseOther = other.Storage as DenseColumnMajorMatrixStorage; + var denseResult = result.Storage as DenseColumnMajorMatrixStorage; + if (denseOther != null && denseResult != null) { - base.DoAdd(other, result); + Control.LinearAlgebraProvider.AddArrays(_values, denseOther.Data, denseResult.Data); + return; } - else + + // dense + diagonal = matrix + var diagonalOther = other.Storage as DiagonalMatrixStorage; + if (diagonalOther != null) { - Control.LinearAlgebraProvider.AddArrays(_values, denseOther._values, denseResult._values); + CopyTo(result); + var diagonal = diagonalOther.Data; + for (int i = 0; i < diagonal.Length; i++) + { + result.At(i, i, result.At(i, i) + diagonal[i]); + } + return; } + + base.DoAdd(other, result); } /// @@ -499,13 +512,13 @@ namespace MathNet.Numerics.LinearAlgebra.Double } CommonParallel.For(0, _values.Length, 4096, (a, b) => + { + var v = denseResult._values; + for (int i = a; i < b; i++) { - var v = denseResult._values; - for (int i = a; i < b; i++) - { - v[i] = _values[i] - scalar; - } - }); + v[i] = _values[i] - scalar; + } + }); } /// @@ -515,16 +528,29 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// The matrix to store the result of the subtraction. protected override void DoSubtract(Matrix other, Matrix result) { - var denseOther = other as DenseMatrix; - var denseResult = result as DenseMatrix; - if (denseOther == null || denseResult == null) + // dense + dense = dense + var denseOther = other.Storage as DenseColumnMajorMatrixStorage; + var denseResult = result.Storage as DenseColumnMajorMatrixStorage; + if (denseOther != null && denseResult != null) { - base.DoSubtract(other, result); + Control.LinearAlgebraProvider.SubtractArrays(_values, denseOther.Data, denseResult.Data); + return; } - else + + // dense + diagonal = matrix + var diagonalOther = other.Storage as DiagonalMatrixStorage; + if (diagonalOther != null) { - Control.LinearAlgebraProvider.SubtractArrays(_values, denseOther._values, denseResult._values); + CopyTo(result); + var diagonal = diagonalOther.Data; + for (int i = 0; i < diagonal.Length; i++) + { + result.At(i, i, result.At(i, i) - diagonal[i]); + } + return; } + + base.DoSubtract(other, result); } /// @@ -585,12 +611,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double { var denseOther = other as DenseMatrix; var denseResult = result as DenseMatrix; - - if (denseOther == null || denseResult == null) - { - base.DoMultiply(other, result); - } - else + if (denseOther != null && denseResult != null) { Control.LinearAlgebraProvider.MatrixMultiplyWithUpdate( Providers.LinearAlgebra.Transpose.DontTranspose, @@ -604,7 +625,31 @@ namespace MathNet.Numerics.LinearAlgebra.Double denseOther._columnCount, 0.0, denseResult._values); + return; } + + var diagonalOther = other.Storage as DiagonalMatrixStorage; + if (diagonalOther != null) + { + var diagonal = diagonalOther.Data; + var d = Math.Min(ColumnCount, other.ColumnCount); + if (d < other.ColumnCount) + { + result.ClearSubMatrix(0, RowCount, ColumnCount, other.ColumnCount - ColumnCount); + } + int index = 0; + for (int j = 0; j < d; j++) + { + for (int i = 0; i < RowCount; i++) + { + result.At(i, j, _values[index]*diagonal[j]); + index++; + } + } + return; + } + + base.DoMultiply(other, result); } /// @@ -614,9 +659,8 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// The result of the multiplication. protected override void DoTransposeAndMultiply(Matrix other, Matrix result) { - var denseOther = other as DenseMatrix; + var denseOther = other as DenseMatrix; var denseResult = result as DenseMatrix; - if (denseOther == null || denseResult == null) { base.DoTransposeAndMultiply(other, result); diff --git a/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs index e489a675..872d85b3 100644 --- a/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs +++ b/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs @@ -577,9 +577,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// i == j (i is the row index, and j is the column index). public override Vector Diagonal() { - // TODO: Should we return reference to array? In current implementation we return copy of array, so changes in DenseVector will - // not influence onto diagonal elements - return new DenseVector((double[])_data.Clone()); + return new DenseVector(_data).Clone(); } /// @@ -605,31 +603,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double Multiply(otherDiagonal.Transpose(), result); } - /// - /// Multiplies this matrix with transpose of another matrix and returns the result. - /// - /// The matrix to multiply with. - /// If this.Columns != other.Rows. - /// If the other matrix is . - /// The result of multiplication. - public override Matrix TransposeAndMultiply(Matrix other) - { - var otherDiagonal = other as DiagonalMatrix; - if (otherDiagonal == null) - { - return base.TransposeAndMultiply(other); - } - - if (ColumnCount != otherDiagonal.ColumnCount) - { - throw DimensionsDontMatch(this, otherDiagonal); - } - - var result = other.CreateMatrix(RowCount, other.RowCount); - TransposeAndMultiply(other, result); - return result; - } - /// /// Returns the transpose of this matrix. /// @@ -692,7 +665,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double { if (RowCount != ColumnCount) { - throw new ArgumentException(Resources.ArgumentMatrixSquare); + throw new ArgumentException(Resources.ArgumentMatrixSquare); } var inverse = (DiagonalMatrix)Clone(); diff --git a/src/Numerics/LinearAlgebra/Double/Matrix.cs b/src/Numerics/LinearAlgebra/Double/Matrix.cs index ec4eaa25..b70d67f7 100644 --- a/src/Numerics/LinearAlgebra/Double/Matrix.cs +++ b/src/Numerics/LinearAlgebra/Double/Matrix.cs @@ -41,7 +41,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// [Serializable] public abstract class Matrix : Matrix - { + { /// /// Initializes a new instance of the Matrix class. /// @@ -89,7 +89,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double public override double FrobeniusNorm() { var transpose = Transpose(); - var aat = this * transpose; + var aat = this*transpose; var norm = 0d; for (var i = 0; i < RowCount; i++) { @@ -156,7 +156,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double } } } - + /// /// Subtracts another matrix from this matrix. /// @@ -186,7 +186,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double { for (var j = 0; j < ColumnCount; j++) { - result.At(i, j, At(i, j) * scalar); + result.At(i, j, At(i, j)*scalar); } } } @@ -197,18 +197,17 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// The vector to multiply with. /// The result of the multiplication. protected override void DoMultiply(Vector rightSide, Vector result) - { - for (var i = 0; i < RowCount; i++) - { - var s = 0.0; - for (var j = 0; j != ColumnCount; j++) - { - s += At(i, j) * rightSide[j]; - } - - result[i] = s; - } - } + { + for (var i = 0; i < RowCount; i++) + { + var s = 0.0; + for (var j = 0; j < ColumnCount; j++) + { + s += At(i, j)*rightSide[j]; + } + result[i] = s; + } + } /// /// Divides each element of the matrix by a scalar and places results into the result matrix. @@ -217,7 +216,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// The matrix to store the result of the division. protected override void DoDivide(double divisor, Matrix result) { - DoMultiply(1.0 / divisor, result); + DoMultiply(1.0/divisor, result); } /// @@ -231,7 +230,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double { for (var j = 0; j < ColumnCount; j++) { - result.At(i, j, dividend / At(i, j)); + result.At(i, j, dividend/At(i, j)); } } } @@ -245,14 +244,13 @@ namespace MathNet.Numerics.LinearAlgebra.Double { for (var j = 0; j < RowCount; j++) { - for (var i = 0; i != other.ColumnCount; i++) + for (var i = 0; i < other.ColumnCount; i++) { var s = 0.0; for (var l = 0; l < ColumnCount; l++) { - s += At(j, l) * other.At(l, i); + s += At(j, l)*other.At(l, i); } - result.At(j, i, s); } } @@ -272,9 +270,8 @@ namespace MathNet.Numerics.LinearAlgebra.Double var s = 0.0; for (var l = 0; l < ColumnCount; l++) { - s += At(i, l) * other.At(j, l); + s += At(i, l)*other.At(j, l); } - result.At(i, j, s); } } @@ -294,9 +291,8 @@ namespace MathNet.Numerics.LinearAlgebra.Double var s = 0.0; for (var l = 0; l < RowCount; l++) { - s += At(l, i) * other.At(l, j); + s += At(l, i)*other.At(l, j); } - result.At(i, j, s); } } @@ -312,11 +308,10 @@ namespace MathNet.Numerics.LinearAlgebra.Double for (var i = 0; i < ColumnCount; i++) { var s = 0.0; - for (var j = 0; j != RowCount; j++) + for (var j = 0; j < RowCount; j++) { - s += At(j, i) * rightSide[j]; + s += At(j, i)*rightSide[j]; } - result[i] = s; } } @@ -329,7 +324,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double { for (var i = 0; i < RowCount; i++) { - for (var j = 0; j != ColumnCount; j++) + for (var j = 0; j < ColumnCount; j++) { result.At(i, j, -At(i, j)); } diff --git a/src/Numerics/LinearAlgebra/Matrix.Arithmetic.cs b/src/Numerics/LinearAlgebra/Matrix.Arithmetic.cs index 25163d6a..bb5fcebf 100644 --- a/src/Numerics/LinearAlgebra/Matrix.Arithmetic.cs +++ b/src/Numerics/LinearAlgebra/Matrix.Arithmetic.cs @@ -702,7 +702,7 @@ namespace MathNet.Numerics.LinearAlgebra /// The matrix to multiply with. /// If this.Columns != other.ColumnCount. /// The result of the multiplication. - public virtual Matrix TransposeAndMultiply(Matrix other) + public Matrix TransposeAndMultiply(Matrix other) { if (ColumnCount != other.ColumnCount) { diff --git a/src/Numerics/LinearAlgebra/Single/DenseMatrix.cs b/src/Numerics/LinearAlgebra/Single/DenseMatrix.cs index 8c3417ee..b112612d 100644 --- a/src/Numerics/LinearAlgebra/Single/DenseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Single/DenseMatrix.cs @@ -399,25 +399,6 @@ namespace MathNet.Numerics.LinearAlgebra.Single get { return _values; } } - /// - /// Returns the transpose of this matrix. - /// - /// The transpose of this matrix. - public override Matrix Transpose() - { - var ret = new DenseMatrix(_columnCount, _rowCount); - for (var j = 0; j < _columnCount; j++) - { - var index = j * _rowCount; - for (var i = 0; i < _rowCount; i++) - { - ret._values[(i * _columnCount) + j] = _values[index + i]; - } - } - - return ret; - } - /// Calculates the induced L1 norm of this matrix. /// The maximum absolute column sum of the matrix. public override double L1Norm() @@ -439,6 +420,25 @@ namespace MathNet.Numerics.LinearAlgebra.Single return Control.LinearAlgebraProvider.MatrixNorm(Norm.FrobeniusNorm, _rowCount, _columnCount, _values); } + /// + /// Returns the transpose of this matrix. + /// + /// The transpose of this matrix. + public override Matrix Transpose() + { + var ret = new DenseMatrix(_columnCount, _rowCount); + for (var j = 0; j < _columnCount; j++) + { + var index = j * _rowCount; + for (var i = 0; i < _rowCount; i++) + { + ret._values[(i * _columnCount) + j] = _values[index + i]; + } + } + + return ret; + } + /// /// Add a scalar to each element of the matrix and stores the result in the result vector. /// @@ -454,13 +454,13 @@ namespace MathNet.Numerics.LinearAlgebra.Single } CommonParallel.For(0, _values.Length, 4096, (a, b) => + { + var v = denseResult._values; + for (int i = a; i < b; i++) { - var v = denseResult._values; - for (int i = a; i < b; i++) - { - v[i] = _values[i] + scalar; - } - }); + v[i] = _values[i] + scalar; + } + }); } /// @@ -472,16 +472,29 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// If the two matrices don't have the same dimensions. protected override void DoAdd(Matrix other, Matrix result) { - var denseOther = other as DenseMatrix; - var denseResult = result as DenseMatrix; - if (denseOther == null || denseResult == null) + // dense + dense = dense + var denseOther = other.Storage as DenseColumnMajorMatrixStorage; + var denseResult = result.Storage as DenseColumnMajorMatrixStorage; + if (denseOther != null && denseResult != null) { - base.DoAdd(other, result); + Control.LinearAlgebraProvider.AddArrays(_values, denseOther.Data, denseResult.Data); + return; } - else + + // dense + diagonal = matrix + var diagonalOther = other.Storage as DiagonalMatrixStorage; + if (diagonalOther != null) { - Control.LinearAlgebraProvider.AddArrays(_values, denseOther._values, denseResult._values); + CopyTo(result); + var diagonal = diagonalOther.Data; + for (int i = 0; i < diagonal.Length; i++) + { + result.At(i, i, result.At(i, i) + diagonal[i]); + } + return; } + + base.DoAdd(other, result); } /// @@ -499,13 +512,13 @@ namespace MathNet.Numerics.LinearAlgebra.Single } CommonParallel.For(0, _values.Length, 4096, (a, b) => + { + var v = denseResult._values; + for (int i = a; i < b; i++) { - var v = denseResult._values; - for (int i = a; i < b; i++) - { - v[i] = _values[i] - scalar; - } - }); + v[i] = _values[i] - scalar; + } + }); } /// @@ -515,16 +528,29 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// The matrix to store the result of the subtraction. protected override void DoSubtract(Matrix other, Matrix result) { - var denseOther = other as DenseMatrix; - var denseResult = result as DenseMatrix; - if (denseOther == null || denseResult == null) + // dense + dense = dense + var denseOther = other.Storage as DenseColumnMajorMatrixStorage; + var denseResult = result.Storage as DenseColumnMajorMatrixStorage; + if (denseOther != null && denseResult != null) { - base.DoSubtract(other, result); + Control.LinearAlgebraProvider.SubtractArrays(_values, denseOther.Data, denseResult.Data); + return; } - else + + // dense + diagonal = matrix + var diagonalOther = other.Storage as DiagonalMatrixStorage; + if (diagonalOther != null) { - Control.LinearAlgebraProvider.SubtractArrays(_values, denseOther._values, denseResult._values); + CopyTo(result); + var diagonal = diagonalOther.Data; + for (int i = 0; i < diagonal.Length; i++) + { + result.At(i, i, result.At(i, i) - diagonal[i]); + } + return; } + + base.DoSubtract(other, result); } /// @@ -585,12 +611,7 @@ namespace MathNet.Numerics.LinearAlgebra.Single { var denseOther = other as DenseMatrix; var denseResult = result as DenseMatrix; - - if (denseOther == null || denseResult == null) - { - base.DoMultiply(other, result); - } - else + if (denseOther != null && denseResult != null) { Control.LinearAlgebraProvider.MatrixMultiplyWithUpdate( Providers.LinearAlgebra.Transpose.DontTranspose, @@ -604,7 +625,31 @@ namespace MathNet.Numerics.LinearAlgebra.Single denseOther._columnCount, 0.0f, denseResult._values); + return; } + + var diagonalOther = other.Storage as DiagonalMatrixStorage; + if (diagonalOther != null) + { + var diagonal = diagonalOther.Data; + var d = Math.Min(ColumnCount, other.ColumnCount); + if (d < other.ColumnCount) + { + result.ClearSubMatrix(0, RowCount, ColumnCount, other.ColumnCount - ColumnCount); + } + int index = 0; + for (int j = 0; j < d; j++) + { + for (int i = 0; i < RowCount; i++) + { + result.At(i, j, _values[index]*diagonal[j]); + index++; + } + } + return; + } + + base.DoMultiply(other, result); } /// diff --git a/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs index e388e2b2..42d5b944 100644 --- a/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs +++ b/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs @@ -577,9 +577,7 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// i == j (i is the row index, and j is the column index). public override Vector Diagonal() { - // TODO: Should we return reference to array? In current implementation we return copy of array, so changes in DenseVector will - // not influence onto diagonal elements - return new DenseVector((float[])_data.Clone()); + return new DenseVector(_data).Clone(); } /// @@ -605,31 +603,6 @@ namespace MathNet.Numerics.LinearAlgebra.Single Multiply(otherDiagonal.Transpose(), result); } - /// - /// Multiplies this matrix with transpose of another matrix and returns the result. - /// - /// The matrix to multiply with. - /// If this.Columns != other.Rows. - /// If the other matrix is . - /// The result of multiplication. - public override Matrix TransposeAndMultiply(Matrix other) - { - var otherDiagonal = other as DiagonalMatrix; - if (otherDiagonal == null) - { - return base.TransposeAndMultiply(other); - } - - if (ColumnCount != otherDiagonal.ColumnCount) - { - throw DimensionsDontMatch(this, otherDiagonal); - } - - var result = other.CreateMatrix(RowCount, other.RowCount); - TransposeAndMultiply(other, result); - return result; - } - /// /// Returns the transpose of this matrix. /// diff --git a/src/Numerics/LinearAlgebra/Single/Matrix.cs b/src/Numerics/LinearAlgebra/Single/Matrix.cs index 761734f0..b8737c0e 100644 --- a/src/Numerics/LinearAlgebra/Single/Matrix.cs +++ b/src/Numerics/LinearAlgebra/Single/Matrix.cs @@ -41,7 +41,7 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// [Serializable] public abstract class Matrix : Matrix - { + { /// /// Initializes a new instance of the Matrix class. /// @@ -89,7 +89,7 @@ namespace MathNet.Numerics.LinearAlgebra.Single public override double FrobeniusNorm() { var transpose = Transpose(); - var aat = this * transpose; + var aat = this*transpose; var norm = 0d; for (var i = 0; i < RowCount; i++) { @@ -186,29 +186,28 @@ namespace MathNet.Numerics.LinearAlgebra.Single { for (var j = 0; j < ColumnCount; j++) { - result.At(i, j, At(i, j) * scalar); + result.At(i, j, At(i, j)*scalar); } } } - /// + /// /// Multiplies this matrix with a vector and places the results into the result vector. /// /// The vector to multiply with. /// The result of the multiplication. protected override void DoMultiply(Vector rightSide, Vector result) - { + { for (var i = 0; i < RowCount; i++) { var s = 0.0f; - for (var j = 0; j != ColumnCount; j++) + for (var j = 0; j < ColumnCount; j++) { - s += At(i, j) * rightSide[j]; + s += At(i, j)*rightSide[j]; } - result[i] = s; } - } + } /// /// Multiplies this matrix with another matrix and places the results into the result matrix. @@ -219,14 +218,13 @@ namespace MathNet.Numerics.LinearAlgebra.Single { for (var j = 0; j < RowCount; j++) { - for (var i = 0; i != other.ColumnCount; i++) + for (var i = 0; i < other.ColumnCount; i++) { var s = 0.0f; for (var l = 0; l < ColumnCount; l++) { - s += At(j, l) * other.At(l, i); + s += At(j, l)*other.At(l, i); } - result.At(j, i, s); } } @@ -239,7 +237,7 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// The matrix to store the result of the division. protected override void DoDivide(float divisor, Matrix result) { - DoMultiply(1.0f / divisor, result); + DoMultiply(1.0f/divisor, result); } /// @@ -253,7 +251,7 @@ namespace MathNet.Numerics.LinearAlgebra.Single { for (var j = 0; j < ColumnCount; j++) { - result.At(i, j, dividend / At(i, j)); + result.At(i, j, dividend/At(i, j)); } } } @@ -272,9 +270,8 @@ namespace MathNet.Numerics.LinearAlgebra.Single var s = 0.0f; for (var l = 0; l < ColumnCount; l++) { - s += At(i, l) * other.At(j, l); + s += At(i, l)*other.At(j, l); } - result.At(i, j, s); } } @@ -294,9 +291,8 @@ namespace MathNet.Numerics.LinearAlgebra.Single var s = 0.0f; for (var l = 0; l < RowCount; l++) { - s += At(l, i) * other.At(l, j); + s += At(l, i)*other.At(l, j); } - result.At(i, j, s); } } @@ -312,11 +308,10 @@ namespace MathNet.Numerics.LinearAlgebra.Single for (var i = 0; i < ColumnCount; i++) { var s = 0.0f; - for (var j = 0; j != RowCount; j++) + for (var j = 0; j < RowCount; j++) { - s += At(j, i) * rightSide[j]; + s += At(j, i)*rightSide[j]; } - result[i] = s; } } @@ -332,7 +327,7 @@ namespace MathNet.Numerics.LinearAlgebra.Single { for (var column = 0; column < ColumnCount; column++) { - result.At(row, column, At(row, column) % divisor); + result.At(row, column, At(row, column)%divisor); } } } @@ -348,7 +343,7 @@ namespace MathNet.Numerics.LinearAlgebra.Single { for (var column = 0; column < ColumnCount; column++) { - result.At(row, column, dividend % At(row, column)); + result.At(row, column, dividend%At(row, column)); } } } @@ -361,7 +356,7 @@ namespace MathNet.Numerics.LinearAlgebra.Single { for (var i = 0; i < RowCount; i++) { - for (var j = 0; j != ColumnCount; j++) + for (var j = 0; j < ColumnCount; j++) { result.At(i, j, -At(i, j)); } diff --git a/src/UnitTests/LinearAlgebraTests/Complex/DiagonalMatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Complex/DiagonalMatrixTests.cs index fc265c89..c54dc08c 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex/DiagonalMatrixTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex/DiagonalMatrixTests.cs @@ -30,8 +30,10 @@ using System; using System.Collections.Generic; +using MathNet.Numerics.Distributions; using MathNet.Numerics.LinearAlgebra; using MathNet.Numerics.LinearAlgebra.Complex; +using MathNet.Numerics.Random; using NUnit.Framework; namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex @@ -383,5 +385,23 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex var matrix = TestMatrices["Square3x3"]; Assert.IsTrue(matrix.IsSymmetric); } + + [Test] + public void DenseDiagonalMatrixMultiplication() + { + var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); + + Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); + + var tall = Matrix.Build.Random(8, 3, dist); + Assert.IsTrue((tall*Matrix.Build.DiagonalIdentity(3).Multiply(2d)).Equals(tall.Multiply(2d))); + Assert.IsTrue((tall*Matrix.Build.Diagonal(3, 5, 2d)).Equals(tall.Multiply(2d).Append(Matrix.Build.Dense(8, 2)))); + Assert.IsTrue((tall*Matrix.Build.Diagonal(3, 2, 2d)).Equals(tall.Multiply(2d).SubMatrix(0, 8, 0, 2))); + + var wide = Matrix.Build.Random(3, 8, dist); + Assert.IsTrue((wide*Matrix.Build.DiagonalIdentity(8).Multiply(2d)).Equals(wide.Multiply(2d))); + Assert.IsTrue((wide*Matrix.Build.Diagonal(8, 10, 2d)).Equals(wide.Multiply(2d).Append(Matrix.Build.Dense(3, 2)))); + Assert.IsTrue((wide*Matrix.Build.Diagonal(8, 2, 2d)).Equals(wide.Multiply(2d).SubMatrix(0, 3, 0, 2))); + } } } diff --git a/src/UnitTests/LinearAlgebraTests/Complex32/DiagonalMatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Complex32/DiagonalMatrixTests.cs index 1e3058e3..2623229d 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex32/DiagonalMatrixTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex32/DiagonalMatrixTests.cs @@ -30,8 +30,10 @@ using System; using System.Collections.Generic; +using MathNet.Numerics.Distributions; using MathNet.Numerics.LinearAlgebra; using MathNet.Numerics.LinearAlgebra.Complex32; +using MathNet.Numerics.Random; using NUnit.Framework; namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32 @@ -379,5 +381,22 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32 var matrix = TestMatrices["Square3x3"]; Assert.IsTrue(matrix.IsSymmetric); } + [Test] + public void DenseDiagonalMatrixMultiplication() + { + var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); + + Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); + + var tall = Matrix.Build.Random(8, 3, dist); + Assert.IsTrue((tall*Matrix.Build.DiagonalIdentity(3).Multiply(2f)).Equals(tall.Multiply(2f))); + Assert.IsTrue((tall*Matrix.Build.Diagonal(3, 5, 2f)).Equals(tall.Multiply(2f).Append(Matrix.Build.Dense(8, 2)))); + Assert.IsTrue((tall*Matrix.Build.Diagonal(3, 2, 2f)).Equals(tall.Multiply(2f).SubMatrix(0, 8, 0, 2))); + + var wide = Matrix.Build.Random(3, 8, dist); + Assert.IsTrue((wide*Matrix.Build.DiagonalIdentity(8).Multiply(2f)).Equals(wide.Multiply(2f))); + Assert.IsTrue((wide*Matrix.Build.Diagonal(8, 10, 2f)).Equals(wide.Multiply(2f).Append(Matrix.Build.Dense(3, 2)))); + Assert.IsTrue((wide*Matrix.Build.Diagonal(8, 2, 2f)).Equals(wide.Multiply(2f).SubMatrix(0, 3, 0, 2))); + } } } diff --git a/src/UnitTests/LinearAlgebraTests/Double/DiagonalMatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Double/DiagonalMatrixTests.cs index 171cfe35..cc3e5240 100644 --- a/src/UnitTests/LinearAlgebraTests/Double/DiagonalMatrixTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Double/DiagonalMatrixTests.cs @@ -30,8 +30,10 @@ using System; using System.Collections.Generic; +using MathNet.Numerics.Distributions; using MathNet.Numerics.LinearAlgebra; using MathNet.Numerics.LinearAlgebra.Double; +using MathNet.Numerics.Random; using NUnit.Framework; namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double @@ -410,5 +412,23 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double var test = diagonal*dense; var test2 = dense*diagonal; } + + [Test] + public void DenseDiagonalMatrixMultiplication() + { + var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); + + Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); + + var tall = Matrix.Build.Random(8, 3, dist); + Assert.IsTrue((tall*Matrix.Build.DiagonalIdentity(3).Multiply(2d)).Equals(tall.Multiply(2d))); + Assert.IsTrue((tall*Matrix.Build.Diagonal(3, 5, 2d)).Equals(tall.Multiply(2d).Append(Matrix.Build.Dense(8, 2)))); + Assert.IsTrue((tall*Matrix.Build.Diagonal(3, 2, 2d)).Equals(tall.Multiply(2d).SubMatrix(0, 8, 0, 2))); + + var wide = Matrix.Build.Random(3, 8, dist); + Assert.IsTrue((wide*Matrix.Build.DiagonalIdentity(8).Multiply(2d)).Equals(wide.Multiply(2d))); + Assert.IsTrue((wide*Matrix.Build.Diagonal(8, 10, 2d)).Equals(wide.Multiply(2d).Append(Matrix.Build.Dense(3, 2)))); + Assert.IsTrue((wide*Matrix.Build.Diagonal(8, 2, 2d)).Equals(wide.Multiply(2d).SubMatrix(0, 3, 0, 2))); + } } } diff --git a/src/UnitTests/LinearAlgebraTests/Single/DiagonalMatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Single/DiagonalMatrixTests.cs index 26ae0809..8773fce1 100644 --- a/src/UnitTests/LinearAlgebraTests/Single/DiagonalMatrixTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Single/DiagonalMatrixTests.cs @@ -30,8 +30,10 @@ using System; using System.Collections.Generic; +using MathNet.Numerics.Distributions; using MathNet.Numerics.LinearAlgebra; using MathNet.Numerics.LinearAlgebra.Single; +using MathNet.Numerics.Random; using NUnit.Framework; namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single @@ -377,5 +379,23 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single var matrix = TestMatrices["Square3x3"]; Assert.IsTrue(matrix.IsSymmetric); } + + [Test] + public void DenseDiagonalMatrixMultiplication() + { + var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); + + Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); + + var tall = Matrix.Build.Random(8, 3, dist); + Assert.IsTrue((tall*Matrix.Build.DiagonalIdentity(3).Multiply(2f)).Equals(tall.Multiply(2f))); + Assert.IsTrue((tall*Matrix.Build.Diagonal(3, 5, 2f)).Equals(tall.Multiply(2f).Append(Matrix.Build.Dense(8, 2)))); + Assert.IsTrue((tall*Matrix.Build.Diagonal(3, 2, 2f)).Equals(tall.Multiply(2f).SubMatrix(0, 8, 0, 2))); + + var wide = Matrix.Build.Random(3, 8, dist); + Assert.IsTrue((wide*Matrix.Build.DiagonalIdentity(8).Multiply(2f)).Equals(wide.Multiply(2f))); + Assert.IsTrue((wide*Matrix.Build.Diagonal(8, 10, 2f)).Equals(wide.Multiply(2f).Append(Matrix.Build.Dense(3, 2)))); + Assert.IsTrue((wide*Matrix.Build.Diagonal(8, 2, 2f)).Equals(wide.Multiply(2f).SubMatrix(0, 3, 0, 2))); + } } } From 19873cb3046c82698359a0a36142811c41615d75 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sun, 1 Dec 2013 16:16:06 +0100 Subject: [PATCH 017/109] LA: dense-diagonal TransposeAndMultiply --- .../LinearAlgebra/Complex/DenseMatrix.cs | 33 +++++++++++++++---- .../LinearAlgebra/Complex32/DenseMatrix.cs | 33 +++++++++++++++---- .../LinearAlgebra/Double/DenseMatrix.cs | 30 ++++++++++++++--- .../LinearAlgebra/Single/DenseMatrix.cs | 33 +++++++++++++++---- .../Complex/DiagonalMatrixTests.cs | 20 ++++++++++- .../Complex32/DiagonalMatrixTests.cs | 21 +++++++++++- .../Double/DiagonalMatrixTests.cs | 20 ++++++++++- .../Single/DiagonalMatrixTests.cs | 20 ++++++++++- 8 files changed, 180 insertions(+), 30 deletions(-) diff --git a/src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs b/src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs index 43adc606..6d6d575e 100644 --- a/src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs @@ -684,14 +684,9 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// The result of the multiplication. protected override void DoTransposeAndMultiply(Matrix other, Matrix result) { - var denseOther = other as DenseMatrix; + var denseOther = other as DenseMatrix; var denseResult = result as DenseMatrix; - - if (denseOther == null || denseResult == null) - { - base.DoTransposeAndMultiply(other, result); - } - else + if (denseOther != null && denseResult != null) { Control.LinearAlgebraProvider.MatrixMultiplyWithUpdate( Providers.LinearAlgebra.Transpose.DontTranspose, @@ -705,7 +700,31 @@ namespace MathNet.Numerics.LinearAlgebra.Complex denseOther._columnCount, 0.0, denseResult._values); + return; } + + var diagonalOther = other.Storage as DiagonalMatrixStorage; + if (diagonalOther != null) + { + var diagonal = diagonalOther.Data; + var d = Math.Min(ColumnCount, other.RowCount); + if (d < other.RowCount) + { + result.ClearSubMatrix(0, RowCount, ColumnCount, other.RowCount - ColumnCount); + } + int index = 0; + for (int j = 0; j < d; j++) + { + for (int i = 0; i < RowCount; i++) + { + result.At(i, j, _values[index]*diagonal[j]); + index++; + } + } + return; + } + + base.DoTransposeAndMultiply(other, result); } /// diff --git a/src/Numerics/LinearAlgebra/Complex32/DenseMatrix.cs b/src/Numerics/LinearAlgebra/Complex32/DenseMatrix.cs index 37721c1a..b9537954 100644 --- a/src/Numerics/LinearAlgebra/Complex32/DenseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex32/DenseMatrix.cs @@ -679,14 +679,9 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// The result of the multiplication. protected override void DoTransposeAndMultiply(Matrix other, Matrix result) { - var denseOther = other as DenseMatrix; + var denseOther = other as DenseMatrix; var denseResult = result as DenseMatrix; - - if (denseOther == null || denseResult == null) - { - base.DoTransposeAndMultiply(other, result); - } - else + if (denseOther != null && denseResult != null) { Control.LinearAlgebraProvider.MatrixMultiplyWithUpdate( Providers.LinearAlgebra.Transpose.DontTranspose, @@ -700,7 +695,31 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 denseOther._columnCount, 0.0f, denseResult._values); + return; } + + var diagonalOther = other.Storage as DiagonalMatrixStorage; + if (diagonalOther != null) + { + var diagonal = diagonalOther.Data; + var d = Math.Min(ColumnCount, other.RowCount); + if (d < other.RowCount) + { + result.ClearSubMatrix(0, RowCount, ColumnCount, other.RowCount - ColumnCount); + } + int index = 0; + for (int j = 0; j < d; j++) + { + for (int i = 0; i < RowCount; i++) + { + result.At(i, j, _values[index]*diagonal[j]); + index++; + } + } + return; + } + + base.DoTransposeAndMultiply(other, result); } /// diff --git a/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs b/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs index f9370001..74831a77 100644 --- a/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs @@ -661,11 +661,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double { var denseOther = other as DenseMatrix; var denseResult = result as DenseMatrix; - if (denseOther == null || denseResult == null) - { - base.DoTransposeAndMultiply(other, result); - } - else + if (denseOther != null && denseResult != null) { Control.LinearAlgebraProvider.MatrixMultiplyWithUpdate( Providers.LinearAlgebra.Transpose.DontTranspose, @@ -679,7 +675,31 @@ namespace MathNet.Numerics.LinearAlgebra.Double denseOther._columnCount, 0.0, denseResult._values); + return; } + + var diagonalOther = other.Storage as DiagonalMatrixStorage; + if (diagonalOther != null) + { + var diagonal = diagonalOther.Data; + var d = Math.Min(ColumnCount, other.RowCount); + if (d < other.RowCount) + { + result.ClearSubMatrix(0, RowCount, ColumnCount, other.RowCount - ColumnCount); + } + int index = 0; + for (int j = 0; j < d; j++) + { + for (int i = 0; i < RowCount; i++) + { + result.At(i, j, _values[index]*diagonal[j]); + index++; + } + } + return; + } + + base.DoTransposeAndMultiply(other, result); } /// diff --git a/src/Numerics/LinearAlgebra/Single/DenseMatrix.cs b/src/Numerics/LinearAlgebra/Single/DenseMatrix.cs index b112612d..0a17c439 100644 --- a/src/Numerics/LinearAlgebra/Single/DenseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Single/DenseMatrix.cs @@ -659,14 +659,9 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// The result of the multiplication. protected override void DoTransposeAndMultiply(Matrix other, Matrix result) { - var denseOther = other as DenseMatrix; + var denseOther = other as DenseMatrix; var denseResult = result as DenseMatrix; - - if (denseOther == null || denseResult == null) - { - base.DoTransposeAndMultiply(other, result); - } - else + if (denseOther != null && denseResult != null) { Control.LinearAlgebraProvider.MatrixMultiplyWithUpdate( Providers.LinearAlgebra.Transpose.DontTranspose, @@ -680,7 +675,31 @@ namespace MathNet.Numerics.LinearAlgebra.Single denseOther._columnCount, 0.0f, denseResult._values); + return; } + + var diagonalOther = other.Storage as DiagonalMatrixStorage; + if (diagonalOther != null) + { + var diagonal = diagonalOther.Data; + var d = Math.Min(ColumnCount, other.RowCount); + if (d < other.RowCount) + { + result.ClearSubMatrix(0, RowCount, ColumnCount, other.RowCount - ColumnCount); + } + int index = 0; + for (int j = 0; j < d; j++) + { + for (int i = 0; i < RowCount; i++) + { + result.At(i, j, _values[index]*diagonal[j]); + index++; + } + } + return; + } + + base.DoTransposeAndMultiply(other, result); } /// diff --git a/src/UnitTests/LinearAlgebraTests/Complex/DiagonalMatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Complex/DiagonalMatrixTests.cs index c54dc08c..d39d57cc 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex/DiagonalMatrixTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex/DiagonalMatrixTests.cs @@ -387,7 +387,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex } [Test] - public void DenseDiagonalMatrixMultiplication() + public void DenseDiagonalMatrixMultiply() { var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); @@ -403,5 +403,23 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex Assert.IsTrue((wide*Matrix.Build.Diagonal(8, 10, 2d)).Equals(wide.Multiply(2d).Append(Matrix.Build.Dense(3, 2)))); Assert.IsTrue((wide*Matrix.Build.Diagonal(8, 2, 2d)).Equals(wide.Multiply(2d).SubMatrix(0, 3, 0, 2))); } + + [Test] + public void DenseDiagonalMatrixTransposeAndMultiply() + { + var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); + + Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); + + var tall = Matrix.Build.Random(8, 3, dist); + Assert.IsTrue(tall.TransposeAndMultiply(Matrix.Build.DiagonalIdentity(3).Multiply(2d)).Equals(tall.Multiply(2d))); + Assert.IsTrue(tall.TransposeAndMultiply(Matrix.Build.Diagonal(5, 3, 2d)).Equals(tall.Multiply(2d).Append(Matrix.Build.Dense(8, 2)))); + Assert.IsTrue(tall.TransposeAndMultiply(Matrix.Build.Diagonal(2, 3, 2d)).Equals(tall.Multiply(2d).SubMatrix(0, 8, 0, 2))); + + var wide = Matrix.Build.Random(3, 8, dist); + Assert.IsTrue(wide.TransposeAndMultiply(Matrix.Build.DiagonalIdentity(8).Multiply(2d)).Equals(wide.Multiply(2d))); + Assert.IsTrue(wide.TransposeAndMultiply(Matrix.Build.Diagonal(10, 8, 2d)).Equals(wide.Multiply(2d).Append(Matrix.Build.Dense(3, 2)))); + Assert.IsTrue(wide.TransposeAndMultiply(Matrix.Build.Diagonal(2, 8, 2d)).Equals(wide.Multiply(2d).SubMatrix(0, 3, 0, 2))); + } } } diff --git a/src/UnitTests/LinearAlgebraTests/Complex32/DiagonalMatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Complex32/DiagonalMatrixTests.cs index 2623229d..c6db5d0d 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex32/DiagonalMatrixTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex32/DiagonalMatrixTests.cs @@ -381,8 +381,9 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32 var matrix = TestMatrices["Square3x3"]; Assert.IsTrue(matrix.IsSymmetric); } + [Test] - public void DenseDiagonalMatrixMultiplication() + public void DenseDiagonalMatrixMultiply() { var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); @@ -398,5 +399,23 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32 Assert.IsTrue((wide*Matrix.Build.Diagonal(8, 10, 2f)).Equals(wide.Multiply(2f).Append(Matrix.Build.Dense(3, 2)))); Assert.IsTrue((wide*Matrix.Build.Diagonal(8, 2, 2f)).Equals(wide.Multiply(2f).SubMatrix(0, 3, 0, 2))); } + + [Test] + public void DenseDiagonalMatrixTransposeAndMultiply() + { + var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); + + Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); + + var tall = Matrix.Build.Random(8, 3, dist); + Assert.IsTrue(tall.TransposeAndMultiply(Matrix.Build.DiagonalIdentity(3).Multiply(2f)).Equals(tall.Multiply(2f))); + Assert.IsTrue(tall.TransposeAndMultiply(Matrix.Build.Diagonal(5, 3, 2f)).Equals(tall.Multiply(2f).Append(Matrix.Build.Dense(8, 2)))); + Assert.IsTrue(tall.TransposeAndMultiply(Matrix.Build.Diagonal(2, 3, 2f)).Equals(tall.Multiply(2f).SubMatrix(0, 8, 0, 2))); + + var wide = Matrix.Build.Random(3, 8, dist); + Assert.IsTrue(wide.TransposeAndMultiply(Matrix.Build.DiagonalIdentity(8).Multiply(2f)).Equals(wide.Multiply(2f))); + Assert.IsTrue(wide.TransposeAndMultiply(Matrix.Build.Diagonal(10, 8, 2f)).Equals(wide.Multiply(2f).Append(Matrix.Build.Dense(3, 2)))); + Assert.IsTrue(wide.TransposeAndMultiply(Matrix.Build.Diagonal(2, 8, 2f)).Equals(wide.Multiply(2f).SubMatrix(0, 3, 0, 2))); + } } } diff --git a/src/UnitTests/LinearAlgebraTests/Double/DiagonalMatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Double/DiagonalMatrixTests.cs index cc3e5240..d90c7c1e 100644 --- a/src/UnitTests/LinearAlgebraTests/Double/DiagonalMatrixTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Double/DiagonalMatrixTests.cs @@ -414,7 +414,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double } [Test] - public void DenseDiagonalMatrixMultiplication() + public void DenseDiagonalMatrixMultiply() { var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); @@ -430,5 +430,23 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double Assert.IsTrue((wide*Matrix.Build.Diagonal(8, 10, 2d)).Equals(wide.Multiply(2d).Append(Matrix.Build.Dense(3, 2)))); Assert.IsTrue((wide*Matrix.Build.Diagonal(8, 2, 2d)).Equals(wide.Multiply(2d).SubMatrix(0, 3, 0, 2))); } + + [Test] + public void DenseDiagonalMatrixTransposeAndMultiply() + { + var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); + + Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); + + var tall = Matrix.Build.Random(8, 3, dist); + Assert.IsTrue(tall.TransposeAndMultiply(Matrix.Build.DiagonalIdentity(3).Multiply(2d)).Equals(tall.Multiply(2d))); + Assert.IsTrue(tall.TransposeAndMultiply(Matrix.Build.Diagonal(5, 3, 2d)).Equals(tall.Multiply(2d).Append(Matrix.Build.Dense(8, 2)))); + Assert.IsTrue(tall.TransposeAndMultiply(Matrix.Build.Diagonal(2, 3, 2d)).Equals(tall.Multiply(2d).SubMatrix(0, 8, 0, 2))); + + var wide = Matrix.Build.Random(3, 8, dist); + Assert.IsTrue(wide.TransposeAndMultiply(Matrix.Build.DiagonalIdentity(8).Multiply(2d)).Equals(wide.Multiply(2d))); + Assert.IsTrue(wide.TransposeAndMultiply(Matrix.Build.Diagonal(10, 8, 2d)).Equals(wide.Multiply(2d).Append(Matrix.Build.Dense(3, 2)))); + Assert.IsTrue(wide.TransposeAndMultiply(Matrix.Build.Diagonal(2, 8, 2d)).Equals(wide.Multiply(2d).SubMatrix(0, 3, 0, 2))); + } } } diff --git a/src/UnitTests/LinearAlgebraTests/Single/DiagonalMatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Single/DiagonalMatrixTests.cs index 8773fce1..f6c17198 100644 --- a/src/UnitTests/LinearAlgebraTests/Single/DiagonalMatrixTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Single/DiagonalMatrixTests.cs @@ -381,7 +381,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single } [Test] - public void DenseDiagonalMatrixMultiplication() + public void DenseDiagonalMatrixMultiply() { var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); @@ -397,5 +397,23 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single Assert.IsTrue((wide*Matrix.Build.Diagonal(8, 10, 2f)).Equals(wide.Multiply(2f).Append(Matrix.Build.Dense(3, 2)))); Assert.IsTrue((wide*Matrix.Build.Diagonal(8, 2, 2f)).Equals(wide.Multiply(2f).SubMatrix(0, 3, 0, 2))); } + + [Test] + public void DenseDiagonalMatrixTransposeAndMultiply() + { + var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); + + Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); + + var tall = Matrix.Build.Random(8, 3, dist); + Assert.IsTrue(tall.TransposeAndMultiply(Matrix.Build.DiagonalIdentity(3).Multiply(2f)).Equals(tall.Multiply(2f))); + Assert.IsTrue(tall.TransposeAndMultiply(Matrix.Build.Diagonal(5, 3, 2f)).Equals(tall.Multiply(2f).Append(Matrix.Build.Dense(8, 2)))); + Assert.IsTrue(tall.TransposeAndMultiply(Matrix.Build.Diagonal(2, 3, 2f)).Equals(tall.Multiply(2f).SubMatrix(0, 8, 0, 2))); + + var wide = Matrix.Build.Random(3, 8, dist); + Assert.IsTrue(wide.TransposeAndMultiply(Matrix.Build.DiagonalIdentity(8).Multiply(2f)).Equals(wide.Multiply(2f))); + Assert.IsTrue(wide.TransposeAndMultiply(Matrix.Build.Diagonal(10, 8, 2f)).Equals(wide.Multiply(2f).Append(Matrix.Build.Dense(3, 2)))); + Assert.IsTrue(wide.TransposeAndMultiply(Matrix.Build.Diagonal(2, 8, 2f)).Equals(wide.Multiply(2f).SubMatrix(0, 3, 0, 2))); + } } } From 2e1dc29f96163f78dae03887d67510f3a1b7405c Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sun, 1 Dec 2013 16:34:24 +0100 Subject: [PATCH 018/109] LA: dense-diagonal TransposeThisAndMultiply --- .../LinearAlgebra/Complex/DenseMatrix.cs | 32 +++++++++++++++---- .../LinearAlgebra/Complex32/DenseMatrix.cs | 32 +++++++++++++++---- .../LinearAlgebra/Double/DenseMatrix.cs | 32 +++++++++++++++---- .../LinearAlgebra/Single/DenseMatrix.cs | 32 +++++++++++++++---- .../Complex/DiagonalMatrixTests.cs | 19 +++++++++-- .../Complex32/DiagonalMatrixTests.cs | 19 +++++++++-- .../Double/DiagonalMatrixTests.cs | 19 +++++++++-- .../Single/DiagonalMatrixTests.cs | 19 +++++++++-- 8 files changed, 172 insertions(+), 32 deletions(-) diff --git a/src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs b/src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs index 6d6d575e..7deaf8d1 100644 --- a/src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs @@ -767,12 +767,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex { var denseOther = other as DenseMatrix; var denseResult = result as DenseMatrix; - - if (denseOther == null || denseResult == null) - { - base.DoTransposeThisAndMultiply(other, result); - } - else + if (denseOther != null && denseResult != null) { Control.LinearAlgebraProvider.MatrixMultiplyWithUpdate( Providers.LinearAlgebra.Transpose.Transpose, @@ -786,7 +781,32 @@ namespace MathNet.Numerics.LinearAlgebra.Complex denseOther._columnCount, 0.0, denseResult._values); + return; } + + var diagonalOther = other.Storage as DiagonalMatrixStorage; + if (diagonalOther != null) + { + var diagonal = diagonalOther.Data; + var d = Math.Min(RowCount, other.ColumnCount); + if (d < other.ColumnCount) + { + result.ClearSubMatrix(0, ColumnCount, RowCount, other.ColumnCount - RowCount); + } + int index = 0; + for (int i = 0; i < ColumnCount; i++) + { + for (int j = 0; j < d; j++) + { + result.At(i, j, _values[index]*diagonal[j]); + index++; + } + index += (RowCount - d); + } + return; + } + + base.DoTransposeThisAndMultiply(other, result); } /// diff --git a/src/Numerics/LinearAlgebra/Complex32/DenseMatrix.cs b/src/Numerics/LinearAlgebra/Complex32/DenseMatrix.cs index b9537954..1337b9aa 100644 --- a/src/Numerics/LinearAlgebra/Complex32/DenseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex32/DenseMatrix.cs @@ -762,12 +762,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 { var denseOther = other as DenseMatrix; var denseResult = result as DenseMatrix; - - if (denseOther == null || denseResult == null) - { - base.DoTransposeThisAndMultiply(other, result); - } - else + if (denseOther != null && denseResult != null) { Control.LinearAlgebraProvider.MatrixMultiplyWithUpdate( Providers.LinearAlgebra.Transpose.Transpose, @@ -781,7 +776,32 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 denseOther._columnCount, 0.0f, denseResult._values); + return; } + + var diagonalOther = other.Storage as DiagonalMatrixStorage; + if (diagonalOther != null) + { + var diagonal = diagonalOther.Data; + var d = Math.Min(RowCount, other.ColumnCount); + if (d < other.ColumnCount) + { + result.ClearSubMatrix(0, ColumnCount, RowCount, other.ColumnCount - RowCount); + } + int index = 0; + for (int i = 0; i < ColumnCount; i++) + { + for (int j = 0; j < d; j++) + { + result.At(i, j, _values[index]*diagonal[j]); + index++; + } + index += (RowCount - d); + } + return; + } + + base.DoTransposeThisAndMultiply(other, result); } /// diff --git a/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs b/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs index 74831a77..a45cd15f 100644 --- a/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs @@ -742,12 +742,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double { var denseOther = other as DenseMatrix; var denseResult = result as DenseMatrix; - - if (denseOther == null || denseResult == null) - { - base.DoTransposeThisAndMultiply(other, result); - } - else + if (denseOther != null && denseResult != null) { Control.LinearAlgebraProvider.MatrixMultiplyWithUpdate( Providers.LinearAlgebra.Transpose.Transpose, @@ -761,7 +756,32 @@ namespace MathNet.Numerics.LinearAlgebra.Double denseOther._columnCount, 0.0, denseResult._values); + return; } + + var diagonalOther = other.Storage as DiagonalMatrixStorage; + if (diagonalOther != null) + { + var diagonal = diagonalOther.Data; + var d = Math.Min(RowCount, other.ColumnCount); + if (d < other.ColumnCount) + { + result.ClearSubMatrix(0, ColumnCount, RowCount, other.ColumnCount - RowCount); + } + int index = 0; + for (int i = 0; i < ColumnCount; i++) + { + for (int j = 0; j < d; j++) + { + result.At(i, j, _values[index]*diagonal[j]); + index++; + } + index += (RowCount - d); + } + return; + } + + base.DoTransposeThisAndMultiply(other, result); } /// diff --git a/src/Numerics/LinearAlgebra/Single/DenseMatrix.cs b/src/Numerics/LinearAlgebra/Single/DenseMatrix.cs index 0a17c439..61dc0c34 100644 --- a/src/Numerics/LinearAlgebra/Single/DenseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Single/DenseMatrix.cs @@ -742,12 +742,7 @@ namespace MathNet.Numerics.LinearAlgebra.Single { var denseOther = other as DenseMatrix; var denseResult = result as DenseMatrix; - - if (denseOther == null || denseResult == null) - { - base.DoTransposeThisAndMultiply(other, result); - } - else + if (denseOther != null && denseResult != null) { Control.LinearAlgebraProvider.MatrixMultiplyWithUpdate( Providers.LinearAlgebra.Transpose.Transpose, @@ -761,7 +756,32 @@ namespace MathNet.Numerics.LinearAlgebra.Single denseOther._columnCount, 0.0f, denseResult._values); + return; } + + var diagonalOther = other.Storage as DiagonalMatrixStorage; + if (diagonalOther != null) + { + var diagonal = diagonalOther.Data; + var d = Math.Min(RowCount, other.ColumnCount); + if (d < other.ColumnCount) + { + result.ClearSubMatrix(0, ColumnCount, RowCount, other.ColumnCount - RowCount); + } + int index = 0; + for (int i = 0; i < ColumnCount; i++) + { + for (int j = 0; j < d; j++) + { + result.At(i, j, _values[index]*diagonal[j]); + index++; + } + index += (RowCount - d); + } + return; + } + + base.DoTransposeThisAndMultiply(other, result); } /// diff --git a/src/UnitTests/LinearAlgebraTests/Complex/DiagonalMatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Complex/DiagonalMatrixTests.cs index d39d57cc..55de88b8 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex/DiagonalMatrixTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex/DiagonalMatrixTests.cs @@ -390,7 +390,6 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex public void DenseDiagonalMatrixMultiply() { var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); - Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); var tall = Matrix.Build.Random(8, 3, dist); @@ -408,7 +407,6 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex public void DenseDiagonalMatrixTransposeAndMultiply() { var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); - Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); var tall = Matrix.Build.Random(8, 3, dist); @@ -421,5 +419,22 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex Assert.IsTrue(wide.TransposeAndMultiply(Matrix.Build.Diagonal(10, 8, 2d)).Equals(wide.Multiply(2d).Append(Matrix.Build.Dense(3, 2)))); Assert.IsTrue(wide.TransposeAndMultiply(Matrix.Build.Diagonal(2, 8, 2d)).Equals(wide.Multiply(2d).SubMatrix(0, 3, 0, 2))); } + + [Test] + public void DenseDiagonalMatrixTransposeThisAndMultiply() + { + var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); + Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); + + var wide = Matrix.Build.Random(3, 8, dist); + Assert.IsTrue(wide.TransposeThisAndMultiply(Matrix.Build.DiagonalIdentity(3).Multiply(2d)).Equals(wide.Transpose().Multiply(2d))); + Assert.IsTrue(wide.TransposeThisAndMultiply(Matrix.Build.Diagonal(3, 5, 2d)).Equals(wide.Transpose().Multiply(2d).Append(Matrix.Build.Dense(8, 2)))); + Assert.IsTrue(wide.TransposeThisAndMultiply(Matrix.Build.Diagonal(3, 2, 2d)).Equals(wide.Transpose().Multiply(2d).SubMatrix(0, 8, 0, 2))); + + var tall = Matrix.Build.Random(8, 3, dist); + Assert.IsTrue(tall.TransposeThisAndMultiply(Matrix.Build.DiagonalIdentity(8).Multiply(2d)).Equals(tall.Transpose().Multiply(2d))); + Assert.IsTrue(tall.TransposeThisAndMultiply(Matrix.Build.Diagonal(8, 10, 2d)).Equals(tall.Transpose().Multiply(2d).Append(Matrix.Build.Dense(3, 2)))); + Assert.IsTrue(tall.TransposeThisAndMultiply(Matrix.Build.Diagonal(8, 2, 2d)).Equals(tall.Transpose().Multiply(2d).SubMatrix(0, 3, 0, 2))); + } } } diff --git a/src/UnitTests/LinearAlgebraTests/Complex32/DiagonalMatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Complex32/DiagonalMatrixTests.cs index c6db5d0d..c4423a74 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex32/DiagonalMatrixTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex32/DiagonalMatrixTests.cs @@ -386,7 +386,6 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32 public void DenseDiagonalMatrixMultiply() { var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); - Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); var tall = Matrix.Build.Random(8, 3, dist); @@ -404,7 +403,6 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32 public void DenseDiagonalMatrixTransposeAndMultiply() { var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); - Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); var tall = Matrix.Build.Random(8, 3, dist); @@ -417,5 +415,22 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32 Assert.IsTrue(wide.TransposeAndMultiply(Matrix.Build.Diagonal(10, 8, 2f)).Equals(wide.Multiply(2f).Append(Matrix.Build.Dense(3, 2)))); Assert.IsTrue(wide.TransposeAndMultiply(Matrix.Build.Diagonal(2, 8, 2f)).Equals(wide.Multiply(2f).SubMatrix(0, 3, 0, 2))); } + + [Test] + public void DenseDiagonalMatrixTransposeThisAndMultiply() + { + var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); + Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); + + var wide = Matrix.Build.Random(3, 8, dist); + Assert.IsTrue(wide.TransposeThisAndMultiply(Matrix.Build.DiagonalIdentity(3).Multiply(2f)).Equals(wide.Transpose().Multiply(2f))); + Assert.IsTrue(wide.TransposeThisAndMultiply(Matrix.Build.Diagonal(3, 5, 2f)).Equals(wide.Transpose().Multiply(2f).Append(Matrix.Build.Dense(8, 2)))); + Assert.IsTrue(wide.TransposeThisAndMultiply(Matrix.Build.Diagonal(3, 2, 2f)).Equals(wide.Transpose().Multiply(2f).SubMatrix(0, 8, 0, 2))); + + var tall = Matrix.Build.Random(8, 3, dist); + Assert.IsTrue(tall.TransposeThisAndMultiply(Matrix.Build.DiagonalIdentity(8).Multiply(2f)).Equals(tall.Transpose().Multiply(2f))); + Assert.IsTrue(tall.TransposeThisAndMultiply(Matrix.Build.Diagonal(8, 10, 2f)).Equals(tall.Transpose().Multiply(2f).Append(Matrix.Build.Dense(3, 2)))); + Assert.IsTrue(tall.TransposeThisAndMultiply(Matrix.Build.Diagonal(8, 2, 2f)).Equals(tall.Transpose().Multiply(2f).SubMatrix(0, 3, 0, 2))); + } } } diff --git a/src/UnitTests/LinearAlgebraTests/Double/DiagonalMatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Double/DiagonalMatrixTests.cs index d90c7c1e..bb27d8b5 100644 --- a/src/UnitTests/LinearAlgebraTests/Double/DiagonalMatrixTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Double/DiagonalMatrixTests.cs @@ -417,7 +417,6 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double public void DenseDiagonalMatrixMultiply() { var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); - Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); var tall = Matrix.Build.Random(8, 3, dist); @@ -435,7 +434,6 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double public void DenseDiagonalMatrixTransposeAndMultiply() { var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); - Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); var tall = Matrix.Build.Random(8, 3, dist); @@ -448,5 +446,22 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double Assert.IsTrue(wide.TransposeAndMultiply(Matrix.Build.Diagonal(10, 8, 2d)).Equals(wide.Multiply(2d).Append(Matrix.Build.Dense(3, 2)))); Assert.IsTrue(wide.TransposeAndMultiply(Matrix.Build.Diagonal(2, 8, 2d)).Equals(wide.Multiply(2d).SubMatrix(0, 3, 0, 2))); } + + [Test] + public void DenseDiagonalMatrixTransposeThisAndMultiply() + { + var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); + Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); + + var wide = Matrix.Build.Random(3, 8, dist); + Assert.IsTrue(wide.TransposeThisAndMultiply(Matrix.Build.DiagonalIdentity(3).Multiply(2d)).Equals(wide.Transpose().Multiply(2d))); + Assert.IsTrue(wide.TransposeThisAndMultiply(Matrix.Build.Diagonal(3, 5, 2d)).Equals(wide.Transpose().Multiply(2d).Append(Matrix.Build.Dense(8, 2)))); + Assert.IsTrue(wide.TransposeThisAndMultiply(Matrix.Build.Diagonal(3, 2, 2d)).Equals(wide.Transpose().Multiply(2d).SubMatrix(0, 8, 0, 2))); + + var tall = Matrix.Build.Random(8, 3, dist); + Assert.IsTrue(tall.TransposeThisAndMultiply(Matrix.Build.DiagonalIdentity(8).Multiply(2d)).Equals(tall.Transpose().Multiply(2d))); + Assert.IsTrue(tall.TransposeThisAndMultiply(Matrix.Build.Diagonal(8, 10, 2d)).Equals(tall.Transpose().Multiply(2d).Append(Matrix.Build.Dense(3, 2)))); + Assert.IsTrue(tall.TransposeThisAndMultiply(Matrix.Build.Diagonal(8, 2, 2d)).Equals(tall.Transpose().Multiply(2d).SubMatrix(0, 3, 0, 2))); + } } } diff --git a/src/UnitTests/LinearAlgebraTests/Single/DiagonalMatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Single/DiagonalMatrixTests.cs index f6c17198..6c253abd 100644 --- a/src/UnitTests/LinearAlgebraTests/Single/DiagonalMatrixTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Single/DiagonalMatrixTests.cs @@ -384,7 +384,6 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single public void DenseDiagonalMatrixMultiply() { var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); - Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); var tall = Matrix.Build.Random(8, 3, dist); @@ -402,7 +401,6 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single public void DenseDiagonalMatrixTransposeAndMultiply() { var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); - Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); var tall = Matrix.Build.Random(8, 3, dist); @@ -415,5 +413,22 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single Assert.IsTrue(wide.TransposeAndMultiply(Matrix.Build.Diagonal(10, 8, 2f)).Equals(wide.Multiply(2f).Append(Matrix.Build.Dense(3, 2)))); Assert.IsTrue(wide.TransposeAndMultiply(Matrix.Build.Diagonal(2, 8, 2f)).Equals(wide.Multiply(2f).SubMatrix(0, 3, 0, 2))); } + + [Test] + public void DenseDiagonalMatrixTransposeThisAndMultiply() + { + var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); + Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); + + var wide = Matrix.Build.Random(3, 8, dist); + Assert.IsTrue(wide.TransposeThisAndMultiply(Matrix.Build.DiagonalIdentity(3).Multiply(2f)).Equals(wide.Transpose().Multiply(2f))); + Assert.IsTrue(wide.TransposeThisAndMultiply(Matrix.Build.Diagonal(3, 5, 2f)).Equals(wide.Transpose().Multiply(2f).Append(Matrix.Build.Dense(8, 2)))); + Assert.IsTrue(wide.TransposeThisAndMultiply(Matrix.Build.Diagonal(3, 2, 2f)).Equals(wide.Transpose().Multiply(2f).SubMatrix(0, 8, 0, 2))); + + var tall = Matrix.Build.Random(8, 3, dist); + Assert.IsTrue(tall.TransposeThisAndMultiply(Matrix.Build.DiagonalIdentity(8).Multiply(2f)).Equals(tall.Transpose().Multiply(2f))); + Assert.IsTrue(tall.TransposeThisAndMultiply(Matrix.Build.Diagonal(8, 10, 2f)).Equals(tall.Transpose().Multiply(2f).Append(Matrix.Build.Dense(3, 2)))); + Assert.IsTrue(tall.TransposeThisAndMultiply(Matrix.Build.Diagonal(8, 2, 2f)).Equals(tall.Transpose().Multiply(2f).SubMatrix(0, 3, 0, 2))); + } } } From 44228a6d41f078b97452a24781e23cd3736f086b Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sun, 1 Dec 2013 17:38:04 +0100 Subject: [PATCH 019/109] LA: Build.SameType of one or two example instances --- src/Numerics/LinearAlgebra/Builder.cs | 41 +++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/Numerics/LinearAlgebra/Builder.cs b/src/Numerics/LinearAlgebra/Builder.cs index e668e7e3..e167164e 100644 --- a/src/Numerics/LinearAlgebra/Builder.cs +++ b/src/Numerics/LinearAlgebra/Builder.cs @@ -396,6 +396,31 @@ namespace MathNet.Numerics.LinearAlgebra throw new NotSupportedException(); } + /// + /// Create a new matrix with the same kind of the provided example. + /// + public Matrix SameType(Matrix example, int rows, int columns) + { + var storage = example.Storage; + if (storage is DenseColumnMajorMatrixStorage) return Dense(rows, columns); + if (storage is DiagonalMatrixStorage) return Diagonal(rows, columns); + if (storage is SparseCompressedRowMatrixStorage) return Sparse(rows, columns); + return Dense(rows, columns); + } + + /// + /// Create a new matrix with a type that can represent and is closest to both provided samples. + /// + public Matrix SameType(Matrix example1, Matrix example2 , int rows, int columns) + { + var storage1 = example1.Storage; + var storage2 = example2.Storage; + if (storage1 is DenseColumnMajorMatrixStorage || storage2 is DenseColumnMajorMatrixStorage) return Dense(rows, columns); + if (storage1 is DiagonalMatrixStorage && storage2 is DiagonalMatrixStorage) return Diagonal(rows, columns); + if (storage1 is SparseCompressedRowMatrixStorage || storage2 is SparseCompressedRowMatrixStorage) return Sparse(rows, columns); + return Dense(rows, columns); + } + /// /// Create a new dense matrix with values sampled from the provided random distribution. /// @@ -1190,6 +1215,22 @@ namespace MathNet.Numerics.LinearAlgebra throw new NotSupportedException(); } + /// + /// Create a new vector with the same kind of the provided example. + /// + public Vector SameType(Vector example, int length) + { + return example.Storage.IsDense ? Dense(length) : Sparse(length); + } + + /// + /// Create a new vector with a type that can represent and is closest to both provided samples. + /// + public Vector SameType(Vector example1, Vector example2, int length) + { + return example1.Storage.IsDense || example2.Storage.IsDense ? Dense(length) : Sparse(length); + } + /// /// Create a new dense vector with values sampled from the provided random distribution. /// From aadb8a4bb50085a539f86ea2ceeacde7becd95e4 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sun, 1 Dec 2013 22:15:07 +0100 Subject: [PATCH 020/109] LA: diagonal-dense Multiply TransoseAndMultiply TransposeThisAndMultiply --- .../LinearAlgebra/Complex/DiagonalMatrix.cs | 242 ++++++++--------- .../LinearAlgebra/Complex32/DiagonalMatrix.cs | 244 ++++++++---------- .../LinearAlgebra/Double/DiagonalMatrix.cs | 237 ++++++++--------- .../LinearAlgebra/Matrix.Arithmetic.cs | 36 +-- .../LinearAlgebra/Single/DiagonalMatrix.cs | 237 ++++++++--------- .../Complex/DiagonalMatrixTests.cs | 51 ++++ .../Complex32/DiagonalMatrixTests.cs | 51 ++++ .../Double/DiagonalMatrixTests.cs | 51 ++++ .../Single/DiagonalMatrixTests.cs | 51 ++++ 9 files changed, 650 insertions(+), 550 deletions(-) diff --git a/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs index c89e3e5b..d0e3781d 100644 --- a/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs @@ -194,39 +194,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex i => new Complex(distribution.Sample(), distribution.Sample()))); } - /// - /// Adds another matrix to this matrix. - /// - /// The matrix to add to this matrix. - /// The result of the addition. - /// If the other matrix is . - /// If the two matrices don't have the same dimensions. - public override Matrix Add(Matrix other) - { - if (other == null) - { - throw new ArgumentNullException("other"); - } - - if (other.RowCount != RowCount || other.ColumnCount != ColumnCount) - { - throw DimensionsDontMatch(this, other, "other"); - } - - Matrix result; - if (other is DiagonalMatrix) - { - result = new DiagonalMatrix(RowCount, ColumnCount); - } - else - { - result = new DenseMatrix(RowCount, ColumnCount); - } - - Add(other, result); - return result; - } - /// /// Adds another matrix to this matrix. /// @@ -249,39 +216,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex } } - /// - /// Subtracts another matrix from this matrix. - /// - /// The matrix to subtract. - /// The result of the subtraction. - /// If the other matrix is . - /// If the two matrices don't have the same dimensions. - public override Matrix Subtract(Matrix other) - { - if (other == null) - { - throw new ArgumentNullException("other"); - } - - if (other.RowCount != RowCount || other.ColumnCount != ColumnCount) - { - throw DimensionsDontMatch(this, other, "other"); - } - - Matrix result; - if (other is DiagonalMatrix) - { - result = new DenseMatrix(RowCount, ColumnCount); - } - else - { - result = new DiagonalMatrix(RowCount, ColumnCount); - } - - Subtract(other, result); - return result; - } - /// /// Subtracts another matrix from this matrix. /// @@ -398,72 +332,44 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// /// The matrix to multiply with. /// The result of the multiplication. - /// If the other matrix is . - /// If the result matrix is . - /// If this.Columns != other.Rows. - /// If the result matrix's dimensions are not the this.Rows x other.Columns. - public override void Multiply(Matrix other, Matrix result) - { - if (other == null) - { - throw new ArgumentNullException("other"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (ColumnCount != other.RowCount) - { - throw DimensionsDontMatch(this, other); - } - - if (result.RowCount != RowCount || result.ColumnCount != other.ColumnCount) - { - throw DimensionsDontMatch(this, result); - } - - var m = other as DiagonalMatrix; - var r = result as DiagonalMatrix; - - if (m == null || r == null) - { - base.Multiply(other, result); - } - else - { - var thisDataCopy = new Complex[r._data.Length]; - var otherDataCopy = new Complex[r._data.Length]; - Array.Copy(_data, thisDataCopy, (r._data.Length > _data.Length) ? _data.Length : r._data.Length); - Array.Copy(m._data, otherDataCopy, (r._data.Length > m._data.Length) ? m._data.Length : r._data.Length); - - Control.LinearAlgebraProvider.PointWiseMultiplyArrays(thisDataCopy, otherDataCopy, r._data); - } - } - - /// - /// Multiplies this matrix with another matrix and returns the result. - /// - /// The matrix to multiply with. - /// If this.Columns != other.Rows. - /// If the other matrix is . - /// The result of multiplication. - public override Matrix Multiply(Matrix other) + protected override void DoMultiply(Matrix other, Matrix result) { - if (other == null) + var diagonalOther = other as DiagonalMatrix; + var diagonalResult = result as DiagonalMatrix; + if (diagonalOther != null && diagonalResult != null) { - throw new ArgumentNullException("other"); + var thisDataCopy = new Complex[diagonalResult._data.Length]; + var otherDataCopy = new Complex[diagonalResult._data.Length]; + Array.Copy(_data, thisDataCopy, (diagonalResult._data.Length > _data.Length) ? _data.Length : diagonalResult._data.Length); + Array.Copy(diagonalOther._data, otherDataCopy, (diagonalResult._data.Length > diagonalOther._data.Length) ? diagonalOther._data.Length : diagonalResult._data.Length); + Control.LinearAlgebraProvider.PointWiseMultiplyArrays(thisDataCopy, otherDataCopy, diagonalResult._data); + return; } - if (ColumnCount != other.RowCount) + var denseOther = other.Storage as DenseColumnMajorMatrixStorage; + if (denseOther != null) { - throw DimensionsDontMatch(this, other); + var dense = denseOther.Data; + var diagonal = _data; + var d = Math.Min(denseOther.RowCount, RowCount); + if (d < RowCount) + { + result.ClearSubMatrix(denseOther.RowCount, RowCount - denseOther.RowCount, 0, denseOther.ColumnCount); + } + int index = 0; + for (int i = 0; i < denseOther.ColumnCount; i++) + { + for (int j = 0; j < d; j++) + { + result.At(j, i, dense[index]*diagonal[j]); + index++; + } + index += (denseOther.RowCount - d); + } + return; } - var result = other.CreateMatrix(RowCount, other.ColumnCount); - Multiply(other, result); - return result; + base.DoMultiply(other, result); } /// @@ -596,22 +502,88 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// /// The matrix to multiply with. /// The result of the multiplication. - /// If the other matrix is . - /// If the result matrix is . - /// If this.Columns != other.Rows. - /// If the result matrix's dimensions are not the this.Rows x other.Columns. - public override void TransposeAndMultiply(Matrix other, Matrix result) + protected override void DoTransposeAndMultiply(Matrix other, Matrix result) { - var otherDiagonal = other as DiagonalMatrix; - var resultDiagonal = result as DiagonalMatrix; + var diagonalOther = other as DiagonalMatrix; + var diagonalResult = result as DiagonalMatrix; + if (diagonalOther != null && diagonalResult != null) + { + var thisDataCopy = new Complex[diagonalResult._data.Length]; + var otherDataCopy = new Complex[diagonalResult._data.Length]; + Array.Copy(_data, thisDataCopy, (diagonalResult._data.Length > _data.Length) ? _data.Length : diagonalResult._data.Length); + Array.Copy(diagonalOther._data, otherDataCopy, (diagonalResult._data.Length > diagonalOther._data.Length) ? diagonalOther._data.Length : diagonalResult._data.Length); + Control.LinearAlgebraProvider.PointWiseMultiplyArrays(thisDataCopy, otherDataCopy, diagonalResult._data); + return; + } + + var denseOther = other.Storage as DenseColumnMajorMatrixStorage; + if (denseOther != null) + { + var dense = denseOther.Data; + var diagonal = _data; + var d = Math.Min(denseOther.ColumnCount, RowCount); + if (d < RowCount) + { + result.ClearSubMatrix(denseOther.ColumnCount, RowCount - denseOther.ColumnCount, 0, denseOther.RowCount); + } + int index = 0; + for (int j = 0; j < d; j++) + { + for (int i = 0; i < denseOther.RowCount; i++) + { + result.At(j, i, dense[index]*diagonal[j]); + index++; + } + } + return; + } + + base.DoTransposeAndMultiply(other, result); + } - if (otherDiagonal == null || resultDiagonal == null) + /// + /// Multiplies the transpose of this matrix with another matrix and places the results into the result matrix. + /// + /// The matrix to multiply with. + /// The result of the multiplication. + protected override void DoTransposeThisAndMultiply(Matrix other, Matrix result) + { + var diagonalOther = other as DiagonalMatrix; + var diagonalResult = result as DiagonalMatrix; + if (diagonalOther != null && diagonalResult != null) { - base.TransposeAndMultiply(other, result); + var thisDataCopy = new Complex[diagonalResult._data.Length]; + var otherDataCopy = new Complex[diagonalResult._data.Length]; + Array.Copy(_data, thisDataCopy, (diagonalResult._data.Length > _data.Length) ? _data.Length : diagonalResult._data.Length); + Array.Copy(diagonalOther._data, otherDataCopy, (diagonalResult._data.Length > diagonalOther._data.Length) ? diagonalOther._data.Length : diagonalResult._data.Length); + Control.LinearAlgebraProvider.PointWiseMultiplyArrays(thisDataCopy, otherDataCopy, diagonalResult._data); + return; + } + + var denseOther = other.Storage as DenseColumnMajorMatrixStorage; + if (denseOther != null) + { + var dense = denseOther.Data; + var diagonal = _data; + var d = Math.Min(denseOther.RowCount, ColumnCount); + if (d < ColumnCount) + { + result.ClearSubMatrix(denseOther.RowCount, ColumnCount - denseOther.RowCount, 0, denseOther.ColumnCount); + } + int index = 0; + for (int i = 0; i < denseOther.ColumnCount; i++) + { + for (int j = 0; j < d; j++) + { + result.At(j, i, dense[index]*diagonal[j]); + index++; + } + index += (denseOther.RowCount - d); + } return; } - Multiply(otherDiagonal.Transpose(), result); + base.DoTransposeThisAndMultiply(other, result); } /// diff --git a/src/Numerics/LinearAlgebra/Complex32/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Complex32/DiagonalMatrix.cs index 2213a10e..91ccf693 100644 --- a/src/Numerics/LinearAlgebra/Complex32/DiagonalMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex32/DiagonalMatrix.cs @@ -189,39 +189,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 i => new Complex32((float) distribution.Sample(), (float) distribution.Sample()))); } - /// - /// Adds another matrix to this matrix. - /// - /// The matrix to add to this matrix. - /// The result of the addition. - /// If the other matrix is . - /// If the two matrices don't have the same dimensions. - public override Matrix Add(Matrix other) - { - if (other == null) - { - throw new ArgumentNullException("other"); - } - - if (other.RowCount != RowCount || other.ColumnCount != ColumnCount) - { - throw DimensionsDontMatch(this, other, "other"); - } - - Matrix result; - if (other is DiagonalMatrix) - { - result = new DiagonalMatrix(RowCount, ColumnCount); - } - else - { - result = new DenseMatrix(RowCount, ColumnCount); - } - - Add(other, result); - return result; - } - /// /// Adds another matrix to this matrix. /// @@ -244,39 +211,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 } } - /// - /// Subtracts another matrix from this matrix. - /// - /// The matrix to subtract. - /// The result of the subtraction. - /// If the other matrix is . - /// If the two matrices don't have the same dimensions. - public override Matrix Subtract(Matrix other) - { - if (other == null) - { - throw new ArgumentNullException("other"); - } - - if (other.RowCount != RowCount || other.ColumnCount != ColumnCount) - { - throw DimensionsDontMatch(this, other, "other"); - } - - Matrix result; - if (other is DiagonalMatrix) - { - result = new DiagonalMatrix(RowCount, ColumnCount); - } - else - { - result = new DenseMatrix(RowCount, ColumnCount); - } - - Subtract(other, result); - return result; - } - /// /// Subtracts another matrix from this matrix. /// @@ -356,8 +290,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// /// The scalar to multiply the matrix with. /// The matrix to store the result of the multiplication. - /// If the result matrix is . - /// If the result matrix's dimensions are not the same as this matrix. protected override void DoMultiply(Complex32 scalar, Matrix result) { if (scalar.IsZero()) @@ -393,72 +325,44 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// /// The matrix to multiply with. /// The result of the multiplication. - /// If the other matrix is . - /// If the result matrix is . - /// If this.Columns != other.Rows. - /// If the result matrix's dimensions are not the this.Rows x other.Columns. - public override void Multiply(Matrix other, Matrix result) - { - if (other == null) - { - throw new ArgumentNullException("other"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (ColumnCount != other.RowCount) - { - throw DimensionsDontMatch(this, other); - } - - if (result.RowCount != RowCount || result.ColumnCount != other.ColumnCount) - { - throw DimensionsDontMatch(this, other); - } - - var m = other as DiagonalMatrix; - var r = result as DiagonalMatrix; - - if (m == null || r == null) - { - base.Multiply(other, result); - } - else - { - var thisDataCopy = new Complex32[r._data.Length]; - var otherDataCopy = new Complex32[r._data.Length]; - Array.Copy(_data, thisDataCopy, (r._data.Length > _data.Length) ? _data.Length : r._data.Length); - Array.Copy(m._data, otherDataCopy, (r._data.Length > m._data.Length) ? m._data.Length : r._data.Length); - - Control.LinearAlgebraProvider.PointWiseMultiplyArrays(thisDataCopy, otherDataCopy, r._data); - } - } - - /// - /// Multiplies this matrix with another matrix and returns the result. - /// - /// The matrix to multiply with. - /// If this.Columns != other.Rows. - /// If the other matrix is . - /// The result of multiplication. - public override Matrix Multiply(Matrix other) + protected override void DoMultiply(Matrix other, Matrix result) { - if (other == null) + var diagonalOther = other as DiagonalMatrix; + var diagonalResult = result as DiagonalMatrix; + if (diagonalOther != null && diagonalResult != null) { - throw new ArgumentNullException("other"); + var thisDataCopy = new Complex32[diagonalResult._data.Length]; + var otherDataCopy = new Complex32[diagonalResult._data.Length]; + Array.Copy(_data, thisDataCopy, (diagonalResult._data.Length > _data.Length) ? _data.Length : diagonalResult._data.Length); + Array.Copy(diagonalOther._data, otherDataCopy, (diagonalResult._data.Length > diagonalOther._data.Length) ? diagonalOther._data.Length : diagonalResult._data.Length); + Control.LinearAlgebraProvider.PointWiseMultiplyArrays(thisDataCopy, otherDataCopy, diagonalResult._data); + return; } - if (ColumnCount != other.RowCount) + var denseOther = other.Storage as DenseColumnMajorMatrixStorage; + if (denseOther != null) { - throw DimensionsDontMatch(this, other); + var dense = denseOther.Data; + var diagonal = _data; + var d = Math.Min(denseOther.RowCount, RowCount); + if (d < RowCount) + { + result.ClearSubMatrix(denseOther.RowCount, RowCount - denseOther.RowCount, 0, denseOther.ColumnCount); + } + int index = 0; + for (int i = 0; i < denseOther.ColumnCount; i++) + { + for (int j = 0; j < d; j++) + { + result.At(j, i, dense[index]*diagonal[j]); + index++; + } + index += (denseOther.RowCount - d); + } + return; } - var result = other.CreateMatrix(RowCount, other.ColumnCount); - Multiply(other, result); - return result; + base.DoMultiply(other, result); } /// @@ -591,22 +495,88 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// /// The matrix to multiply with. /// The result of the multiplication. - /// If the other matrix is . - /// If the result matrix is . - /// If this.Columns != other.Rows. - /// If the result matrix's dimensions are not the this.Rows x other.Columns. - public override void TransposeAndMultiply(Matrix other, Matrix result) + protected override void DoTransposeAndMultiply(Matrix other, Matrix result) { - var otherDiagonal = other as DiagonalMatrix; - var resultDiagonal = result as DiagonalMatrix; + var diagonalOther = other as DiagonalMatrix; + var diagonalResult = result as DiagonalMatrix; + if (diagonalOther != null && diagonalResult != null) + { + var thisDataCopy = new Complex32[diagonalResult._data.Length]; + var otherDataCopy = new Complex32[diagonalResult._data.Length]; + Array.Copy(_data, thisDataCopy, (diagonalResult._data.Length > _data.Length) ? _data.Length : diagonalResult._data.Length); + Array.Copy(diagonalOther._data, otherDataCopy, (diagonalResult._data.Length > diagonalOther._data.Length) ? diagonalOther._data.Length : diagonalResult._data.Length); + Control.LinearAlgebraProvider.PointWiseMultiplyArrays(thisDataCopy, otherDataCopy, diagonalResult._data); + return; + } - if (otherDiagonal == null || resultDiagonal == null) + var denseOther = other.Storage as DenseColumnMajorMatrixStorage; + if (denseOther != null) { - base.TransposeAndMultiply(other, result); + var dense = denseOther.Data; + var diagonal = _data; + var d = Math.Min(denseOther.ColumnCount, RowCount); + if (d < RowCount) + { + result.ClearSubMatrix(denseOther.ColumnCount, RowCount - denseOther.ColumnCount, 0, denseOther.RowCount); + } + int index = 0; + for (int j = 0; j < d; j++) + { + for (int i = 0; i < denseOther.RowCount; i++) + { + result.At(j, i, dense[index]*diagonal[j]); + index++; + } + } + return; + } + + base.DoTransposeAndMultiply(other, result); + } + + /// + /// Multiplies the transpose of this matrix with another matrix and places the results into the result matrix. + /// + /// The matrix to multiply with. + /// The result of the multiplication. + protected override void DoTransposeThisAndMultiply(Matrix other, Matrix result) + { + var diagonalOther = other as DiagonalMatrix; + var diagonalResult = result as DiagonalMatrix; + if (diagonalOther != null && diagonalResult != null) + { + var thisDataCopy = new Complex32[diagonalResult._data.Length]; + var otherDataCopy = new Complex32[diagonalResult._data.Length]; + Array.Copy(_data, thisDataCopy, (diagonalResult._data.Length > _data.Length) ? _data.Length : diagonalResult._data.Length); + Array.Copy(diagonalOther._data, otherDataCopy, (diagonalResult._data.Length > diagonalOther._data.Length) ? diagonalOther._data.Length : diagonalResult._data.Length); + Control.LinearAlgebraProvider.PointWiseMultiplyArrays(thisDataCopy, otherDataCopy, diagonalResult._data); + return; + } + + var denseOther = other.Storage as DenseColumnMajorMatrixStorage; + if (denseOther != null) + { + var dense = denseOther.Data; + var diagonal = _data; + var d = Math.Min(denseOther.RowCount, ColumnCount); + if (d < ColumnCount) + { + result.ClearSubMatrix(denseOther.RowCount, ColumnCount - denseOther.RowCount, 0, denseOther.ColumnCount); + } + int index = 0; + for (int i = 0; i < denseOther.ColumnCount; i++) + { + for (int j = 0; j < d; j++) + { + result.At(j, i, dense[index]*diagonal[j]); + index++; + } + index += (denseOther.RowCount - d); + } return; } - Multiply(otherDiagonal.Transpose(), result); + base.DoTransposeThisAndMultiply(other, result); } /// diff --git a/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs index 872d85b3..2e0b8216 100644 --- a/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs +++ b/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs @@ -188,39 +188,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double i => distribution.Sample())); } - /// - /// Adds another matrix to this matrix. - /// - /// The matrix to add to this matrix. - /// The result of the addition. - /// If the other matrix is . - /// If the two matrices don't have the same dimensions. - public override Matrix Add(Matrix other) - { - if (other == null) - { - throw new ArgumentNullException("other"); - } - - if (other.RowCount != RowCount || other.ColumnCount != ColumnCount) - { - throw DimensionsDontMatch(this, other, "other"); - } - - Matrix result; - if (other is DiagonalMatrix) - { - result = new DiagonalMatrix(RowCount, ColumnCount); - } - else - { - result = new DenseMatrix(RowCount, ColumnCount); - } - - Add(other, result); - return result; - } - /// /// Adds another matrix to this matrix. /// @@ -243,39 +210,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double } } - /// - /// Subtracts another matrix from this matrix. - /// - /// The matrix to subtract. - /// The result of the subtraction. - /// If the other matrix is . - /// If the two matrices don't have the same dimensions. - public override Matrix Subtract(Matrix other) - { - if (other == null) - { - throw new ArgumentNullException("other"); - } - - if (other.RowCount != RowCount || other.ColumnCount != ColumnCount) - { - throw DimensionsDontMatch(this, other, "other"); - } - - Matrix result; - if (other is DiagonalMatrix) - { - result = new DiagonalMatrix(RowCount, ColumnCount); - } - else - { - result = new DenseMatrix(RowCount, ColumnCount); - } - - Subtract(other, result); - return result; - } - /// /// Subtracts another matrix from this matrix. /// @@ -392,67 +326,44 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// /// The matrix to multiply with. /// The result of the multiplication. - /// If the other matrix is . - /// If the result matrix is . - /// If this.Columns != other.Rows. - /// If the result matrix's dimensions are not the this.Rows x other.Columns. - public override void Multiply(Matrix other, Matrix result) - { - if (other == null) - { - throw new ArgumentNullException("other"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (ColumnCount != other.RowCount || result.RowCount != RowCount || result.ColumnCount != other.ColumnCount) - { - throw DimensionsDontMatch(this, other, result); - } - - var m = other as DiagonalMatrix; - var r = result as DiagonalMatrix; - - if (m == null || r == null) - { - base.Multiply(other, result); - } - else - { - var thisDataCopy = new double[r._data.Length]; - var otherDataCopy = new double[r._data.Length]; - Buffer.BlockCopy(_data, 0, thisDataCopy, 0, (r._data.Length > _data.Length) ? _data.Length * Constants.SizeOfDouble : r._data.Length * Constants.SizeOfDouble); - Buffer.BlockCopy(m._data, 0, otherDataCopy, 0, (r._data.Length > m._data.Length) ? m._data.Length * Constants.SizeOfDouble : r._data.Length * Constants.SizeOfDouble); - - Control.LinearAlgebraProvider.PointWiseMultiplyArrays(thisDataCopy, otherDataCopy, r._data); - } - } - - /// - /// Multiplies this matrix with another matrix and returns the result. - /// - /// The matrix to multiply with. - /// If this.Columns != other.Rows. - /// If the other matrix is . - /// The result of multiplication. - public override Matrix Multiply(Matrix other) + protected override void DoMultiply(Matrix other, Matrix result) { - if (other == null) + var diagonalOther = other as DiagonalMatrix; + var diagonalResult = result as DiagonalMatrix; + if (diagonalOther != null && diagonalResult != null) { - throw new ArgumentNullException("other"); + var thisDataCopy = new double[diagonalResult._data.Length]; + var otherDataCopy = new double[diagonalResult._data.Length]; + Array.Copy(_data, thisDataCopy, (diagonalResult._data.Length > _data.Length) ? _data.Length : diagonalResult._data.Length); + Array.Copy(diagonalOther._data, otherDataCopy, (diagonalResult._data.Length > diagonalOther._data.Length) ? diagonalOther._data.Length : diagonalResult._data.Length); + Control.LinearAlgebraProvider.PointWiseMultiplyArrays(thisDataCopy, otherDataCopy, diagonalResult._data); + return; } - if (ColumnCount != other.RowCount) + var denseOther = other.Storage as DenseColumnMajorMatrixStorage; + if (denseOther != null) { - throw DimensionsDontMatch(this, other); + var dense = denseOther.Data; + var diagonal = _data; + var d = Math.Min(denseOther.RowCount, RowCount); + if (d < RowCount) + { + result.ClearSubMatrix(denseOther.RowCount, RowCount - denseOther.RowCount, 0, denseOther.ColumnCount); + } + int index = 0; + for (int i = 0; i < denseOther.ColumnCount; i++) + { + for (int j = 0; j < d; j++) + { + result.At(j, i, dense[index]*diagonal[j]); + index++; + } + index += (denseOther.RowCount - d); + } + return; } - var result = other.CreateMatrix(RowCount, other.ColumnCount); - Multiply(other, result); - return result; + base.DoMultiply(other, result); } /// @@ -585,22 +496,88 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// /// The matrix to multiply with. /// The result of the multiplication. - /// If the other matrix is . - /// If the result matrix is . - /// If this.Columns != other.Rows. - /// If the result matrix's dimensions are not the this.Rows x other.Columns. - public override void TransposeAndMultiply(Matrix other, Matrix result) + protected override void DoTransposeAndMultiply(Matrix other, Matrix result) + { + var diagonalOther = other as DiagonalMatrix; + var diagonalResult = result as DiagonalMatrix; + if (diagonalOther != null && diagonalResult != null) + { + var thisDataCopy = new double[diagonalResult._data.Length]; + var otherDataCopy = new double[diagonalResult._data.Length]; + Array.Copy(_data, thisDataCopy, (diagonalResult._data.Length > _data.Length) ? _data.Length : diagonalResult._data.Length); + Array.Copy(diagonalOther._data, otherDataCopy, (diagonalResult._data.Length > diagonalOther._data.Length) ? diagonalOther._data.Length : diagonalResult._data.Length); + Control.LinearAlgebraProvider.PointWiseMultiplyArrays(thisDataCopy, otherDataCopy, diagonalResult._data); + return; + } + + var denseOther = other.Storage as DenseColumnMajorMatrixStorage; + if (denseOther != null) + { + var dense = denseOther.Data; + var diagonal = _data; + var d = Math.Min(denseOther.ColumnCount, RowCount); + if (d < RowCount) + { + result.ClearSubMatrix(denseOther.ColumnCount, RowCount - denseOther.ColumnCount, 0, denseOther.RowCount); + } + int index = 0; + for (int j = 0; j < d; j++) + { + for (int i = 0; i < denseOther.RowCount; i++) + { + result.At(j, i, dense[index]*diagonal[j]); + index++; + } + } + return; + } + + base.DoTransposeAndMultiply(other, result); + } + + /// + /// Multiplies the transpose of this matrix with another matrix and places the results into the result matrix. + /// + /// The matrix to multiply with. + /// The result of the multiplication. + protected override void DoTransposeThisAndMultiply(Matrix other, Matrix result) { - var otherDiagonal = other as DiagonalMatrix; - var resultDiagonal = result as DiagonalMatrix; + var diagonalOther = other as DiagonalMatrix; + var diagonalResult = result as DiagonalMatrix; + if (diagonalOther != null && diagonalResult != null) + { + var thisDataCopy = new double[diagonalResult._data.Length]; + var otherDataCopy = new double[diagonalResult._data.Length]; + Array.Copy(_data, thisDataCopy, (diagonalResult._data.Length > _data.Length) ? _data.Length : diagonalResult._data.Length); + Array.Copy(diagonalOther._data, otherDataCopy, (diagonalResult._data.Length > diagonalOther._data.Length) ? diagonalOther._data.Length : diagonalResult._data.Length); + Control.LinearAlgebraProvider.PointWiseMultiplyArrays(thisDataCopy, otherDataCopy, diagonalResult._data); + return; + } - if (otherDiagonal == null || resultDiagonal == null) + var denseOther = other.Storage as DenseColumnMajorMatrixStorage; + if (denseOther != null) { - base.TransposeAndMultiply(other, result); + var dense = denseOther.Data; + var diagonal = _data; + var d = Math.Min(denseOther.RowCount, ColumnCount); + if (d < ColumnCount) + { + result.ClearSubMatrix(denseOther.RowCount, ColumnCount - denseOther.RowCount, 0, denseOther.ColumnCount); + } + int index = 0; + for (int i = 0; i < denseOther.ColumnCount; i++) + { + for (int j = 0; j < d; j++) + { + result.At(j, i, dense[index]*diagonal[j]); + index++; + } + index += (denseOther.RowCount - d); + } return; } - Multiply(otherDiagonal.Transpose(), result); + base.DoTransposeThisAndMultiply(other, result); } /// diff --git a/src/Numerics/LinearAlgebra/Matrix.Arithmetic.cs b/src/Numerics/LinearAlgebra/Matrix.Arithmetic.cs index bb5fcebf..bd36f09a 100644 --- a/src/Numerics/LinearAlgebra/Matrix.Arithmetic.cs +++ b/src/Numerics/LinearAlgebra/Matrix.Arithmetic.cs @@ -204,7 +204,7 @@ namespace MathNet.Numerics.LinearAlgebra return Clone(); } - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameType(this, RowCount, ColumnCount); DoAdd(scalar, result); return result; } @@ -237,14 +237,14 @@ namespace MathNet.Numerics.LinearAlgebra /// The matrix to add to this matrix. /// The result of the addition. /// If the two matrices don't have the same dimensions. - public virtual Matrix Add(Matrix other) + public Matrix Add(Matrix other) { if (other.RowCount != RowCount || other.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, other); } - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameType(this, other, RowCount, ColumnCount); DoAdd(other, result); return result; } @@ -282,7 +282,7 @@ namespace MathNet.Numerics.LinearAlgebra return Clone(); } - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameType(this, RowCount, ColumnCount); DoSubtract(scalar, result); return result; } @@ -316,7 +316,7 @@ namespace MathNet.Numerics.LinearAlgebra /// A new matrix containing the subtraction of the scalar and this matrix. public Matrix SubtractFrom(T scalar) { - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameType(this, RowCount, ColumnCount); DoSubtractFrom(scalar, result); return result; } @@ -343,14 +343,14 @@ namespace MathNet.Numerics.LinearAlgebra /// The matrix to subtract. /// The result of the subtraction. /// If the two matrices don't have the same dimensions. - public virtual Matrix Subtract(Matrix other) + public Matrix Subtract(Matrix other) { if (other.RowCount != RowCount || other.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, other); } - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameType(this, other, RowCount, ColumnCount); DoSubtract(other, result); return result; } @@ -633,7 +633,7 @@ namespace MathNet.Numerics.LinearAlgebra /// The result of the multiplication. /// If this.Columns != other.Rows. /// If the result matrix's dimensions are not the this.Rows x other.Columns. - public virtual void Multiply(Matrix other, Matrix result) + public void Multiply(Matrix other, Matrix result) { if (ColumnCount != other.RowCount || result.RowCount != RowCount || result.ColumnCount != other.ColumnCount) { @@ -642,7 +642,7 @@ namespace MathNet.Numerics.LinearAlgebra if (ReferenceEquals(this, result) || ReferenceEquals(other, result)) { - var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); + var tmp = Build.SameType(result, result.RowCount, result.ColumnCount); DoMultiply(other, tmp); tmp.CopyTo(result); } @@ -658,14 +658,14 @@ namespace MathNet.Numerics.LinearAlgebra /// The matrix to multiply with. /// If this.Columns != other.Rows. /// The result of the multiplication. - public virtual Matrix Multiply(Matrix other) + public Matrix Multiply(Matrix other) { if (ColumnCount != other.RowCount) { throw DimensionsDontMatch(this, other); } - var result = CreateMatrix(RowCount, other.ColumnCount); + var result = Build.SameType(this, other, RowCount, other.ColumnCount); DoMultiply(other, result); return result; } @@ -686,7 +686,7 @@ namespace MathNet.Numerics.LinearAlgebra if (ReferenceEquals(this, result) || ReferenceEquals(other, result)) { - var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); + var tmp = Build.SameType(result, result.RowCount, result.ColumnCount); DoTransposeAndMultiply(other, tmp); tmp.CopyTo(result); } @@ -709,7 +709,7 @@ namespace MathNet.Numerics.LinearAlgebra throw DimensionsDontMatch(this, other); } - var result = CreateMatrix(RowCount, other.RowCount); + var result = Build.SameType(this, other, RowCount, other.RowCount); DoTransposeAndMultiply(other, result); return result; } @@ -727,9 +727,9 @@ namespace MathNet.Numerics.LinearAlgebra throw DimensionsDontMatch(this, rightSide, "rightSide"); } - var ret = CreateVector(ColumnCount); - DoTransposeThisAndMultiply(rightSide, ret); - return ret; + var result = CreateVector(ColumnCount); + DoTransposeThisAndMultiply(rightSide, result); + return result; } /// @@ -779,7 +779,7 @@ namespace MathNet.Numerics.LinearAlgebra if (ReferenceEquals(this, result) || ReferenceEquals(other, result)) { - var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); + var tmp = Build.SameType(result, result.RowCount, result.ColumnCount); DoTransposeThisAndMultiply(other, tmp); tmp.CopyTo(result); } @@ -802,7 +802,7 @@ namespace MathNet.Numerics.LinearAlgebra throw DimensionsDontMatch(this, other); } - var result = CreateMatrix(ColumnCount, other.ColumnCount); + var result = Build.SameType(this, other, ColumnCount, other.ColumnCount); DoTransposeThisAndMultiply(other, result); return result; } diff --git a/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs index 42d5b944..381a3f41 100644 --- a/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs +++ b/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs @@ -188,39 +188,6 @@ namespace MathNet.Numerics.LinearAlgebra.Single i => (float) distribution.Sample())); } - /// - /// Adds another matrix to this matrix. - /// - /// The matrix to add to this matrix. - /// The result of the addition. - /// If the other matrix is . - /// If the two matrices don't have the same dimensions. - public override Matrix Add(Matrix other) - { - if (other == null) - { - throw new ArgumentNullException("other"); - } - - if (other.RowCount != RowCount || other.ColumnCount != ColumnCount) - { - throw DimensionsDontMatch(this, other, "other"); - } - - Matrix result; - if (other is DiagonalMatrix) - { - result = new DiagonalMatrix(RowCount, ColumnCount); - } - else - { - result = new DenseMatrix(RowCount, ColumnCount); - } - - Add(other, result); - return result; - } - /// /// Adds another matrix to this matrix. /// @@ -243,39 +210,6 @@ namespace MathNet.Numerics.LinearAlgebra.Single } } - /// - /// Subtracts another matrix from this matrix. - /// - /// The matrix to subtract. - /// The result of the subtraction. - /// If the other matrix is . - /// If the two matrices don't have the same dimensions. - public override Matrix Subtract(Matrix other) - { - if (other == null) - { - throw new ArgumentNullException("other"); - } - - if (other.RowCount != RowCount || other.ColumnCount != ColumnCount) - { - throw DimensionsDontMatch(this, other, "other"); - } - - Matrix result; - if (other is DiagonalMatrix) - { - result = new DiagonalMatrix(RowCount, ColumnCount); - } - else - { - result = new DenseMatrix(RowCount, ColumnCount); - } - - Subtract(other, result); - return result; - } - /// /// Subtracts another matrix from this matrix. /// @@ -392,67 +326,44 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// /// The matrix to multiply with. /// The result of the multiplication. - /// If the other matrix is . - /// If the result matrix is . - /// If this.Columns != other.Rows. - /// If the result matrix's dimensions are not the this.Rows x other.Columns. - public override void Multiply(Matrix other, Matrix result) - { - if (other == null) - { - throw new ArgumentNullException("other"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (ColumnCount != other.RowCount || result.RowCount != RowCount || result.ColumnCount != other.ColumnCount) - { - throw DimensionsDontMatch(this, other, result); - } - - var m = other as DiagonalMatrix; - var r = result as DiagonalMatrix; - - if (m == null || r == null) - { - base.Multiply(other, result); - } - else - { - var thisDataCopy = new float[r._data.Length]; - var otherDataCopy = new float[r._data.Length]; - Buffer.BlockCopy(_data, 0, thisDataCopy, 0, (r._data.Length > _data.Length) ? _data.Length * Constants.SizeOfFloat : r._data.Length * Constants.SizeOfFloat); - Buffer.BlockCopy(m._data, 0, otherDataCopy, 0, (r._data.Length > m._data.Length) ? m._data.Length * Constants.SizeOfFloat : r._data.Length * Constants.SizeOfFloat); - - Control.LinearAlgebraProvider.PointWiseMultiplyArrays(thisDataCopy, otherDataCopy, r._data); - } - } - - /// - /// Multiplies this matrix with another matrix and returns the result. - /// - /// The matrix to multiply with. - /// If this.Columns != other.Rows. - /// If the other matrix is . - /// The result of multiplication. - public override Matrix Multiply(Matrix other) + protected override void DoMultiply(Matrix other, Matrix result) { - if (other == null) + var diagonalOther = other as DiagonalMatrix; + var diagonalResult = result as DiagonalMatrix; + if (diagonalOther != null && diagonalResult != null) { - throw new ArgumentNullException("other"); + var thisDataCopy = new float[diagonalResult._data.Length]; + var otherDataCopy = new float[diagonalResult._data.Length]; + Array.Copy(_data, thisDataCopy, (diagonalResult._data.Length > _data.Length) ? _data.Length : diagonalResult._data.Length); + Array.Copy(diagonalOther._data, otherDataCopy, (diagonalResult._data.Length > diagonalOther._data.Length) ? diagonalOther._data.Length : diagonalResult._data.Length); + Control.LinearAlgebraProvider.PointWiseMultiplyArrays(thisDataCopy, otherDataCopy, diagonalResult._data); + return; } - if (ColumnCount != other.RowCount) + var denseOther = other.Storage as DenseColumnMajorMatrixStorage; + if (denseOther != null) { - throw DimensionsDontMatch(this, other); + var dense = denseOther.Data; + var diagonal = _data; + var d = Math.Min(denseOther.RowCount, RowCount); + if (d < RowCount) + { + result.ClearSubMatrix(denseOther.RowCount, RowCount - denseOther.RowCount, 0, denseOther.ColumnCount); + } + int index = 0; + for (int i = 0; i < denseOther.ColumnCount; i++) + { + for (int j = 0; j < d; j++) + { + result.At(j, i, dense[index]*diagonal[j]); + index++; + } + index += (denseOther.RowCount - d); + } + return; } - var result = other.CreateMatrix(RowCount, other.ColumnCount); - Multiply(other, result); - return result; + base.DoMultiply(other, result); } /// @@ -585,22 +496,88 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// /// The matrix to multiply with. /// The result of the multiplication. - /// If the other matrix is . - /// If the result matrix is . - /// If this.Columns != other.Rows. - /// If the result matrix's dimensions are not the this.Rows x other.Columns. - public override void TransposeAndMultiply(Matrix other, Matrix result) + protected override void DoTransposeAndMultiply(Matrix other, Matrix result) + { + var diagonalOther = other as DiagonalMatrix; + var diagonalResult = result as DiagonalMatrix; + if (diagonalOther != null && diagonalResult != null) + { + var thisDataCopy = new float[diagonalResult._data.Length]; + var otherDataCopy = new float[diagonalResult._data.Length]; + Array.Copy(_data, thisDataCopy, (diagonalResult._data.Length > _data.Length) ? _data.Length : diagonalResult._data.Length); + Array.Copy(diagonalOther._data, otherDataCopy, (diagonalResult._data.Length > diagonalOther._data.Length) ? diagonalOther._data.Length : diagonalResult._data.Length); + Control.LinearAlgebraProvider.PointWiseMultiplyArrays(thisDataCopy, otherDataCopy, diagonalResult._data); + return; + } + + var denseOther = other.Storage as DenseColumnMajorMatrixStorage; + if (denseOther != null) + { + var dense = denseOther.Data; + var diagonal = _data; + var d = Math.Min(denseOther.ColumnCount, RowCount); + if (d < RowCount) + { + result.ClearSubMatrix(denseOther.ColumnCount, RowCount - denseOther.ColumnCount, 0, denseOther.RowCount); + } + int index = 0; + for (int j = 0; j < d; j++) + { + for (int i = 0; i < denseOther.RowCount; i++) + { + result.At(j, i, dense[index]*diagonal[j]); + index++; + } + } + return; + } + + base.DoTransposeAndMultiply(other, result); + } + + /// + /// Multiplies the transpose of this matrix with another matrix and places the results into the result matrix. + /// + /// The matrix to multiply with. + /// The result of the multiplication. + protected override void DoTransposeThisAndMultiply(Matrix other, Matrix result) { - var otherDiagonal = other as DiagonalMatrix; - var resultDiagonal = result as DiagonalMatrix; + var diagonalOther = other as DiagonalMatrix; + var diagonalResult = result as DiagonalMatrix; + if (diagonalOther != null && diagonalResult != null) + { + var thisDataCopy = new float[diagonalResult._data.Length]; + var otherDataCopy = new float[diagonalResult._data.Length]; + Array.Copy(_data, thisDataCopy, (diagonalResult._data.Length > _data.Length) ? _data.Length : diagonalResult._data.Length); + Array.Copy(diagonalOther._data, otherDataCopy, (diagonalResult._data.Length > diagonalOther._data.Length) ? diagonalOther._data.Length : diagonalResult._data.Length); + Control.LinearAlgebraProvider.PointWiseMultiplyArrays(thisDataCopy, otherDataCopy, diagonalResult._data); + return; + } - if (otherDiagonal == null || resultDiagonal == null) + var denseOther = other.Storage as DenseColumnMajorMatrixStorage; + if (denseOther != null) { - base.TransposeAndMultiply(other, result); + var dense = denseOther.Data; + var diagonal = _data; + var d = Math.Min(denseOther.RowCount, ColumnCount); + if (d < ColumnCount) + { + result.ClearSubMatrix(denseOther.RowCount, ColumnCount - denseOther.RowCount, 0, denseOther.ColumnCount); + } + int index = 0; + for (int i = 0; i < denseOther.ColumnCount; i++) + { + for (int j = 0; j < d; j++) + { + result.At(j, i, dense[index]*diagonal[j]); + index++; + } + index += (denseOther.RowCount - d); + } return; } - Multiply(otherDiagonal.Transpose(), result); + base.DoTransposeThisAndMultiply(other, result); } /// diff --git a/src/UnitTests/LinearAlgebraTests/Complex/DiagonalMatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Complex/DiagonalMatrixTests.cs index 55de88b8..928e5195 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex/DiagonalMatrixTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex/DiagonalMatrixTests.cs @@ -436,5 +436,56 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex Assert.IsTrue(tall.TransposeThisAndMultiply(Matrix.Build.Diagonal(8, 10, 2d)).Equals(tall.Transpose().Multiply(2d).Append(Matrix.Build.Dense(3, 2)))); Assert.IsTrue(tall.TransposeThisAndMultiply(Matrix.Build.Diagonal(8, 2, 2d)).Equals(tall.Transpose().Multiply(2d).SubMatrix(0, 3, 0, 2))); } + + [Test] + public void DiagonalDenseMatrixMultiply() + { + var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); + Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); + + var wide = Matrix.Build.Random(3, 8, dist); + Assert.IsTrue((Matrix.Build.DiagonalIdentity(3).Multiply(2d)*wide).Equals(wide.Multiply(2d))); + Assert.IsTrue((Matrix.Build.Diagonal(5, 3, 2d)*wide).Equals(wide.Multiply(2d).Stack(Matrix.Build.Dense(2, 8)))); + Assert.IsTrue((Matrix.Build.Diagonal(2, 3, 2d)*wide).Equals(wide.Multiply(2d).SubMatrix(0, 2, 0, 8))); + + var tall = Matrix.Build.Random(8, 3, dist); + Assert.IsTrue((Matrix.Build.DiagonalIdentity(8).Multiply(2d)*tall).Equals(tall.Multiply(2d))); + Assert.IsTrue((Matrix.Build.Diagonal(10, 8, 2d)*tall).Equals(tall.Multiply(2d).Stack(Matrix.Build.Dense(2, 3)))); + Assert.IsTrue((Matrix.Build.Diagonal(2, 8, 2d)*tall).Equals(tall.Multiply(2d).SubMatrix(0, 2, 0, 3))); + } + + [Test] + public void DiagonalDenseMatrixTransposeAndMultiply() + { + var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); + Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); + + var tall = Matrix.Build.Random(8, 3, dist); + Assert.IsTrue(Matrix.Build.DiagonalIdentity(3).Multiply(2d).TransposeAndMultiply(tall).Equals(tall.Multiply(2d).Transpose())); + Assert.IsTrue(Matrix.Build.Diagonal(5, 3, 2d).TransposeAndMultiply(tall).Equals(tall.Multiply(2d).Append(Matrix.Build.Dense(8, 2)).Transpose())); + Assert.IsTrue(Matrix.Build.Diagonal(2, 3, 2d).TransposeAndMultiply(tall).Equals(tall.Multiply(2d).SubMatrix(0, 8, 0, 2).Transpose())); + + var wide = Matrix.Build.Random(3, 8, dist); + Assert.IsTrue(Matrix.Build.DiagonalIdentity(8).Multiply(2d).TransposeAndMultiply(wide).Equals(wide.Multiply(2d).Transpose())); + Assert.IsTrue(Matrix.Build.Diagonal(10, 8, 2d).TransposeAndMultiply(wide).Equals(wide.Multiply(2d).Append(Matrix.Build.Dense(3, 2)).Transpose())); + Assert.IsTrue(Matrix.Build.Diagonal(2, 8, 2d).TransposeAndMultiply(wide).Equals(wide.Multiply(2d).SubMatrix(0, 3, 0, 2).Transpose())); + } + + [Test] + public void DiagonalDenseMatrixTransposeThisAndMultiply() + { + var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); + Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); + + var wide = Matrix.Build.Random(3, 8, dist); + Assert.IsTrue((Matrix.Build.DiagonalIdentity(3).Multiply(2d).TransposeThisAndMultiply(wide)).Equals(wide.Multiply(2d))); + Assert.IsTrue((Matrix.Build.Diagonal(3, 5, 2d).TransposeThisAndMultiply(wide)).Equals(wide.Multiply(2d).Stack(Matrix.Build.Dense(2, 8)))); + Assert.IsTrue((Matrix.Build.Diagonal(3, 2, 2d).TransposeThisAndMultiply(wide)).Equals(wide.Multiply(2d).SubMatrix(0, 2, 0, 8))); + + var tall = Matrix.Build.Random(8, 3, dist); + Assert.IsTrue((Matrix.Build.DiagonalIdentity(8).Multiply(2d).TransposeThisAndMultiply(tall)).Equals(tall.Multiply(2d))); + Assert.IsTrue((Matrix.Build.Diagonal(8, 10, 2d).TransposeThisAndMultiply(tall)).Equals(tall.Multiply(2d).Stack(Matrix.Build.Dense(2, 3)))); + Assert.IsTrue((Matrix.Build.Diagonal(8, 2, 2d).TransposeThisAndMultiply(tall)).Equals(tall.Multiply(2d).SubMatrix(0, 2, 0, 3))); + } } } diff --git a/src/UnitTests/LinearAlgebraTests/Complex32/DiagonalMatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Complex32/DiagonalMatrixTests.cs index c4423a74..9afe6de7 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex32/DiagonalMatrixTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex32/DiagonalMatrixTests.cs @@ -432,5 +432,56 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32 Assert.IsTrue(tall.TransposeThisAndMultiply(Matrix.Build.Diagonal(8, 10, 2f)).Equals(tall.Transpose().Multiply(2f).Append(Matrix.Build.Dense(3, 2)))); Assert.IsTrue(tall.TransposeThisAndMultiply(Matrix.Build.Diagonal(8, 2, 2f)).Equals(tall.Transpose().Multiply(2f).SubMatrix(0, 3, 0, 2))); } + + [Test] + public void DiagonalDenseMatrixMultiply() + { + var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); + Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); + + var wide = Matrix.Build.Random(3, 8, dist); + Assert.IsTrue((Matrix.Build.DiagonalIdentity(3).Multiply(2f)*wide).Equals(wide.Multiply(2f))); + Assert.IsTrue((Matrix.Build.Diagonal(5, 3, 2f)*wide).Equals(wide.Multiply(2f).Stack(Matrix.Build.Dense(2, 8)))); + Assert.IsTrue((Matrix.Build.Diagonal(2, 3, 2f)*wide).Equals(wide.Multiply(2f).SubMatrix(0, 2, 0, 8))); + + var tall = Matrix.Build.Random(8, 3, dist); + Assert.IsTrue((Matrix.Build.DiagonalIdentity(8).Multiply(2f)*tall).Equals(tall.Multiply(2f))); + Assert.IsTrue((Matrix.Build.Diagonal(10, 8, 2f)*tall).Equals(tall.Multiply(2f).Stack(Matrix.Build.Dense(2, 3)))); + Assert.IsTrue((Matrix.Build.Diagonal(2, 8, 2f)*tall).Equals(tall.Multiply(2f).SubMatrix(0, 2, 0, 3))); + } + + [Test] + public void DiagonalDenseMatrixTransposeAndMultiply() + { + var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); + Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); + + var tall = Matrix.Build.Random(8, 3, dist); + Assert.IsTrue(Matrix.Build.DiagonalIdentity(3).Multiply(2f).TransposeAndMultiply(tall).Equals(tall.Multiply(2f).Transpose())); + Assert.IsTrue(Matrix.Build.Diagonal(5, 3, 2f).TransposeAndMultiply(tall).Equals(tall.Multiply(2f).Append(Matrix.Build.Dense(8, 2)).Transpose())); + Assert.IsTrue(Matrix.Build.Diagonal(2, 3, 2f).TransposeAndMultiply(tall).Equals(tall.Multiply(2f).SubMatrix(0, 8, 0, 2).Transpose())); + + var wide = Matrix.Build.Random(3, 8, dist); + Assert.IsTrue(Matrix.Build.DiagonalIdentity(8).Multiply(2f).TransposeAndMultiply(wide).Equals(wide.Multiply(2f).Transpose())); + Assert.IsTrue(Matrix.Build.Diagonal(10, 8, 2f).TransposeAndMultiply(wide).Equals(wide.Multiply(2f).Append(Matrix.Build.Dense(3, 2)).Transpose())); + Assert.IsTrue(Matrix.Build.Diagonal(2, 8, 2f).TransposeAndMultiply(wide).Equals(wide.Multiply(2f).SubMatrix(0, 3, 0, 2).Transpose())); + } + + [Test] + public void DiagonalDenseMatrixTransposeThisAndMultiply() + { + var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); + Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); + + var wide = Matrix.Build.Random(3, 8, dist); + Assert.IsTrue((Matrix.Build.DiagonalIdentity(3).Multiply(2f).TransposeThisAndMultiply(wide)).Equals(wide.Multiply(2f))); + Assert.IsTrue((Matrix.Build.Diagonal(3, 5, 2f).TransposeThisAndMultiply(wide)).Equals(wide.Multiply(2f).Stack(Matrix.Build.Dense(2, 8)))); + Assert.IsTrue((Matrix.Build.Diagonal(3, 2, 2f).TransposeThisAndMultiply(wide)).Equals(wide.Multiply(2f).SubMatrix(0, 2, 0, 8))); + + var tall = Matrix.Build.Random(8, 3, dist); + Assert.IsTrue((Matrix.Build.DiagonalIdentity(8).Multiply(2f).TransposeThisAndMultiply(tall)).Equals(tall.Multiply(2f))); + Assert.IsTrue((Matrix.Build.Diagonal(8, 10, 2f).TransposeThisAndMultiply(tall)).Equals(tall.Multiply(2f).Stack(Matrix.Build.Dense(2, 3)))); + Assert.IsTrue((Matrix.Build.Diagonal(8, 2, 2f).TransposeThisAndMultiply(tall)).Equals(tall.Multiply(2f).SubMatrix(0, 2, 0, 3))); + } } } diff --git a/src/UnitTests/LinearAlgebraTests/Double/DiagonalMatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Double/DiagonalMatrixTests.cs index bb27d8b5..7bf5b72a 100644 --- a/src/UnitTests/LinearAlgebraTests/Double/DiagonalMatrixTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Double/DiagonalMatrixTests.cs @@ -463,5 +463,56 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double Assert.IsTrue(tall.TransposeThisAndMultiply(Matrix.Build.Diagonal(8, 10, 2d)).Equals(tall.Transpose().Multiply(2d).Append(Matrix.Build.Dense(3, 2)))); Assert.IsTrue(tall.TransposeThisAndMultiply(Matrix.Build.Diagonal(8, 2, 2d)).Equals(tall.Transpose().Multiply(2d).SubMatrix(0, 3, 0, 2))); } + + [Test] + public void DiagonalDenseMatrixMultiply() + { + var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); + Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); + + var wide = Matrix.Build.Random(3, 8, dist); + Assert.IsTrue((Matrix.Build.DiagonalIdentity(3).Multiply(2d)*wide).Equals(wide.Multiply(2d))); + Assert.IsTrue((Matrix.Build.Diagonal(5, 3, 2d)*wide).Equals(wide.Multiply(2d).Stack(Matrix.Build.Dense(2, 8)))); + Assert.IsTrue((Matrix.Build.Diagonal(2, 3, 2d)*wide).Equals(wide.Multiply(2d).SubMatrix(0, 2, 0, 8))); + + var tall = Matrix.Build.Random(8, 3, dist); + Assert.IsTrue((Matrix.Build.DiagonalIdentity(8).Multiply(2d)*tall).Equals(tall.Multiply(2d))); + Assert.IsTrue((Matrix.Build.Diagonal(10, 8, 2d)*tall).Equals(tall.Multiply(2d).Stack(Matrix.Build.Dense(2, 3)))); + Assert.IsTrue((Matrix.Build.Diagonal(2, 8, 2d)*tall).Equals(tall.Multiply(2d).SubMatrix(0, 2, 0, 3))); + } + + [Test] + public void DiagonalDenseMatrixTransposeAndMultiply() + { + var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); + Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); + + var tall = Matrix.Build.Random(8, 3, dist); + Assert.IsTrue(Matrix.Build.DiagonalIdentity(3).Multiply(2d).TransposeAndMultiply(tall).Equals(tall.Multiply(2d).Transpose())); + Assert.IsTrue(Matrix.Build.Diagonal(5, 3, 2d).TransposeAndMultiply(tall).Equals(tall.Multiply(2d).Append(Matrix.Build.Dense(8, 2)).Transpose())); + Assert.IsTrue(Matrix.Build.Diagonal(2, 3, 2d).TransposeAndMultiply(tall).Equals(tall.Multiply(2d).SubMatrix(0, 8, 0, 2).Transpose())); + + var wide = Matrix.Build.Random(3, 8, dist); + Assert.IsTrue(Matrix.Build.DiagonalIdentity(8).Multiply(2d).TransposeAndMultiply(wide).Equals(wide.Multiply(2d).Transpose())); + Assert.IsTrue(Matrix.Build.Diagonal(10, 8, 2d).TransposeAndMultiply(wide).Equals(wide.Multiply(2d).Append(Matrix.Build.Dense(3, 2)).Transpose())); + Assert.IsTrue(Matrix.Build.Diagonal(2, 8, 2d).TransposeAndMultiply(wide).Equals(wide.Multiply(2d).SubMatrix(0, 3, 0, 2).Transpose())); + } + + [Test] + public void DiagonalDenseMatrixTransposeThisAndMultiply() + { + var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); + Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); + + var wide = Matrix.Build.Random(3, 8, dist); + Assert.IsTrue((Matrix.Build.DiagonalIdentity(3).Multiply(2d).TransposeThisAndMultiply(wide)).Equals(wide.Multiply(2d))); + Assert.IsTrue((Matrix.Build.Diagonal(3, 5, 2d).TransposeThisAndMultiply(wide)).Equals(wide.Multiply(2d).Stack(Matrix.Build.Dense(2, 8)))); + Assert.IsTrue((Matrix.Build.Diagonal(3, 2, 2d).TransposeThisAndMultiply(wide)).Equals(wide.Multiply(2d).SubMatrix(0, 2, 0, 8))); + + var tall = Matrix.Build.Random(8, 3, dist); + Assert.IsTrue((Matrix.Build.DiagonalIdentity(8).Multiply(2d).TransposeThisAndMultiply(tall)).Equals(tall.Multiply(2d))); + Assert.IsTrue((Matrix.Build.Diagonal(8, 10, 2d).TransposeThisAndMultiply(tall)).Equals(tall.Multiply(2d).Stack(Matrix.Build.Dense(2, 3)))); + Assert.IsTrue((Matrix.Build.Diagonal(8, 2, 2d).TransposeThisAndMultiply(tall)).Equals(tall.Multiply(2d).SubMatrix(0, 2, 0, 3))); + } } } diff --git a/src/UnitTests/LinearAlgebraTests/Single/DiagonalMatrixTests.cs b/src/UnitTests/LinearAlgebraTests/Single/DiagonalMatrixTests.cs index 6c253abd..118cc55e 100644 --- a/src/UnitTests/LinearAlgebraTests/Single/DiagonalMatrixTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Single/DiagonalMatrixTests.cs @@ -430,5 +430,56 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single Assert.IsTrue(tall.TransposeThisAndMultiply(Matrix.Build.Diagonal(8, 10, 2f)).Equals(tall.Transpose().Multiply(2f).Append(Matrix.Build.Dense(3, 2)))); Assert.IsTrue(tall.TransposeThisAndMultiply(Matrix.Build.Diagonal(8, 2, 2f)).Equals(tall.Transpose().Multiply(2f).SubMatrix(0, 3, 0, 2))); } + + [Test] + public void DiagonalDenseMatrixMultiply() + { + var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); + Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); + + var wide = Matrix.Build.Random(3, 8, dist); + Assert.IsTrue((Matrix.Build.DiagonalIdentity(3).Multiply(2f)*wide).Equals(wide.Multiply(2f))); + Assert.IsTrue((Matrix.Build.Diagonal(5, 3, 2f)*wide).Equals(wide.Multiply(2f).Stack(Matrix.Build.Dense(2, 8)))); + Assert.IsTrue((Matrix.Build.Diagonal(2, 3, 2f)*wide).Equals(wide.Multiply(2f).SubMatrix(0, 2, 0, 8))); + + var tall = Matrix.Build.Random(8, 3, dist); + Assert.IsTrue((Matrix.Build.DiagonalIdentity(8).Multiply(2f)*tall).Equals(tall.Multiply(2f))); + Assert.IsTrue((Matrix.Build.Diagonal(10, 8, 2f)*tall).Equals(tall.Multiply(2f).Stack(Matrix.Build.Dense(2, 3)))); + Assert.IsTrue((Matrix.Build.Diagonal(2, 8, 2f)*tall).Equals(tall.Multiply(2f).SubMatrix(0, 2, 0, 3))); + } + + [Test] + public void DiagonalDenseMatrixTransposeAndMultiply() + { + var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); + Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); + + var tall = Matrix.Build.Random(8, 3, dist); + Assert.IsTrue(Matrix.Build.DiagonalIdentity(3).Multiply(2f).TransposeAndMultiply(tall).Equals(tall.Multiply(2f).Transpose())); + Assert.IsTrue(Matrix.Build.Diagonal(5, 3, 2f).TransposeAndMultiply(tall).Equals(tall.Multiply(2f).Append(Matrix.Build.Dense(8, 2)).Transpose())); + Assert.IsTrue(Matrix.Build.Diagonal(2, 3, 2f).TransposeAndMultiply(tall).Equals(tall.Multiply(2f).SubMatrix(0, 8, 0, 2).Transpose())); + + var wide = Matrix.Build.Random(3, 8, dist); + Assert.IsTrue(Matrix.Build.DiagonalIdentity(8).Multiply(2f).TransposeAndMultiply(wide).Equals(wide.Multiply(2f).Transpose())); + Assert.IsTrue(Matrix.Build.Diagonal(10, 8, 2f).TransposeAndMultiply(wide).Equals(wide.Multiply(2f).Append(Matrix.Build.Dense(3, 2)).Transpose())); + Assert.IsTrue(Matrix.Build.Diagonal(2, 8, 2f).TransposeAndMultiply(wide).Equals(wide.Multiply(2f).SubMatrix(0, 3, 0, 2).Transpose())); + } + + [Test] + public void DiagonalDenseMatrixTransposeThisAndMultiply() + { + var dist = new ContinuousUniform(-1.0, 1.0, new MersenneTwister()); + Assert.IsInstanceOf(Matrix.Build.DiagonalIdentity(3, 3)); + + var wide = Matrix.Build.Random(3, 8, dist); + Assert.IsTrue((Matrix.Build.DiagonalIdentity(3).Multiply(2f).TransposeThisAndMultiply(wide)).Equals(wide.Multiply(2f))); + Assert.IsTrue((Matrix.Build.Diagonal(3, 5, 2f).TransposeThisAndMultiply(wide)).Equals(wide.Multiply(2f).Stack(Matrix.Build.Dense(2, 8)))); + Assert.IsTrue((Matrix.Build.Diagonal(3, 2, 2f).TransposeThisAndMultiply(wide)).Equals(wide.Multiply(2f).SubMatrix(0, 2, 0, 8))); + + var tall = Matrix.Build.Random(8, 3, dist); + Assert.IsTrue((Matrix.Build.DiagonalIdentity(8).Multiply(2f).TransposeThisAndMultiply(tall)).Equals(tall.Multiply(2f))); + Assert.IsTrue((Matrix.Build.Diagonal(8, 10, 2f).TransposeThisAndMultiply(tall)).Equals(tall.Multiply(2f).Stack(Matrix.Build.Dense(2, 3)))); + Assert.IsTrue((Matrix.Build.Diagonal(8, 2, 2f).TransposeThisAndMultiply(tall)).Equals(tall.Multiply(2f).SubMatrix(0, 2, 0, 3))); + } } } From 6754045a568ef316ff6e4d00cc52764eaa320896 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sun, 1 Dec 2013 23:19:16 +0100 Subject: [PATCH 021/109] LA: diagonal matrix-vector Multiply LeftMultiply/TransposeThisAndMultiply --- .../LinearAlgebra/Complex/DiagonalMatrix.cs | 210 +++++++----------- .../LinearAlgebra/Complex32/DiagonalMatrix.cs | 210 +++++++----------- .../LinearAlgebra/Double/DiagonalMatrix.cs | 210 +++++++----------- .../LinearAlgebra/Matrix.Arithmetic.cs | 4 +- .../LinearAlgebra/Single/DiagonalMatrix.cs | 210 +++++++----------- 5 files changed, 342 insertions(+), 502 deletions(-) diff --git a/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs index d0e3781d..5d5cfbaf 100644 --- a/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs @@ -327,6 +327,36 @@ namespace MathNet.Numerics.LinearAlgebra.Complex } } + /// + /// Multiplies this matrix with a vector and places the results into the result vector. + /// + /// The vector to multiply with. + /// The result of the multiplication. + protected override void DoMultiply(Vector rightSide, Vector result) + { + var d = Math.Min(ColumnCount, RowCount); + if (d < RowCount) + { + result.ClearSubVector(ColumnCount, RowCount - ColumnCount); + } + + if (d == ColumnCount) + { + var denseOther = rightSide.Storage as DenseVectorStorage; + var denseResult = result.Storage as DenseVectorStorage; + if (denseOther != null && denseResult != null) + { + Control.LinearAlgebraProvider.PointWiseMultiplyArrays(_data, denseOther.Data, denseResult.Data); + return; + } + } + + for (var i = 0; i < d; i++) + { + result.At(i, _data[i]*rightSide.At(i)); + } + } + /// /// Multiplies this matrix with another matrix and places the results into the result matrix. /// @@ -372,131 +402,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex base.DoMultiply(other, result); } - /// - /// Multiplies this matrix with a vector and places the results into the result matrix. - /// - /// The vector to multiply with. - /// The result of the multiplication. - /// If is . - /// If is . - /// If result.Count != this.RowCount. - /// If this.ColumnCount != .Count. - public override void Multiply(Vector rightSide, Vector result) - { - if (rightSide == null) - { - throw new ArgumentNullException("rightSide"); - } - - if (ColumnCount != rightSide.Count) - { - throw DimensionsDontMatch(this, rightSide, "rightSide"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (RowCount != result.Count) - { - throw DimensionsDontMatch(this, result, "result"); - } - - if (ReferenceEquals(rightSide, result)) - { - var tmp = result.CreateVector(result.Count); - Multiply(rightSide, tmp); - tmp.CopyTo(result); - } - else - { - // Clear the result vector - result.Clear(); - - // Multiply the elements in the vector with the corresponding diagonal element in this. - for (var r = 0; r < _data.Length; r++) - { - result[r] = _data[r] * rightSide[r]; - } - } - } - - /// - /// Left multiply a matrix with a vector ( = vector * matrix ) and place the result in the result vector. - /// - /// The vector to multiply with. - /// The result of the multiplication. - /// If is . - /// If the result matrix is . - /// If result.Count != this.ColumnCount. - /// If this.RowCount != .Count. - public override void LeftMultiply(Vector leftSide, Vector result) - { - if (leftSide == null) - { - throw new ArgumentNullException("leftSide"); - } - - if (RowCount != leftSide.Count) - { - throw DimensionsDontMatch(this, leftSide, "leftSide"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (ColumnCount != result.Count) - { - throw DimensionsDontMatch(this, result, "result"); - } - - if (ReferenceEquals(leftSide, result)) - { - var tmp = result.CreateVector(result.Count); - LeftMultiply(leftSide, tmp); - tmp.CopyTo(result); - } - else - { - // Clear the result vector - result.Clear(); - - // Multiply the elements in the vector with the corresponding diagonal element in this. - for (var r = 0; r < _data.Length; r++) - { - result[r] = _data[r] * leftSide[r]; - } - } - } - - /// - /// Computes the determinant of this matrix. - /// - /// The determinant of this matrix. - public override Complex Determinant() - { - if (RowCount != ColumnCount) - { - throw new ArgumentException(Resources.ArgumentMatrixSquare); - } - - return _data.Aggregate(Complex.One, (current, t) => current * t); - } - - /// - /// Returns the elements of the diagonal in a . - /// - /// The elements of the diagonal. - /// For non-square matrices, the method returns Min(Rows, Columns) elements where - /// i == j (i is the row index, and j is the column index). - public override Vector Diagonal() - { - return new DenseVector(_data).Clone(); - } - /// /// Multiplies this matrix with transpose of another matrix and places the results into the result matrix. /// @@ -586,6 +491,61 @@ namespace MathNet.Numerics.LinearAlgebra.Complex base.DoTransposeThisAndMultiply(other, result); } + /// + /// Multiplies the transpose of this matrix with a vector and places the results into the result vector. + /// + /// The vector to multiply with. + /// The result of the multiplication. + protected override void DoTransposeThisAndMultiply(Vector rightSide, Vector result) + { + var d = Math.Min(ColumnCount, RowCount); + if (d < ColumnCount) + { + result.ClearSubVector(RowCount, ColumnCount - RowCount); + } + + if (d == RowCount) + { + var denseOther = rightSide.Storage as DenseVectorStorage; + var denseResult = result.Storage as DenseVectorStorage; + if (denseOther != null && denseResult != null) + { + Control.LinearAlgebraProvider.PointWiseMultiplyArrays(_data, denseOther.Data, denseResult.Data); + return; + } + } + + for (var i = 0; i < d; i++) + { + result.At(i, _data[i]*rightSide.At(i)); + } + } + + /// + /// Computes the determinant of this matrix. + /// + /// The determinant of this matrix. + public override Complex Determinant() + { + if (RowCount != ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixSquare); + } + + return _data.Aggregate(Complex.One, (current, t) => current * t); + } + + /// + /// Returns the elements of the diagonal in a . + /// + /// The elements of the diagonal. + /// For non-square matrices, the method returns Min(Rows, Columns) elements where + /// i == j (i is the row index, and j is the column index). + public override Vector Diagonal() + { + return new DenseVector(_data).Clone(); + } + /// /// Returns the transpose of this matrix. /// diff --git a/src/Numerics/LinearAlgebra/Complex32/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Complex32/DiagonalMatrix.cs index 91ccf693..4f3c3b97 100644 --- a/src/Numerics/LinearAlgebra/Complex32/DiagonalMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex32/DiagonalMatrix.cs @@ -320,6 +320,36 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 } } + /// + /// Multiplies this matrix with a vector and places the results into the result vector. + /// + /// The vector to multiply with. + /// The result of the multiplication. + protected override void DoMultiply(Vector rightSide, Vector result) + { + var d = Math.Min(ColumnCount, RowCount); + if (d < RowCount) + { + result.ClearSubVector(ColumnCount, RowCount - ColumnCount); + } + + if (d == ColumnCount) + { + var denseOther = rightSide.Storage as DenseVectorStorage; + var denseResult = result.Storage as DenseVectorStorage; + if (denseOther != null && denseResult != null) + { + Control.LinearAlgebraProvider.PointWiseMultiplyArrays(_data, denseOther.Data, denseResult.Data); + return; + } + } + + for (var i = 0; i < d; i++) + { + result.At(i, _data[i]*rightSide.At(i)); + } + } + /// /// Multiplies this matrix with another matrix and places the results into the result matrix. /// @@ -365,131 +395,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 base.DoMultiply(other, result); } - /// - /// Multiplies this matrix with a vector and places the results into the result matrix. - /// - /// The vector to multiply with. - /// The result of the multiplication. - /// If is . - /// If is . - /// If result.Count != this.RowCount. - /// If this.ColumnCount != .Count. - public override void Multiply(Vector rightSide, Vector result) - { - if (rightSide == null) - { - throw new ArgumentNullException("rightSide"); - } - - if (ColumnCount != rightSide.Count) - { - throw DimensionsDontMatch(this, rightSide, "rightSide"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (RowCount != result.Count) - { - throw DimensionsDontMatch(this, result, "result"); - } - - if (ReferenceEquals(rightSide, result)) - { - var tmp = result.CreateVector(result.Count); - Multiply(rightSide, tmp); - tmp.CopyTo(result); - } - else - { - // Clear the result vector - result.Clear(); - - // Multiply the elements in the vector with the corresponding diagonal element in this. - for (var r = 0; r < _data.Length; r++) - { - result[r] = _data[r] * rightSide[r]; - } - } - } - - /// - /// Left multiply a matrix with a vector ( = vector * matrix ) and place the result in the result vector. - /// - /// The vector to multiply with. - /// The result of the multiplication. - /// If is . - /// If the result matrix is . - /// If result.Count != this.ColumnCount. - /// If this.RowCount != .Count. - public override void LeftMultiply(Vector leftSide, Vector result) - { - if (leftSide == null) - { - throw new ArgumentNullException("leftSide"); - } - - if (RowCount != leftSide.Count) - { - throw DimensionsDontMatch(this, leftSide, "leftSide"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (ColumnCount != result.Count) - { - throw DimensionsDontMatch(this, result, "result"); - } - - if (ReferenceEquals(leftSide, result)) - { - var tmp = result.CreateVector(result.Count); - LeftMultiply(leftSide, tmp); - tmp.CopyTo(result); - } - else - { - // Clear the result vector - result.Clear(); - - // Multiply the elements in the vector with the corresponding diagonal element in this. - for (var r = 0; r < _data.Length; r++) - { - result[r] = _data[r] * leftSide[r]; - } - } - } - - /// - /// Computes the determinant of this matrix. - /// - /// The determinant of this matrix. - public override Complex32 Determinant() - { - if (RowCount != ColumnCount) - { - throw new ArgumentException(Resources.ArgumentMatrixSquare); - } - - return _data.Aggregate(Complex32.One, (current, t) => current * t); - } - - /// - /// Returns the elements of the diagonal in a . - /// - /// The elements of the diagonal. - /// For non-square matrices, the method returns Min(Rows, Columns) elements where - /// i == j (i is the row index, and j is the column index). - public override Vector Diagonal() - { - return new DenseVector(_data).Clone(); - } - /// /// Multiplies this matrix with transpose of another matrix and places the results into the result matrix. /// @@ -579,6 +484,61 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 base.DoTransposeThisAndMultiply(other, result); } + /// + /// Multiplies the transpose of this matrix with a vector and places the results into the result vector. + /// + /// The vector to multiply with. + /// The result of the multiplication. + protected override void DoTransposeThisAndMultiply(Vector rightSide, Vector result) + { + var d = Math.Min(ColumnCount, RowCount); + if (d < ColumnCount) + { + result.ClearSubVector(RowCount, ColumnCount - RowCount); + } + + if (d == RowCount) + { + var denseOther = rightSide.Storage as DenseVectorStorage; + var denseResult = result.Storage as DenseVectorStorage; + if (denseOther != null && denseResult != null) + { + Control.LinearAlgebraProvider.PointWiseMultiplyArrays(_data, denseOther.Data, denseResult.Data); + return; + } + } + + for (var i = 0; i < d; i++) + { + result.At(i, _data[i]*rightSide.At(i)); + } + } + + /// + /// Computes the determinant of this matrix. + /// + /// The determinant of this matrix. + public override Complex32 Determinant() + { + if (RowCount != ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixSquare); + } + + return _data.Aggregate(Complex32.One, (current, t) => current * t); + } + + /// + /// Returns the elements of the diagonal in a . + /// + /// The elements of the diagonal. + /// For non-square matrices, the method returns Min(Rows, Columns) elements where + /// i == j (i is the row index, and j is the column index). + public override Vector Diagonal() + { + return new DenseVector(_data).Clone(); + } + /// /// Returns the transpose of this matrix. /// diff --git a/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs index 2e0b8216..81ad1356 100644 --- a/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs +++ b/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs @@ -321,6 +321,36 @@ namespace MathNet.Numerics.LinearAlgebra.Double } } + /// + /// Multiplies this matrix with a vector and places the results into the result vector. + /// + /// The vector to multiply with. + /// The result of the multiplication. + protected override void DoMultiply(Vector rightSide, Vector result) + { + var d = Math.Min(ColumnCount, RowCount); + if (d < RowCount) + { + result.ClearSubVector(ColumnCount, RowCount - ColumnCount); + } + + if (d == ColumnCount) + { + var denseOther = rightSide.Storage as DenseVectorStorage; + var denseResult = result.Storage as DenseVectorStorage; + if (denseOther != null && denseResult != null) + { + Control.LinearAlgebraProvider.PointWiseMultiplyArrays(_data, denseOther.Data, denseResult.Data); + return; + } + } + + for (var i = 0; i < d; i++) + { + result.At(i, _data[i]*rightSide.At(i)); + } + } + /// /// Multiplies this matrix with another matrix and places the results into the result matrix. /// @@ -366,131 +396,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double base.DoMultiply(other, result); } - /// - /// Multiplies this matrix with a vector and places the results into the result matrix. - /// - /// The vector to multiply with. - /// The result of the multiplication. - /// If is . - /// If is . - /// If result.Count != this.RowCount. - /// If this.ColumnCount != .Count. - public override void Multiply(Vector rightSide, Vector result) - { - if (rightSide == null) - { - throw new ArgumentNullException("rightSide"); - } - - if (ColumnCount != rightSide.Count) - { - throw DimensionsDontMatch(this, rightSide); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (RowCount != result.Count) - { - throw DimensionsDontMatch(this, result, "result"); - } - - if (ReferenceEquals(rightSide, result)) - { - var tmp = result.CreateVector(result.Count); - Multiply(rightSide, tmp); - tmp.CopyTo(result); - } - else - { - // Clear the result vector - result.Clear(); - - // Multiply the elements in the vector with the corresponding diagonal element in this. - for (var r = 0; r < _data.Length; r++) - { - result[r] = _data[r] * rightSide[r]; - } - } - } - - /// - /// Left multiply a matrix with a vector ( = vector * matrix ) and place the result in the result vector. - /// - /// The vector to multiply with. - /// The result of the multiplication. - /// If is . - /// If the result matrix is . - /// If result.Count != this.ColumnCount. - /// If this.RowCount != .Count. - public override void LeftMultiply(Vector leftSide, Vector result) - { - if (leftSide == null) - { - throw new ArgumentNullException("leftSide"); - } - - if (RowCount != leftSide.Count) - { - throw DimensionsDontMatch(this, leftSide, "leftSide"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (ColumnCount != result.Count) - { - throw DimensionsDontMatch(this, result, "result"); - } - - if (ReferenceEquals(leftSide, result)) - { - var tmp = result.CreateVector(result.Count); - LeftMultiply(leftSide, tmp); - tmp.CopyTo(result); - } - else - { - // Clear the result vector - result.Clear(); - - // Multiply the elements in the vector with the corresponding diagonal element in this. - for (var r = 0; r < _data.Length; r++) - { - result[r] = _data[r] * leftSide[r]; - } - } - } - - /// - /// Computes the determinant of this matrix. - /// - /// The determinant of this matrix. - public override double Determinant() - { - if (RowCount != ColumnCount) - { - throw new ArgumentException(Resources.ArgumentMatrixSquare); - } - - return _data.Aggregate(1.0, (current, t) => current * t); - } - - /// - /// Returns the elements of the diagonal in a . - /// - /// The elements of the diagonal. - /// For non-square matrices, the method returns Min(Rows, Columns) elements where - /// i == j (i is the row index, and j is the column index). - public override Vector Diagonal() - { - return new DenseVector(_data).Clone(); - } - /// /// Multiplies this matrix with transpose of another matrix and places the results into the result matrix. /// @@ -580,6 +485,61 @@ namespace MathNet.Numerics.LinearAlgebra.Double base.DoTransposeThisAndMultiply(other, result); } + /// + /// Multiplies the transpose of this matrix with a vector and places the results into the result vector. + /// + /// The vector to multiply with. + /// The result of the multiplication. + protected override void DoTransposeThisAndMultiply(Vector rightSide, Vector result) + { + var d = Math.Min(ColumnCount, RowCount); + if (d < ColumnCount) + { + result.ClearSubVector(RowCount, ColumnCount - RowCount); + } + + if (d == RowCount) + { + var denseOther = rightSide.Storage as DenseVectorStorage; + var denseResult = result.Storage as DenseVectorStorage; + if (denseOther != null && denseResult != null) + { + Control.LinearAlgebraProvider.PointWiseMultiplyArrays(_data, denseOther.Data, denseResult.Data); + return; + } + } + + for (var i = 0; i < d; i++) + { + result.At(i, _data[i]*rightSide.At(i)); + } + } + + /// + /// Computes the determinant of this matrix. + /// + /// The determinant of this matrix. + public override double Determinant() + { + if (RowCount != ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixSquare); + } + + return _data.Aggregate(1.0, (current, t) => current * t); + } + + /// + /// Returns the elements of the diagonal in a . + /// + /// The elements of the diagonal. + /// For non-square matrices, the method returns Min(Rows, Columns) elements where + /// i == j (i is the row index, and j is the column index). + public override Vector Diagonal() + { + return new DenseVector(_data).Clone(); + } + /// /// Returns the transpose of this matrix. /// diff --git a/src/Numerics/LinearAlgebra/Matrix.Arithmetic.cs b/src/Numerics/LinearAlgebra/Matrix.Arithmetic.cs index bd36f09a..3a976ecc 100644 --- a/src/Numerics/LinearAlgebra/Matrix.Arithmetic.cs +++ b/src/Numerics/LinearAlgebra/Matrix.Arithmetic.cs @@ -543,7 +543,7 @@ namespace MathNet.Numerics.LinearAlgebra /// The result of the multiplication. /// If result.Count != this.RowCount. /// If this.ColumnCount != .Count. - public virtual void Multiply(Vector rightSide, Vector result) + public void Multiply(Vector rightSide, Vector result) { if (ColumnCount != rightSide.Count) { @@ -592,7 +592,7 @@ namespace MathNet.Numerics.LinearAlgebra /// The result of the multiplication. /// If result.Count != this.ColumnCount. /// If this.RowCount != .Count. - public virtual void LeftMultiply(Vector leftSide, Vector result) + public void LeftMultiply(Vector leftSide, Vector result) { if (RowCount != leftSide.Count) { diff --git a/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs index 381a3f41..78288526 100644 --- a/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs +++ b/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs @@ -321,6 +321,36 @@ namespace MathNet.Numerics.LinearAlgebra.Single } } + /// + /// Multiplies this matrix with a vector and places the results into the result vector. + /// + /// The vector to multiply with. + /// The result of the multiplication. + protected override void DoMultiply(Vector rightSide, Vector result) + { + var d = Math.Min(ColumnCount, RowCount); + if (d < RowCount) + { + result.ClearSubVector(ColumnCount, RowCount - ColumnCount); + } + + if (d == ColumnCount) + { + var denseOther = rightSide.Storage as DenseVectorStorage; + var denseResult = result.Storage as DenseVectorStorage; + if (denseOther != null && denseResult != null) + { + Control.LinearAlgebraProvider.PointWiseMultiplyArrays(_data, denseOther.Data, denseResult.Data); + return; + } + } + + for (var i = 0; i < d; i++) + { + result.At(i, _data[i]*rightSide.At(i)); + } + } + /// /// Multiplies this matrix with another matrix and places the results into the result matrix. /// @@ -366,131 +396,6 @@ namespace MathNet.Numerics.LinearAlgebra.Single base.DoMultiply(other, result); } - /// - /// Multiplies this matrix with a vector and places the results into the result matrix. - /// - /// The vector to multiply with. - /// The result of the multiplication. - /// If is . - /// If is . - /// If result.Count != this.RowCount. - /// If this.ColumnCount != .Count. - public override void Multiply(Vector rightSide, Vector result) - { - if (rightSide == null) - { - throw new ArgumentNullException("rightSide"); - } - - if (ColumnCount != rightSide.Count) - { - throw DimensionsDontMatch(this, rightSide, "rightSide"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (RowCount != result.Count) - { - throw DimensionsDontMatch(this, result, "result"); - } - - if (ReferenceEquals(rightSide, result)) - { - var tmp = result.CreateVector(result.Count); - Multiply(rightSide, tmp); - tmp.CopyTo(result); - } - else - { - // Clear the result vector - result.Clear(); - - // Multiply the elements in the vector with the corresponding diagonal element in this. - for (var r = 0; r < _data.Length; r++) - { - result[r] = _data[r] * rightSide[r]; - } - } - } - - /// - /// Left multiply a matrix with a vector ( = vector * matrix ) and place the result in the result vector. - /// - /// The vector to multiply with. - /// The result of the multiplication. - /// If is . - /// If the result matrix is . - /// If result.Count != this.ColumnCount. - /// If this.RowCount != .Count. - public override void LeftMultiply(Vector leftSide, Vector result) - { - if (leftSide == null) - { - throw new ArgumentNullException("leftSide"); - } - - if (RowCount != leftSide.Count) - { - throw DimensionsDontMatch(this, leftSide, "leftSide"); - } - - if (result == null) - { - throw new ArgumentNullException("result"); - } - - if (ColumnCount != result.Count) - { - throw DimensionsDontMatch(this, result, "result"); - } - - if (ReferenceEquals(leftSide, result)) - { - var tmp = result.CreateVector(result.Count); - LeftMultiply(leftSide, tmp); - tmp.CopyTo(result); - } - else - { - // Clear the result vector - result.Clear(); - - // Multiply the elements in the vector with the corresponding diagonal element in this. - for (var r = 0; r < _data.Length; r++) - { - result[r] = _data[r] * leftSide[r]; - } - } - } - - /// - /// Computes the determinant of this matrix. - /// - /// The determinant of this matrix. - public override float Determinant() - { - if (RowCount != ColumnCount) - { - throw new ArgumentException(Resources.ArgumentMatrixSquare); - } - - return _data.Aggregate(1.0f, (current, t) => current * t); - } - - /// - /// Returns the elements of the diagonal in a . - /// - /// The elements of the diagonal. - /// For non-square matrices, the method returns Min(Rows, Columns) elements where - /// i == j (i is the row index, and j is the column index). - public override Vector Diagonal() - { - return new DenseVector(_data).Clone(); - } - /// /// Multiplies this matrix with transpose of another matrix and places the results into the result matrix. /// @@ -580,6 +485,61 @@ namespace MathNet.Numerics.LinearAlgebra.Single base.DoTransposeThisAndMultiply(other, result); } + /// + /// Multiplies the transpose of this matrix with a vector and places the results into the result vector. + /// + /// The vector to multiply with. + /// The result of the multiplication. + protected override void DoTransposeThisAndMultiply(Vector rightSide, Vector result) + { + var d = Math.Min(ColumnCount, RowCount); + if (d < ColumnCount) + { + result.ClearSubVector(RowCount, ColumnCount - RowCount); + } + + if (d == RowCount) + { + var denseOther = rightSide.Storage as DenseVectorStorage; + var denseResult = result.Storage as DenseVectorStorage; + if (denseOther != null && denseResult != null) + { + Control.LinearAlgebraProvider.PointWiseMultiplyArrays(_data, denseOther.Data, denseResult.Data); + return; + } + } + + for (var i = 0; i < d; i++) + { + result.At(i, _data[i]*rightSide.At(i)); + } + } + + /// + /// Computes the determinant of this matrix. + /// + /// The determinant of this matrix. + public override float Determinant() + { + if (RowCount != ColumnCount) + { + throw new ArgumentException(Resources.ArgumentMatrixSquare); + } + + return _data.Aggregate(1.0f, (current, t) => current * t); + } + + /// + /// Returns the elements of the diagonal in a . + /// + /// The elements of the diagonal. + /// For non-square matrices, the method returns Min(Rows, Columns) elements where + /// i == j (i is the row index, and j is the column index). + public override Vector Diagonal() + { + return new DenseVector(_data).Clone(); + } + /// /// Returns the transpose of this matrix. /// From 465a2c2bd8b6820b7141797d18ff5b97dc137c05 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sun, 1 Dec 2013 23:36:55 +0100 Subject: [PATCH 022/109] LA: Diagonal matrices: clean up some pointless null-checks --- .../LinearAlgebra/Complex/DiagonalMatrix.cs | 46 ------------------- .../LinearAlgebra/Complex32/DiagonalMatrix.cs | 45 ------------------ .../LinearAlgebra/Double/DiagonalMatrix.cs | 46 ------------------- .../LinearAlgebra/Single/DiagonalMatrix.cs | 46 ------------------- .../MatrixStructureTheory.Access.cs | 10 ++-- .../MatrixStructureTheory.Reform.cs | 4 +- 6 files changed, 7 insertions(+), 190 deletions(-) diff --git a/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs index 5d5cfbaf..d18bdff1 100644 --- a/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs @@ -199,7 +199,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// /// The matrix to add to this matrix. /// The matrix to store the result of the addition. - /// If the other matrix is . /// If the two matrices don't have the same dimensions. protected override void DoAdd(Matrix other, Matrix result) { @@ -221,7 +220,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// /// The matrix to subtract. /// The matrix to store the result of the subtraction. - /// If the other matrix is . /// If the two matrices don't have the same dimensions. protected override void DoSubtract(Matrix other, Matrix result) { @@ -243,18 +241,12 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// /// The array to copy the values from. The length of the vector should be /// Min(Rows, Columns). - /// If is . /// If the length of does not /// equal Min(Rows, Columns). /// For non-square matrices, the elements of are copied to /// this[i,i]. public override void SetDiagonal(Complex[] source) { - if (source == null) - { - throw new ArgumentNullException("source"); - } - if (source.Length != _data.Length) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "source"); @@ -268,7 +260,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// /// The vector to copy the values from. The length of the vector should be /// Min(Rows, Columns). - /// If is . /// If the length of does not /// equal Min(Rows, Columns). /// For non-square matrices, the elements of are copied to @@ -295,7 +286,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// /// The scalar to multiply the matrix with. /// The matrix to store the result of the multiplication. - /// If the result matrix is . /// If the result matrix's dimensions are not the same as this matrix. protected override void DoMultiply(Complex scalar, Matrix result) { @@ -640,15 +630,9 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// Puts the lower triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. - /// If is . /// If the result matrix's dimensions are not the same as this matrix. public override void LowerTriangle(Matrix result) { - if (result == null) - { - throw new ArgumentNullException("result"); - } - if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); @@ -680,15 +664,9 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// Puts the strictly lower triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. - /// If is . /// If the result matrix's dimensions are not the same as this matrix. public override void StrictlyLowerTriangle(Matrix result) { - if (result == null) - { - throw new ArgumentNullException("result"); - } - if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); @@ -710,15 +688,9 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// Puts the upper triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. - /// If is . /// If the result matrix's dimensions are not the same as this matrix. public override void UpperTriangle(Matrix result) { - if (result == null) - { - throw new ArgumentNullException("result"); - } - if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); @@ -745,15 +717,9 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// Puts the strictly upper triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. - /// If is . /// If the result matrix's dimensions are not the same as this matrix. public override void StrictlyUpperTriangle(Matrix result) { - if (result == null) - { - throw new ArgumentNullException("result"); - } - if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); @@ -794,16 +760,10 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// The index of where to insert the column. /// The column to insert. /// A new with the inserted column. - /// If is . /// If is < zero or > the number of columns. /// If the size of != the number of rows. public override Matrix InsertColumn(int columnIndex, Vector column) { - if (column == null) - { - throw new ArgumentNullException("column"); - } - if (columnIndex < 0 || columnIndex > ColumnCount) { throw new ArgumentOutOfRangeException("columnIndex"); @@ -837,16 +797,10 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// The index of where to insert the row. /// The row to insert. /// A new with the inserted column. - /// If is . /// If is < zero or > the number of rows. /// If the size of != the number of columns. public override Matrix InsertRow(int rowIndex, Vector row) { - if (row == null) - { - throw new ArgumentNullException("row"); - } - if (rowIndex < 0 || rowIndex > RowCount) { throw new ArgumentOutOfRangeException("rowIndex"); diff --git a/src/Numerics/LinearAlgebra/Complex32/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Complex32/DiagonalMatrix.cs index 4f3c3b97..233b51d0 100644 --- a/src/Numerics/LinearAlgebra/Complex32/DiagonalMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex32/DiagonalMatrix.cs @@ -194,7 +194,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// /// The matrix to add to this matrix. /// The matrix to store the result of the addition. - /// If the other matrix is . /// If the two matrices don't have the same dimensions. protected override void DoAdd(Matrix other, Matrix result) { @@ -216,7 +215,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// /// The matrix to subtract. /// The matrix to store the result of the subtraction. - /// If the other matrix is . /// If the two matrices don't have the same dimensions. protected override void DoSubtract(Matrix other, Matrix result) { @@ -238,18 +236,12 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// /// The array to copy the values from. The length of the vector should be /// Min(Rows, Columns). - /// If is . /// If the length of does not /// equal Min(Rows, Columns). /// For non-square matrices, the elements of are copied to /// this[i,i]. public override void SetDiagonal(Complex32[] source) { - if (source == null) - { - throw new ArgumentNullException("source"); - } - if (source.Length != _data.Length) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "source"); @@ -263,7 +255,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// /// The vector to copy the values from. The length of the vector should be /// Min(Rows, Columns). - /// If is . /// If the length of does not /// equal Min(Rows, Columns). /// For non-square matrices, the elements of are copied to @@ -633,15 +624,9 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// Puts the lower triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. - /// If is . /// If the result matrix's dimensions are not the same as this matrix. public override void LowerTriangle(Matrix result) { - if (result == null) - { - throw new ArgumentNullException("result"); - } - if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); @@ -673,15 +658,9 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// Puts the strictly lower triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. - /// If is . /// If the result matrix's dimensions are not the same as this matrix. public override void StrictlyLowerTriangle(Matrix result) { - if (result == null) - { - throw new ArgumentNullException("result"); - } - if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); @@ -703,15 +682,9 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// Puts the upper triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. - /// If is . /// If the result matrix's dimensions are not the same as this matrix. public override void UpperTriangle(Matrix result) { - if (result == null) - { - throw new ArgumentNullException("result"); - } - if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); @@ -738,15 +711,9 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// Puts the strictly upper triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. - /// If is . /// If the result matrix's dimensions are not the same as this matrix. public override void StrictlyUpperTriangle(Matrix result) { - if (result == null) - { - throw new ArgumentNullException("result"); - } - if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); @@ -787,16 +754,10 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// The index of where to insert the column. /// The column to insert. /// A new with the inserted column. - /// If is . /// If is < zero or > the number of columns. /// If the size of != the number of rows. public override Matrix InsertColumn(int columnIndex, Vector column) { - if (column == null) - { - throw new ArgumentNullException("column"); - } - if (columnIndex < 0 || columnIndex > ColumnCount) { throw new ArgumentOutOfRangeException("columnIndex"); @@ -830,16 +791,10 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// The index of where to insert the row. /// The row to insert. /// A new with the inserted column. - /// If is . /// If is < zero or > the number of rows. /// If the size of != the number of columns. public override Matrix InsertRow(int rowIndex, Vector row) { - if (row == null) - { - throw new ArgumentNullException("row"); - } - if (rowIndex < 0 || rowIndex > RowCount) { throw new ArgumentOutOfRangeException("rowIndex"); diff --git a/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs index 81ad1356..9d735cb5 100644 --- a/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs +++ b/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs @@ -193,7 +193,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// /// The matrix to add to this matrix. /// The matrix to store the result of the addition. - /// If the other matrix is . /// If the two matrices don't have the same dimensions. protected override void DoAdd(Matrix other, Matrix result) { @@ -215,7 +214,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// /// The matrix to subtract. /// The matrix to store the result of the subtraction. - /// If the other matrix is . /// If the two matrices don't have the same dimensions. protected override void DoSubtract(Matrix other, Matrix result) { @@ -237,18 +235,12 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// /// The array to copy the values from. The length of the vector should be /// Min(Rows, Columns). - /// If is . /// If the length of does not /// equal Min(Rows, Columns). /// For non-square matrices, the elements of are copied to /// this[i,i]. public override void SetDiagonal(double[] source) { - if (source == null) - { - throw new ArgumentNullException("source"); - } - if (source.Length != _data.Length) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "source"); @@ -262,7 +254,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// /// The vector to copy the values from. The length of the vector should be /// Min(Rows, Columns). - /// If is . /// If the length of does not /// equal Min(Rows, Columns). /// For non-square matrices, the elements of are copied to @@ -289,7 +280,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// /// The scalar to multiply the matrix with. /// The matrix to store the result of the multiplication. - /// If the result matrix is . /// If the result matrix's dimensions are not the same as this matrix. protected override void DoMultiply(double scalar, Matrix result) { @@ -634,15 +624,9 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// Puts the lower triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. - /// If is . /// If the result matrix's dimensions are not the same as this matrix. public override void LowerTriangle(Matrix result) { - if (result == null) - { - throw new ArgumentNullException("result"); - } - if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); @@ -674,15 +658,9 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// Puts the strictly lower triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. - /// If is . /// If the result matrix's dimensions are not the same as this matrix. public override void StrictlyLowerTriangle(Matrix result) { - if (result == null) - { - throw new ArgumentNullException("result"); - } - if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); @@ -704,15 +682,9 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// Puts the upper triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. - /// If is . /// If the result matrix's dimensions are not the same as this matrix. public override void UpperTriangle(Matrix result) { - if (result == null) - { - throw new ArgumentNullException("result"); - } - if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); @@ -739,15 +711,9 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// Puts the strictly upper triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. - /// If is . /// If the result matrix's dimensions are not the same as this matrix. public override void StrictlyUpperTriangle(Matrix result) { - if (result == null) - { - throw new ArgumentNullException("result"); - } - if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); @@ -788,16 +754,10 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// The index of where to insert the column. /// The column to insert. /// A new with the inserted column. - /// If is . /// If is < zero or > the number of columns. /// If the size of != the number of rows. public override Matrix InsertColumn(int columnIndex, Vector column) { - if (column == null) - { - throw new ArgumentNullException("column"); - } - if (columnIndex < 0 || columnIndex > ColumnCount) { throw new ArgumentOutOfRangeException("columnIndex"); @@ -831,16 +791,10 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// The index of where to insert the row. /// The row to insert. /// A new with the inserted column. - /// If is . /// If is < zero or > the number of rows. /// If the size of != the number of columns. public override Matrix InsertRow(int rowIndex, Vector row) { - if (row == null) - { - throw new ArgumentNullException("row"); - } - if (rowIndex < 0 || rowIndex > RowCount) { throw new ArgumentOutOfRangeException("rowIndex"); diff --git a/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs index 78288526..fa9aa7c7 100644 --- a/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs +++ b/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs @@ -193,7 +193,6 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// /// The matrix to add to this matrix. /// The matrix to store the result of the addition. - /// If the other matrix is . /// If the two matrices don't have the same dimensions. protected override void DoAdd(Matrix other, Matrix result) { @@ -215,7 +214,6 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// /// The matrix to subtract. /// The matrix to store the result of the subtraction. - /// If the other matrix is . /// If the two matrices don't have the same dimensions. protected override void DoSubtract(Matrix other, Matrix result) { @@ -237,18 +235,12 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// /// The array to copy the values from. The length of the vector should be /// Min(Rows, Columns). - /// If is . /// If the length of does not /// equal Min(Rows, Columns). /// For non-square matrices, the elements of are copied to /// this[i,i]. public override void SetDiagonal(float[] source) { - if (source == null) - { - throw new ArgumentNullException("source"); - } - if (source.Length != _data.Length) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "source"); @@ -262,7 +254,6 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// /// The vector to copy the values from. The length of the vector should be /// Min(Rows, Columns). - /// If is . /// If the length of does not /// equal Min(Rows, Columns). /// For non-square matrices, the elements of are copied to @@ -289,7 +280,6 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// /// The scalar to multiply the matrix with. /// The matrix to store the result of the multiplication. - /// If the result matrix is . /// If the result matrix's dimensions are not the same as this matrix. protected override void DoMultiply(float scalar, Matrix result) { @@ -634,15 +624,9 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// Puts the lower triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. - /// If is . /// If the result matrix's dimensions are not the same as this matrix. public override void LowerTriangle(Matrix result) { - if (result == null) - { - throw new ArgumentNullException("result"); - } - if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); @@ -674,15 +658,9 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// Puts the strictly lower triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. - /// If is . /// If the result matrix's dimensions are not the same as this matrix. public override void StrictlyLowerTriangle(Matrix result) { - if (result == null) - { - throw new ArgumentNullException("result"); - } - if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); @@ -704,15 +682,9 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// Puts the upper triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. - /// If is . /// If the result matrix's dimensions are not the same as this matrix. public override void UpperTriangle(Matrix result) { - if (result == null) - { - throw new ArgumentNullException("result"); - } - if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); @@ -739,15 +711,9 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// Puts the strictly upper triangle of this matrix into the result matrix. ///
/// Where to store the lower triangle. - /// If is . /// If the result matrix's dimensions are not the same as this matrix. public override void StrictlyUpperTriangle(Matrix result) { - if (result == null) - { - throw new ArgumentNullException("result"); - } - if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); @@ -788,16 +754,10 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// The index of where to insert the column. /// The column to insert. /// A new with the inserted column. - /// If is . /// If is < zero or > the number of columns. /// If the size of != the number of rows. public override Matrix InsertColumn(int columnIndex, Vector column) { - if (column == null) - { - throw new ArgumentNullException("column"); - } - if (columnIndex < 0 || columnIndex > ColumnCount) { throw new ArgumentOutOfRangeException("columnIndex"); @@ -831,16 +791,10 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// The index of where to insert the row. /// The row to insert. /// A new with the inserted column. - /// If is . /// If is < zero or > the number of rows. /// If the size of != the number of columns. public override Matrix InsertRow(int rowIndex, Vector row) { - if (row == null) - { - throw new ArgumentNullException("row"); - } - if (rowIndex < 0 || rowIndex > RowCount) { throw new ArgumentOutOfRangeException("rowIndex"); diff --git a/src/UnitTests/LinearAlgebraTests/MatrixStructureTheory.Access.cs b/src/UnitTests/LinearAlgebraTests/MatrixStructureTheory.Access.cs index a724cfc4..caebcecb 100644 --- a/src/UnitTests/LinearAlgebraTests/MatrixStructureTheory.Access.cs +++ b/src/UnitTests/LinearAlgebraTests/MatrixStructureTheory.Access.cs @@ -404,7 +404,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests } } - Assert.That(() => matrix.UpperTriangle(null), Throws.InstanceOf()); + Assert.That(() => matrix.UpperTriangle(null), Throws.Exception); Assert.That(() => matrix.UpperTriangle(CreateSparseZero(matrix.RowCount + 1, matrix.ColumnCount)), Throws.ArgumentException); Assert.That(() => matrix.UpperTriangle(CreateDenseZero(matrix.RowCount, matrix.ColumnCount + 1)), Throws.ArgumentException); } @@ -445,7 +445,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests } } - Assert.That(() => matrix.LowerTriangle(null), Throws.InstanceOf()); + Assert.That(() => matrix.LowerTriangle(null), Throws.Exception); Assert.That(() => matrix.LowerTriangle(CreateSparseZero(matrix.RowCount + 1, matrix.ColumnCount)), Throws.ArgumentException); Assert.That(() => matrix.LowerTriangle(CreateDenseZero(matrix.RowCount, matrix.ColumnCount + 1)), Throws.ArgumentException); } @@ -486,7 +486,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests } } - Assert.That(() => matrix.StrictlyUpperTriangle(null), Throws.InstanceOf()); + Assert.That(() => matrix.StrictlyUpperTriangle(null), Throws.Exception); Assert.That(() => matrix.StrictlyUpperTriangle(CreateSparseZero(matrix.RowCount + 1, matrix.ColumnCount)), Throws.ArgumentException); Assert.That(() => matrix.StrictlyUpperTriangle(CreateDenseZero(matrix.RowCount, matrix.ColumnCount + 1)), Throws.ArgumentException); } @@ -527,7 +527,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests } } - Assert.That(() => matrix.StrictlyLowerTriangle(null), Throws.InstanceOf()); + Assert.That(() => matrix.StrictlyLowerTriangle(null), Throws.Exception); Assert.That(() => matrix.StrictlyLowerTriangle(CreateSparseZero(matrix.RowCount + 1, matrix.ColumnCount)), Throws.ArgumentException); Assert.That(() => matrix.StrictlyLowerTriangle(CreateDenseZero(matrix.RowCount, matrix.ColumnCount + 1)), Throws.ArgumentException); } @@ -577,7 +577,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests } // Invalid - Assert.That(() => matrix.SetDiagonal(default(T[])), Throws.InstanceOf()); + Assert.That(() => matrix.SetDiagonal(default(T[])), Throws.Exception); Assert.That(() => matrix.SetDiagonal(new T[Math.Min(matrix.RowCount, matrix.ColumnCount) - 1]), Throws.ArgumentException); Assert.That(() => matrix.SetDiagonal(new T[Math.Min(matrix.RowCount, matrix.ColumnCount) + 1]), Throws.ArgumentException); } diff --git a/src/UnitTests/LinearAlgebraTests/MatrixStructureTheory.Reform.cs b/src/UnitTests/LinearAlgebraTests/MatrixStructureTheory.Reform.cs index 09a237af..d226ed3a 100644 --- a/src/UnitTests/LinearAlgebraTests/MatrixStructureTheory.Reform.cs +++ b/src/UnitTests/LinearAlgebraTests/MatrixStructureTheory.Reform.cs @@ -124,7 +124,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests } // Invalid - Assert.That(() => matrix.InsertRow(0, default(Vector)), Throws.InstanceOf()); + Assert.That(() => matrix.InsertRow(0, default(Vector)), Throws.Exception); Assert.That(() => matrix.InsertRow(-1, CreateVectorZero(matrix.ColumnCount)), Throws.InstanceOf()); Assert.That(() => matrix.InsertRow(matrix.RowCount + 1, CreateVectorZero(matrix.ColumnCount)), Throws.InstanceOf()); Assert.That(() => matrix.InsertRow(0, CreateVectorZero(matrix.ColumnCount - 1)), Throws.ArgumentException); @@ -160,7 +160,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests } // Invalid - Assert.That(() => matrix.InsertColumn(0, default(Vector)), Throws.InstanceOf()); + Assert.That(() => matrix.InsertColumn(0, default(Vector)), Throws.Exception); Assert.That(() => matrix.InsertColumn(-1, CreateVectorZero(matrix.RowCount)), Throws.InstanceOf()); Assert.That(() => matrix.InsertColumn(matrix.ColumnCount + 1, CreateVectorZero(matrix.RowCount)), Throws.InstanceOf()); Assert.That(() => matrix.InsertColumn(0, CreateVectorZero(matrix.RowCount - 1)), Throws.ArgumentException); From 241336e1e4b8c996f2eccb3d727cb24f6f413499 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Mon, 2 Dec 2013 00:05:43 +0100 Subject: [PATCH 023/109] LA: diagonal-dense Add Subtract --- .../LinearAlgebra/Complex/DenseMatrix.cs | 4 +-- .../LinearAlgebra/Complex/DiagonalMatrix.cs | 26 ++++++++++++------- .../LinearAlgebra/Complex32/DenseMatrix.cs | 4 +-- .../LinearAlgebra/Complex32/DiagonalMatrix.cs | 26 ++++++++++++------- .../LinearAlgebra/Double/DenseMatrix.cs | 4 +-- .../LinearAlgebra/Double/DiagonalMatrix.cs | 26 ++++++++++++------- .../LinearAlgebra/Single/DenseMatrix.cs | 4 +-- .../LinearAlgebra/Single/DiagonalMatrix.cs | 26 ++++++++++++------- 8 files changed, 72 insertions(+), 48 deletions(-) diff --git a/src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs b/src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs index 7deaf8d1..63ea0f19 100644 --- a/src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs @@ -506,11 +506,11 @@ namespace MathNet.Numerics.LinearAlgebra.Complex return; } - // dense + diagonal = matrix + // dense + diagonal = any var diagonalOther = other.Storage as DiagonalMatrixStorage; if (diagonalOther != null) { - CopyTo(result); + Storage.CopyToUnchecked(result.Storage); var diagonal = diagonalOther.Data; for (int i = 0; i < diagonal.Length; i++) { diff --git a/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs index d18bdff1..094d8986 100644 --- a/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs @@ -202,16 +202,19 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// If the two matrices don't have the same dimensions. protected override void DoAdd(Matrix other, Matrix result) { + // diagonal + diagonal = diagonal var diagOther = other as DiagonalMatrix; var diagResult = result as DiagonalMatrix; - - if (diagOther == null || diagResult == null) + if (diagOther != null && diagResult != null) { - base.DoAdd(other, result); + Control.LinearAlgebraProvider.AddArrays(_data, diagOther._data, diagResult._data); + return; } - else + + other.CopyTo(result); + for (int i = 0; i < _data.Length; i++) { - Control.LinearAlgebraProvider.AddArrays(_data, diagOther._data, diagResult._data); + result.At(i, i, result.At(i, i) + _data[i]); } } @@ -223,16 +226,19 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// If the two matrices don't have the same dimensions. protected override void DoSubtract(Matrix other, Matrix result) { + // diagonal - diagonal = diagonal var diagOther = other as DiagonalMatrix; var diagResult = result as DiagonalMatrix; - - if (diagOther == null || diagResult == null) + if (diagOther != null && diagResult != null) { - base.DoSubtract(other, result); + Control.LinearAlgebraProvider.SubtractArrays(_data, diagOther._data, diagResult._data); + return; } - else + + other.Negate(result); + for (int i = 0; i < _data.Length; i++) { - Control.LinearAlgebraProvider.SubtractArrays(_data, diagOther._data, diagResult._data); + result.At(i, i, result.At(i, i) + _data[i]); } } diff --git a/src/Numerics/LinearAlgebra/Complex32/DenseMatrix.cs b/src/Numerics/LinearAlgebra/Complex32/DenseMatrix.cs index 1337b9aa..cfe659f0 100644 --- a/src/Numerics/LinearAlgebra/Complex32/DenseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex32/DenseMatrix.cs @@ -501,11 +501,11 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 return; } - // dense + diagonal = matrix + // dense + diagonal = any var diagonalOther = other.Storage as DiagonalMatrixStorage; if (diagonalOther != null) { - CopyTo(result); + Storage.CopyToUnchecked(result.Storage); var diagonal = diagonalOther.Data; for (int i = 0; i < diagonal.Length; i++) { diff --git a/src/Numerics/LinearAlgebra/Complex32/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Complex32/DiagonalMatrix.cs index 233b51d0..4c55048f 100644 --- a/src/Numerics/LinearAlgebra/Complex32/DiagonalMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex32/DiagonalMatrix.cs @@ -197,16 +197,19 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// If the two matrices don't have the same dimensions. protected override void DoAdd(Matrix other, Matrix result) { + // diagonal + diagonal = diagonal var diagOther = other as DiagonalMatrix; var diagResult = result as DiagonalMatrix; - - if (diagOther == null || diagResult == null) + if (diagOther != null && diagResult != null) { - base.DoAdd(other, result); + Control.LinearAlgebraProvider.AddArrays(_data, diagOther._data, diagResult._data); + return; } - else + + other.CopyTo(result); + for (int i = 0; i < _data.Length; i++) { - Control.LinearAlgebraProvider.AddArrays(_data, diagOther._data, diagResult._data); + result.At(i, i, result.At(i, i) + _data[i]); } } @@ -218,16 +221,19 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// If the two matrices don't have the same dimensions. protected override void DoSubtract(Matrix other, Matrix result) { + // diagonal - diagonal = diagonal var diagOther = other as DiagonalMatrix; var diagResult = result as DiagonalMatrix; - - if (diagOther == null || diagResult == null) + if (diagOther != null && diagResult != null) { - base.DoSubtract(other, result); + Control.LinearAlgebraProvider.SubtractArrays(_data, diagOther._data, diagResult._data); + return; } - else + + other.Negate(result); + for (int i = 0; i < _data.Length; i++) { - Control.LinearAlgebraProvider.SubtractArrays(_data, diagOther._data, diagResult._data); + result.At(i, i, result.At(i, i) + _data[i]); } } diff --git a/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs b/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs index a45cd15f..abb831bc 100644 --- a/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs @@ -481,11 +481,11 @@ namespace MathNet.Numerics.LinearAlgebra.Double return; } - // dense + diagonal = matrix + // dense + diagonal = any var diagonalOther = other.Storage as DiagonalMatrixStorage; if (diagonalOther != null) { - CopyTo(result); + Storage.CopyToUnchecked(result.Storage); var diagonal = diagonalOther.Data; for (int i = 0; i < diagonal.Length; i++) { diff --git a/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs index 9d735cb5..2daca108 100644 --- a/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs +++ b/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs @@ -196,16 +196,19 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// If the two matrices don't have the same dimensions. protected override void DoAdd(Matrix other, Matrix result) { + // diagonal + diagonal = diagonal var diagOther = other as DiagonalMatrix; var diagResult = result as DiagonalMatrix; - - if (diagOther == null || diagResult == null) + if (diagOther != null && diagResult != null) { - base.DoAdd(other, result); + Control.LinearAlgebraProvider.AddArrays(_data, diagOther._data, diagResult._data); + return; } - else + + other.CopyTo(result); + for (int i = 0; i < _data.Length; i++) { - Control.LinearAlgebraProvider.AddArrays(_data, diagOther._data, diagResult._data); + result.At(i, i, result.At(i, i) + _data[i]); } } @@ -217,16 +220,19 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// If the two matrices don't have the same dimensions. protected override void DoSubtract(Matrix other, Matrix result) { + // diagonal - diagonal = diagonal var diagOther = other as DiagonalMatrix; var diagResult = result as DiagonalMatrix; - - if (diagOther == null || diagResult == null) + if (diagOther != null && diagResult != null) { - base.DoSubtract(other, result); + Control.LinearAlgebraProvider.SubtractArrays(_data, diagOther._data, diagResult._data); + return; } - else + + other.Negate(result); + for (int i = 0; i < _data.Length; i++) { - Control.LinearAlgebraProvider.SubtractArrays(_data, diagOther._data, diagResult._data); + result.At(i, i, result.At(i, i) + _data[i]); } } diff --git a/src/Numerics/LinearAlgebra/Single/DenseMatrix.cs b/src/Numerics/LinearAlgebra/Single/DenseMatrix.cs index 61dc0c34..4691979b 100644 --- a/src/Numerics/LinearAlgebra/Single/DenseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Single/DenseMatrix.cs @@ -481,11 +481,11 @@ namespace MathNet.Numerics.LinearAlgebra.Single return; } - // dense + diagonal = matrix + // dense + diagonal = any var diagonalOther = other.Storage as DiagonalMatrixStorage; if (diagonalOther != null) { - CopyTo(result); + Storage.CopyToUnchecked(result.Storage); var diagonal = diagonalOther.Data; for (int i = 0; i < diagonal.Length; i++) { diff --git a/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs index fa9aa7c7..5b84ee75 100644 --- a/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs +++ b/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs @@ -196,16 +196,19 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// If the two matrices don't have the same dimensions. protected override void DoAdd(Matrix other, Matrix result) { + // diagonal + diagonal = diagonal var diagOther = other as DiagonalMatrix; var diagResult = result as DiagonalMatrix; - - if (diagOther == null || diagResult == null) + if (diagOther != null && diagResult != null) { - base.DoAdd(other, result); + Control.LinearAlgebraProvider.AddArrays(_data, diagOther._data, diagResult._data); + return; } - else + + other.CopyTo(result); + for (int i = 0; i < _data.Length; i++) { - Control.LinearAlgebraProvider.AddArrays(_data, diagOther._data, diagResult._data); + result.At(i, i, result.At(i, i) + _data[i]); } } @@ -217,16 +220,19 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// If the two matrices don't have the same dimensions. protected override void DoSubtract(Matrix other, Matrix result) { + // diagonal - diagonal = diagonal var diagOther = other as DiagonalMatrix; var diagResult = result as DiagonalMatrix; - - if (diagOther == null || diagResult == null) + if (diagOther != null && diagResult != null) { - base.DoSubtract(other, result); + Control.LinearAlgebraProvider.SubtractArrays(_data, diagOther._data, diagResult._data); + return; } - else + + other.Negate(result); + for (int i = 0; i < _data.Length; i++) { - Control.LinearAlgebraProvider.SubtractArrays(_data, diagOther._data, diagResult._data); + result.At(i, i, result.At(i, i) + _data[i]); } } From d83566155fe92e9cc845e3b8b759002cad8fb9b3 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Mon, 2 Dec 2013 00:16:30 +0100 Subject: [PATCH 024/109] Fit: use diagonal matrices where appropritate (since they are much faster now) --- src/Numerics/Fit.cs | 2 +- src/Numerics/LinearRegression/WeightedRegression.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Numerics/Fit.cs b/src/Numerics/Fit.cs index b16026cd..14ddce5c 100644 --- a/src/Numerics/Fit.cs +++ b/src/Numerics/Fit.cs @@ -106,7 +106,7 @@ namespace MathNet.Numerics public static double[] PolynomialWeighted(double[] x, double[] y, double[] w, int order) { var design = Matrix.Build.Dense(x.Length, order + 1, (i, j) => Math.Pow(x[i], j)); - return WeightedRegression.Weighted(design, Vector.Build.Dense(y), Matrix.Build.DenseOfDiagonalArray(w)).ToArray(); + return WeightedRegression.Weighted(design, Vector.Build.Dense(y), Matrix.Build.Diagonal(w.Length, w.Length, w)).ToArray(); } /// diff --git a/src/Numerics/LinearRegression/WeightedRegression.cs b/src/Numerics/LinearRegression/WeightedRegression.cs index 3a6b80a9..693a9f99 100644 --- a/src/Numerics/LinearRegression/WeightedRegression.cs +++ b/src/Numerics/LinearRegression/WeightedRegression.cs @@ -65,7 +65,7 @@ namespace MathNet.Numerics.LinearRegression predictor = predictor.InsertColumn(0, Vector.Build.Dense(predictor.RowCount, Vector.One)); } var response = Vector.Build.Dense(y); - var weights = Matrix.Build.DenseOfDiagonalArray(w); + var weights = Matrix.Build.Diagonal(w.Length, w.Length, w); return predictor.TransposeThisAndMultiply(weights*predictor).Cholesky().Solve(predictor.TransposeThisAndMultiply(weights*response)).ToArray(); } From 64a641e8caa19c268cf62ca855ebbbad5009c899 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Mon, 2 Dec 2013 17:57:45 +0100 Subject: [PATCH 025/109] LA: extend Build.Diagonal for diagonal matrices --- src/Numerics/Fit.cs | 2 +- src/Numerics/LinearAlgebra/Builder.cs | 60 +++++++++++++++++++ .../LinearRegression/WeightedRegression.cs | 2 +- 3 files changed, 62 insertions(+), 2 deletions(-) diff --git a/src/Numerics/Fit.cs b/src/Numerics/Fit.cs index 14ddce5c..1669a883 100644 --- a/src/Numerics/Fit.cs +++ b/src/Numerics/Fit.cs @@ -106,7 +106,7 @@ namespace MathNet.Numerics public static double[] PolynomialWeighted(double[] x, double[] y, double[] w, int order) { var design = Matrix.Build.Dense(x.Length, order + 1, (i, j) => Math.Pow(x[i], j)); - return WeightedRegression.Weighted(design, Vector.Build.Dense(y), Matrix.Build.Diagonal(w.Length, w.Length, w)).ToArray(); + return WeightedRegression.Weighted(design, Vector.Build.Dense(y), Matrix.Build.Diagonal(w)).ToArray(); } /// diff --git a/src/Numerics/LinearAlgebra/Builder.cs b/src/Numerics/LinearAlgebra/Builder.cs index e167164e..811dd961 100644 --- a/src/Numerics/LinearAlgebra/Builder.cs +++ b/src/Numerics/LinearAlgebra/Builder.cs @@ -1147,6 +1147,17 @@ namespace MathNet.Numerics.LinearAlgebra return Diagonal(new DiagonalMatrixStorage(rows, columns, storage)); } + /// + /// Create a new square diagonal matrix directly binding to a raw array. + /// The array is assumed to represent the diagonal values and is used directly without copying. + /// Very efficient, but changes to the array and the matrix will affect each other. + /// + /// + public Matrix Diagonal(T[] storage) + { + return Diagonal(new DiagonalMatrixStorage(storage.Length, storage.Length, storage)); + } + /// /// Create a new diagonal matrix and initialize each diagonal value to the same provided value. /// @@ -1180,6 +1191,55 @@ namespace MathNet.Numerics.LinearAlgebra return Diagonal(DiagonalMatrixStorage.OfInit(order, order, i => One)); } + + /// + /// Create a new diagonal matrix with the diagonal as a copy of the given vector. + /// This new matrix will be independent from the vector. + /// A new memory block will be allocated for storing the matrix. + /// + public Matrix DiagonalOfDiagonalVector(Vector diagonal) + { + var m = Diagonal(diagonal.Count, diagonal.Count); + m.SetDiagonal(diagonal); + return m; + } + + /// + /// Create a new diagonal matrix with the diagonal as a copy of the given vector. + /// This new matrix will be independent from the vector. + /// A new memory block will be allocated for storing the matrix. + /// + public Matrix DiagonalOfDiagonalVector(int rows, int columns, Vector diagonal) + { + var m = Diagonal(rows, columns); + m.SetDiagonal(diagonal); + return m; + } + + /// + /// Create a new diagonal matrix with the diagonal as a copy of the given array. + /// This new matrix will be independent from the array. + /// A new memory block will be allocated for storing the matrix. + /// + public Matrix DiagonalOfDiagonalArray(T[] diagonal) + { + var m = Diagonal(diagonal.Length, diagonal.Length); + m.SetDiagonal(diagonal); + return m; + } + + /// + /// Create a new diagonal matrix with the diagonal as a copy of the given array. + /// This new matrix will be independent from the array. + /// A new memory block will be allocated for storing the matrix. + /// + public Matrix DiagonalOfDiagonalArray(int rows, int columns, T[] diagonal) + { + var m = Diagonal(rows, columns); + m.SetDiagonal(diagonal); + return m; + } + public abstract IIterationStopCriterium[] IterativeSolverStopCriteria(int maxIterations = 1000); } diff --git a/src/Numerics/LinearRegression/WeightedRegression.cs b/src/Numerics/LinearRegression/WeightedRegression.cs index 693a9f99..9d7ec732 100644 --- a/src/Numerics/LinearRegression/WeightedRegression.cs +++ b/src/Numerics/LinearRegression/WeightedRegression.cs @@ -65,7 +65,7 @@ namespace MathNet.Numerics.LinearRegression predictor = predictor.InsertColumn(0, Vector.Build.Dense(predictor.RowCount, Vector.One)); } var response = Vector.Build.Dense(y); - var weights = Matrix.Build.Diagonal(w.Length, w.Length, w); + var weights = Matrix.Build.Diagonal(w); return predictor.TransposeThisAndMultiply(weights*predictor).Cholesky().Solve(predictor.TransposeThisAndMultiply(weights*response)).ToArray(); } From 7f7120ea6083854e995f79edcf6064cad123c299 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Mon, 2 Dec 2013 20:50:58 +0100 Subject: [PATCH 026/109] LA: phase out Matrix.CreateMatrix/CreateVector (no longer needed) --- src/FSharp/LinearAlgebra.Matrix.fs | 8 +- src/Numerics/Distributions/InverseWishart.cs | 16 ++-- src/Numerics/Distributions/Wishart.cs | 12 +-- src/Numerics/LinearAlgebra/Builder.cs | 74 ++++++++++++---- .../Complex/Factorization/UserEvd.cs | 2 +- .../Complex/Factorization/UserGramSchmidt.cs | 2 +- .../Complex/Factorization/UserLU.cs | 2 +- .../Complex/Factorization/UserQR.cs | 2 +- .../Complex/Factorization/UserSvd.cs | 8 +- src/Numerics/LinearAlgebra/Complex/Matrix.cs | 2 +- .../LinearAlgebra/Complex/SparseMatrix.cs | 16 ++-- .../Complex32/Factorization/UserEvd.cs | 2 +- .../Factorization/UserGramSchmidt.cs | 2 +- .../Complex32/Factorization/UserLU.cs | 2 +- .../Complex32/Factorization/UserQR.cs | 2 +- .../Complex32/Factorization/UserSvd.cs | 8 +- .../LinearAlgebra/Complex32/Matrix.cs | 2 +- .../LinearAlgebra/Complex32/SparseMatrix.cs | 16 ++-- .../Double/Factorization/UserEvd.cs | 4 +- .../Double/Factorization/UserGramSchmidt.cs | 2 +- .../Double/Factorization/UserLU.cs | 2 +- .../Double/Factorization/UserQR.cs | 2 +- .../Double/Factorization/UserSvd.cs | 8 +- .../LinearAlgebra/Double/SparseMatrix.cs | 16 ++-- .../LinearAlgebra/Factorization/Cholesky.cs | 4 +- .../LinearAlgebra/Factorization/Evd.cs | 8 +- .../LinearAlgebra/Factorization/LU.cs | 4 +- .../LinearAlgebra/Factorization/QR.cs | 8 +- .../LinearAlgebra/Factorization/Svd.cs | 10 +-- .../LinearAlgebra/Matrix.Arithmetic.cs | 62 +++++++------- src/Numerics/LinearAlgebra/Matrix.Solve.cs | 8 +- src/Numerics/LinearAlgebra/Matrix.cs | 84 ++++++------------- .../Single/Factorization/UserEvd.cs | 4 +- .../Single/Factorization/UserGramSchmidt.cs | 2 +- .../Single/Factorization/UserLU.cs | 2 +- .../Single/Factorization/UserQR.cs | 2 +- .../Single/Factorization/UserSvd.cs | 8 +- .../LinearAlgebra/Single/SparseMatrix.cs | 16 ++-- .../MatrixStructureTheory.cs | 12 +-- 39 files changed, 218 insertions(+), 228 deletions(-) diff --git a/src/FSharp/LinearAlgebra.Matrix.fs b/src/FSharp/LinearAlgebra.Matrix.fs index 6b11f055..8f634399 100644 --- a/src/FSharp/LinearAlgebra.Matrix.fs +++ b/src/FSharp/LinearAlgebra.Matrix.fs @@ -304,8 +304,8 @@ module Matrix = A.MapIndexedInplace((fun i j x -> f i j), true) /// Fold all columns into one row vector. - let inline foldByCol f acc (A: #Matrix<_>) = - let v = A.CreateVector(A.ColumnCount) + let inline foldByCol f acc (A: #Matrix<'T>) = + let v = Vector<'T>.Build.SameAs(A, A.ColumnCount) for k=0 to A.ColumnCount-1 do let mutable macc = acc for i=0 to A.RowCount-1 do @@ -314,8 +314,8 @@ module Matrix = v :> _ Vector /// Fold all rows into one column vector. - let inline foldByRow f acc (A: #Matrix<_>) = - let v = A.CreateVector(A.RowCount) + let inline foldByRow f acc (A: #Matrix<'T>) = + let v = Vector<'T>.Build.SameAs(A, A.RowCount) for k=0 to A.RowCount-1 do let mutable macc = acc for i=0 to A.ColumnCount-1 do diff --git a/src/Numerics/Distributions/InverseWishart.cs b/src/Numerics/Distributions/InverseWishart.cs index 47d5ac8a..6afbeb4a 100644 --- a/src/Numerics/Distributions/InverseWishart.cs +++ b/src/Numerics/Distributions/InverseWishart.cs @@ -187,18 +187,12 @@ namespace MathNet.Numerics.Distributions { get { - var res = _scale.CreateMatrix(_scale.RowCount, _scale.ColumnCount); - for (var i = 0; i < res.RowCount; i++) + return Matrix.Build.Dense(_scale.RowCount, _scale.ColumnCount, (i, j) => { - for (var j = 0; j < res.ColumnCount; j++) - { - var num1 = ((_freedom - _scale.RowCount + 1)*_scale.At(i, j)*_scale.At(i, j)) + ((_freedom - _scale.RowCount - 1)*_scale.At(i, i)*_scale.At(j, j)); - var num2 = (_freedom - _scale.RowCount)*(_freedom - _scale.RowCount - 1)*(_freedom - _scale.RowCount - 1)*(_freedom - _scale.RowCount - 3); - res.At(i, j, num1/num2); - } - } - - return res; + var num1 = ((_freedom - _scale.RowCount + 1)*_scale.At(i, j)*_scale.At(i, j)) + ((_freedom - _scale.RowCount - 1)*_scale.At(i, i)*_scale.At(j, j)); + var num2 = (_freedom - _scale.RowCount)*(_freedom - _scale.RowCount - 1)*(_freedom - _scale.RowCount - 1)*(_freedom - _scale.RowCount - 3); + return num1/num2; + }); } } diff --git a/src/Numerics/Distributions/Wishart.cs b/src/Numerics/Distributions/Wishart.cs index c850fe5f..b7fe2987 100644 --- a/src/Numerics/Distributions/Wishart.cs +++ b/src/Numerics/Distributions/Wishart.cs @@ -207,16 +207,8 @@ namespace MathNet.Numerics.Distributions { get { - var res = _scale.CreateMatrix(_scale.RowCount, _scale.ColumnCount); - for (var i = 0; i < res.RowCount; i++) - { - for (var j = 0; j < res.ColumnCount; j++) - { - res.At(i, j, _degreesOfFreedom*((_scale.At(i, j)*_scale.At(i, j)) + (_scale.At(i, i)*_scale.At(j, j)))); - } - } - - return res; + return Matrix.Build.Dense(_scale.RowCount, _scale.ColumnCount, + (i, j) => _degreesOfFreedom*((_scale.At(i, j)*_scale.At(i, j)) + (_scale.At(i, i)*_scale.At(j, j)))); } } diff --git a/src/Numerics/LinearAlgebra/Builder.cs b/src/Numerics/LinearAlgebra/Builder.cs index 811dd961..615c6552 100644 --- a/src/Numerics/LinearAlgebra/Builder.cs +++ b/src/Numerics/LinearAlgebra/Builder.cs @@ -39,8 +39,6 @@ using MathNet.Numerics.LinearAlgebra.Storage; namespace MathNet.Numerics.LinearAlgebra.Double { - using Solvers; - internal class MatrixBuilder : MatrixBuilder { public override double Zero @@ -116,8 +114,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double namespace MathNet.Numerics.LinearAlgebra.Single { - using Solvers; - internal class MatrixBuilder : MatrixBuilder { public override float Zero @@ -193,8 +189,6 @@ namespace MathNet.Numerics.LinearAlgebra.Single namespace MathNet.Numerics.LinearAlgebra.Complex { - using Solvers; - #if NOSYSNUMERICS using Complex = Numerics.Complex; #else @@ -276,8 +270,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex namespace MathNet.Numerics.LinearAlgebra.Complex32 { - using Solvers; - internal class MatrixBuilder : MatrixBuilder { public override Numerics.Complex32 Zero @@ -399,28 +391,52 @@ namespace MathNet.Numerics.LinearAlgebra /// /// Create a new matrix with the same kind of the provided example. /// - public Matrix SameType(Matrix example, int rows, int columns) + public Matrix SameAs(Matrix example, int rows, int columns, bool fullyMutable = false) { var storage = example.Storage; if (storage is DenseColumnMajorMatrixStorage) return Dense(rows, columns); - if (storage is DiagonalMatrixStorage) return Diagonal(rows, columns); + if (!fullyMutable && storage is DiagonalMatrixStorage) return Diagonal(rows, columns); if (storage is SparseCompressedRowMatrixStorage) return Sparse(rows, columns); return Dense(rows, columns); } + /// + /// Create a new matrix with the same kind and dimensions of the provided example. + /// + public Matrix SameAs(Matrix example) + { + return SameAs(example, example.RowCount, example.ColumnCount); + } + + /// + /// Create a new matrix with the same kind of the provided example. + /// + public Matrix SameAs(Vector example, int rows, int columns) + { + return example.Storage.IsDense ? Dense(rows, columns) : Sparse(rows, columns); + } + /// /// Create a new matrix with a type that can represent and is closest to both provided samples. /// - public Matrix SameType(Matrix example1, Matrix example2 , int rows, int columns) + public Matrix SameAs(Matrix example, Matrix otherExample, int rows, int columns, bool fullyMutable = false) { - var storage1 = example1.Storage; - var storage2 = example2.Storage; + var storage1 = example.Storage; + var storage2 = otherExample.Storage; if (storage1 is DenseColumnMajorMatrixStorage || storage2 is DenseColumnMajorMatrixStorage) return Dense(rows, columns); - if (storage1 is DiagonalMatrixStorage && storage2 is DiagonalMatrixStorage) return Diagonal(rows, columns); + if (!fullyMutable && storage1 is DiagonalMatrixStorage && storage2 is DiagonalMatrixStorage) return Diagonal(rows, columns); if (storage1 is SparseCompressedRowMatrixStorage || storage2 is SparseCompressedRowMatrixStorage) return Sparse(rows, columns); return Dense(rows, columns); } + /// + /// Create a new matrix with a type that can represent and is closest to both provided samples and the dimensions of example. + /// + public Matrix SameAs(Matrix example, Matrix otherExample) + { + return SameAs(example, otherExample, example.RowCount, example.ColumnCount); + } + /// /// Create a new dense matrix with values sampled from the provided random distribution. /// @@ -1278,7 +1294,23 @@ namespace MathNet.Numerics.LinearAlgebra /// /// Create a new vector with the same kind of the provided example. /// - public Vector SameType(Vector example, int length) + public Vector SameAs(Vector example, int length) + { + return example.Storage.IsDense ? Dense(length) : Sparse(length); + } + + /// + /// Create a new vector with the same kind and dimension of the provided example. + /// + public Vector SameAs(Vector example) + { + return example.Storage.IsDense ? Dense(example.Count) : Sparse(example.Count); + } + + /// + /// Create a new vector with the same kind of the provided example. + /// + public Vector SameAs(Matrix example, int length) { return example.Storage.IsDense ? Dense(length) : Sparse(length); } @@ -1286,9 +1318,17 @@ namespace MathNet.Numerics.LinearAlgebra /// /// Create a new vector with a type that can represent and is closest to both provided samples. /// - public Vector SameType(Vector example1, Vector example2, int length) + public Vector SameAs(Vector example, Vector otherExample, int length) + { + return example.Storage.IsDense || otherExample.Storage.IsDense ? Dense(length) : Sparse(length); + } + + /// + /// Create a new vector with a type that can represent and is closest to both provided samples and the dimensions of example. + /// + public Vector SameAs(Vector example, Vector otherExample) { - return example1.Storage.IsDense || example2.Storage.IsDense ? Dense(length) : Sparse(length); + return example.Storage.IsDense || otherExample.Storage.IsDense ? Dense(example.Count) : Sparse(example.Count); } /// diff --git a/src/Numerics/LinearAlgebra/Complex/Factorization/UserEvd.cs b/src/Numerics/LinearAlgebra/Complex/Factorization/UserEvd.cs index 5a700087..3a145ae3 100644 --- a/src/Numerics/LinearAlgebra/Complex/Factorization/UserEvd.cs +++ b/src/Numerics/LinearAlgebra/Complex/Factorization/UserEvd.cs @@ -75,7 +75,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization // Initialize matricies for eigenvalues and eigenvectors var eigenVectors = DenseMatrix.CreateIdentity(order); - var blockDiagonal = matrix.CreateMatrix(order, order); + var blockDiagonal = Matrix.Build.SameAs(matrix, order, order); var eigenValues = new DenseVector(order); var isSymmetric = true; diff --git a/src/Numerics/LinearAlgebra/Complex/Factorization/UserGramSchmidt.cs b/src/Numerics/LinearAlgebra/Complex/Factorization/UserGramSchmidt.cs index b22953c0..0ef5cdd0 100644 --- a/src/Numerics/LinearAlgebra/Complex/Factorization/UserGramSchmidt.cs +++ b/src/Numerics/LinearAlgebra/Complex/Factorization/UserGramSchmidt.cs @@ -65,7 +65,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization } var q = matrix.Clone(); - var r = matrix.CreateMatrix(matrix.ColumnCount, matrix.ColumnCount); + var r = Matrix.Build.SameAs(matrix, matrix.ColumnCount, matrix.ColumnCount); for (var k = 0; k < q.ColumnCount; k++) { diff --git a/src/Numerics/LinearAlgebra/Complex/Factorization/UserLU.cs b/src/Numerics/LinearAlgebra/Complex/Factorization/UserLU.cs index 94ce71f5..7c8245ce 100644 --- a/src/Numerics/LinearAlgebra/Complex/Factorization/UserLU.cs +++ b/src/Numerics/LinearAlgebra/Complex/Factorization/UserLU.cs @@ -302,7 +302,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization public override Matrix Inverse() { var order = Factors.RowCount; - var inverse = Factors.CreateMatrix(order, order); + var inverse = Matrix.Build.SameAs(Factors, order, order); for (var i = 0; i < order; i++) { inverse.At(i, i, 1.0); diff --git a/src/Numerics/LinearAlgebra/Complex/Factorization/UserQR.cs b/src/Numerics/LinearAlgebra/Complex/Factorization/UserQR.cs index cd4ca63d..5726ebf1 100644 --- a/src/Numerics/LinearAlgebra/Complex/Factorization/UserQR.cs +++ b/src/Numerics/LinearAlgebra/Complex/Factorization/UserQR.cs @@ -77,7 +77,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization if (method == QRMethod.Full) { r = matrix.Clone(); - q = matrix.CreateMatrix(matrix.RowCount, matrix.RowCount); + q = Matrix.Build.SameAs(matrix, matrix.RowCount, matrix.RowCount); for (var i = 0; i < matrix.RowCount; i++) { diff --git a/src/Numerics/LinearAlgebra/Complex/Factorization/UserSvd.cs b/src/Numerics/LinearAlgebra/Complex/Factorization/UserSvd.cs index fae5f588..3e433691 100644 --- a/src/Numerics/LinearAlgebra/Complex/Factorization/UserSvd.cs +++ b/src/Numerics/LinearAlgebra/Complex/Factorization/UserSvd.cs @@ -69,9 +69,9 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization var nm = Math.Min(matrix.RowCount + 1, matrix.ColumnCount); var matrixCopy = matrix.Clone(); - var s = matrixCopy.CreateVector(nm); - var u = matrixCopy.CreateMatrix(matrixCopy.RowCount, matrixCopy.RowCount); - var vt = matrixCopy.CreateMatrix(matrixCopy.ColumnCount, matrixCopy.ColumnCount); + var s = Vector.Build.SameAs(matrixCopy, nm); + var u = Matrix.Build.SameAs(matrixCopy, matrixCopy.RowCount, matrixCopy.RowCount); + var vt = Matrix.Build.SameAs(matrixCopy, matrixCopy.ColumnCount, matrixCopy.ColumnCount); const int maxiter = 1000; var e = new Complex[matrixCopy.ColumnCount]; @@ -592,7 +592,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Factorization if (matrixCopy.RowCount < matrixCopy.ColumnCount) { nm--; - var tmp = matrixCopy.CreateVector(nm); + var tmp = Vector.Build.SameAs(matrixCopy, nm); for (i = 0; i < nm; i++) { tmp[i] = s[i]; diff --git a/src/Numerics/LinearAlgebra/Complex/Matrix.cs b/src/Numerics/LinearAlgebra/Complex/Matrix.cs index a15cd12c..771391fb 100644 --- a/src/Numerics/LinearAlgebra/Complex/Matrix.cs +++ b/src/Numerics/LinearAlgebra/Complex/Matrix.cs @@ -112,7 +112,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// The conjugate transpose of this matrix. public override Matrix ConjugateTranspose() { - var ret = CreateMatrix(ColumnCount, RowCount); + var ret = Build.SameAs(this, ColumnCount, RowCount); for (var j = 0; j < ColumnCount; j++) { for (var i = 0; i < RowCount; i++) diff --git a/src/Numerics/LinearAlgebra/Complex/SparseMatrix.cs b/src/Numerics/LinearAlgebra/Complex/SparseMatrix.cs index 02daadea..1891fbb9 100644 --- a/src/Numerics/LinearAlgebra/Complex/SparseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex/SparseMatrix.cs @@ -382,7 +382,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// The lower triangle of this matrix. public override Matrix LowerTriangle() { - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); LowerTriangleImpl(result); return result; } @@ -407,7 +407,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex if (ReferenceEquals(this, result)) { - var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); + var tmp = Build.SameAs(result); LowerTriangle(tmp); tmp.CopyTo(result); } @@ -447,7 +447,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// The upper triangle of this matrix. public override Matrix UpperTriangle() { - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); UpperTriangleImpl(result); return result; } @@ -472,7 +472,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex if (ReferenceEquals(this, result)) { - var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); + var tmp = Build.SameAs(result); UpperTriangle(tmp); tmp.CopyTo(result); } @@ -513,7 +513,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// The lower triangle of this matrix. public override Matrix StrictlyLowerTriangle() { - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); StrictlyLowerTriangleImpl(result); return result; } @@ -538,7 +538,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex if (ReferenceEquals(this, result)) { - var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); + var tmp = Build.SameAs(result); StrictlyLowerTriangle(tmp); tmp.CopyTo(result); } @@ -579,7 +579,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// The upper triangle of this matrix. public override Matrix StrictlyUpperTriangle() { - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); StrictlyUpperTriangleImpl(result); return result; } @@ -604,7 +604,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex if (ReferenceEquals(this, result)) { - var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); + var tmp = Build.SameAs(result); StrictlyUpperTriangle(tmp); tmp.CopyTo(result); } diff --git a/src/Numerics/LinearAlgebra/Complex32/Factorization/UserEvd.cs b/src/Numerics/LinearAlgebra/Complex32/Factorization/UserEvd.cs index 087bc5ca..69078437 100644 --- a/src/Numerics/LinearAlgebra/Complex32/Factorization/UserEvd.cs +++ b/src/Numerics/LinearAlgebra/Complex32/Factorization/UserEvd.cs @@ -74,7 +74,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization // Initialize matricies for eigenvalues and eigenvectors var eigenVectors = DenseMatrix.CreateIdentity(order); - var blockDiagonal = matrix.CreateMatrix(order, order); + var blockDiagonal = Matrix.Build.SameAs(matrix, order, order); var eigenValues = new LinearAlgebra.Complex.DenseVector(order); var isSymmetric = true; diff --git a/src/Numerics/LinearAlgebra/Complex32/Factorization/UserGramSchmidt.cs b/src/Numerics/LinearAlgebra/Complex32/Factorization/UserGramSchmidt.cs index e1dac658..175b2140 100644 --- a/src/Numerics/LinearAlgebra/Complex32/Factorization/UserGramSchmidt.cs +++ b/src/Numerics/LinearAlgebra/Complex32/Factorization/UserGramSchmidt.cs @@ -60,7 +60,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization } var q = matrix.Clone(); - var r = matrix.CreateMatrix(matrix.ColumnCount, matrix.ColumnCount); + var r = Matrix.Build.SameAs(matrix, matrix.ColumnCount, matrix.ColumnCount); for (var k = 0; k < q.ColumnCount; k++) { diff --git a/src/Numerics/LinearAlgebra/Complex32/Factorization/UserLU.cs b/src/Numerics/LinearAlgebra/Complex32/Factorization/UserLU.cs index a1e32b88..ad51b57a 100644 --- a/src/Numerics/LinearAlgebra/Complex32/Factorization/UserLU.cs +++ b/src/Numerics/LinearAlgebra/Complex32/Factorization/UserLU.cs @@ -297,7 +297,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization public override Matrix Inverse() { var order = Factors.RowCount; - var inverse = Factors.CreateMatrix(order, order); + var inverse = Matrix.Build.SameAs(Factors, order, order); for (var i = 0; i < order; i++) { inverse.At(i, i, 1.0f); diff --git a/src/Numerics/LinearAlgebra/Complex32/Factorization/UserQR.cs b/src/Numerics/LinearAlgebra/Complex32/Factorization/UserQR.cs index 94542d00..6fab031d 100644 --- a/src/Numerics/LinearAlgebra/Complex32/Factorization/UserQR.cs +++ b/src/Numerics/LinearAlgebra/Complex32/Factorization/UserQR.cs @@ -72,7 +72,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization if (method == QRMethod.Full) { r = matrix.Clone(); - q = matrix.CreateMatrix(matrix.RowCount, matrix.RowCount); + q = Matrix.Build.SameAs(matrix, matrix.RowCount, matrix.RowCount); for (var i = 0; i < matrix.RowCount; i++) { diff --git a/src/Numerics/LinearAlgebra/Complex32/Factorization/UserSvd.cs b/src/Numerics/LinearAlgebra/Complex32/Factorization/UserSvd.cs index b2acf111..0caedd73 100644 --- a/src/Numerics/LinearAlgebra/Complex32/Factorization/UserSvd.cs +++ b/src/Numerics/LinearAlgebra/Complex32/Factorization/UserSvd.cs @@ -64,9 +64,9 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization var nm = Math.Min(matrix.RowCount + 1, matrix.ColumnCount); var matrixCopy = matrix.Clone(); - var s = matrixCopy.CreateVector(nm); - var u = matrixCopy.CreateMatrix(matrixCopy.RowCount, matrixCopy.RowCount); - var vt = matrixCopy.CreateMatrix(matrixCopy.ColumnCount, matrixCopy.ColumnCount); + var s = Vector.Build.SameAs(matrixCopy, nm); + var u = Matrix.Build.SameAs(matrixCopy, matrixCopy.RowCount, matrixCopy.RowCount); + var vt = Matrix.Build.SameAs(matrixCopy, matrixCopy.ColumnCount, matrixCopy.ColumnCount); const int maxiter = 1000; var e = new Complex32[matrixCopy.ColumnCount]; @@ -587,7 +587,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Factorization if (matrixCopy.RowCount < matrixCopy.ColumnCount) { nm--; - var tmp = matrixCopy.CreateVector(nm); + var tmp = Vector.Build.SameAs(matrixCopy, nm); for (i = 0; i < nm; i++) { tmp[i] = s[i]; diff --git a/src/Numerics/LinearAlgebra/Complex32/Matrix.cs b/src/Numerics/LinearAlgebra/Complex32/Matrix.cs index dae6f558..b9f248d2 100644 --- a/src/Numerics/LinearAlgebra/Complex32/Matrix.cs +++ b/src/Numerics/LinearAlgebra/Complex32/Matrix.cs @@ -106,7 +106,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// The conjugate transpose of this matrix. public override Matrix ConjugateTranspose() { - var ret = CreateMatrix(ColumnCount, RowCount); + var ret = Build.SameAs(this, ColumnCount, RowCount); for (var j = 0; j < ColumnCount; j++) { for (var i = 0; i < RowCount; i++) diff --git a/src/Numerics/LinearAlgebra/Complex32/SparseMatrix.cs b/src/Numerics/LinearAlgebra/Complex32/SparseMatrix.cs index 2b6a13bf..311aec5a 100644 --- a/src/Numerics/LinearAlgebra/Complex32/SparseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex32/SparseMatrix.cs @@ -377,7 +377,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// The lower triangle of this matrix. public override Matrix LowerTriangle() { - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); LowerTriangleImpl(result); return result; } @@ -402,7 +402,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 if (ReferenceEquals(this, result)) { - var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); + var tmp = Build.SameAs(result); LowerTriangle(tmp); tmp.CopyTo(result); } @@ -442,7 +442,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// The upper triangle of this matrix. public override Matrix UpperTriangle() { - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); UpperTriangleImpl(result); return result; } @@ -467,7 +467,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 if (ReferenceEquals(this, result)) { - var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); + var tmp = Build.SameAs(result); UpperTriangle(tmp); tmp.CopyTo(result); } @@ -508,7 +508,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// The lower triangle of this matrix. public override Matrix StrictlyLowerTriangle() { - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); StrictlyLowerTriangleImpl(result); return result; } @@ -533,7 +533,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 if (ReferenceEquals(this, result)) { - var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); + var tmp = Build.SameAs(result); StrictlyLowerTriangle(tmp); tmp.CopyTo(result); } @@ -574,7 +574,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// The upper triangle of this matrix. public override Matrix StrictlyUpperTriangle() { - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); StrictlyUpperTriangleImpl(result); return result; } @@ -599,7 +599,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 if (ReferenceEquals(this, result)) { - var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); + var tmp = Build.SameAs(result); StrictlyUpperTriangle(tmp); tmp.CopyTo(result); } diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/UserEvd.cs b/src/Numerics/LinearAlgebra/Double/Factorization/UserEvd.cs index c0a4c97d..4ee3a7dd 100644 --- a/src/Numerics/LinearAlgebra/Double/Factorization/UserEvd.cs +++ b/src/Numerics/LinearAlgebra/Double/Factorization/UserEvd.cs @@ -74,8 +74,8 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization var order = matrix.RowCount; // Initialize matricies for eigenvalues and eigenvectors - var eigenVectors = matrix.CreateMatrix(order, order); - var blockDiagonal = matrix.CreateMatrix(order, order); + var eigenVectors = Matrix.Build.SameAs(matrix, order, order); + var blockDiagonal = Matrix.Build.SameAs(matrix, order, order); var eigenValues = new LinearAlgebra.Complex.DenseVector(order); var isSymmetric = true; diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/UserGramSchmidt.cs b/src/Numerics/LinearAlgebra/Double/Factorization/UserGramSchmidt.cs index 7736651f..569585c9 100644 --- a/src/Numerics/LinearAlgebra/Double/Factorization/UserGramSchmidt.cs +++ b/src/Numerics/LinearAlgebra/Double/Factorization/UserGramSchmidt.cs @@ -58,7 +58,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization } var q = matrix.Clone(); - var r = matrix.CreateMatrix(matrix.ColumnCount, matrix.ColumnCount); + var r = Matrix.Build.SameAs(matrix, matrix.ColumnCount, matrix.ColumnCount); for (var k = 0; k < q.ColumnCount; k++) { diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/UserLU.cs b/src/Numerics/LinearAlgebra/Double/Factorization/UserLU.cs index 860ba0d2..a04041e0 100644 --- a/src/Numerics/LinearAlgebra/Double/Factorization/UserLU.cs +++ b/src/Numerics/LinearAlgebra/Double/Factorization/UserLU.cs @@ -295,7 +295,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization public override Matrix Inverse() { var order = Factors.RowCount; - var inverse = Factors.CreateMatrix(order, order); + var inverse = Matrix.Build.SameAs(Factors, order, order); for (var i = 0; i < order; i++) { inverse.At(i, i, 1.0); diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/UserQR.cs b/src/Numerics/LinearAlgebra/Double/Factorization/UserQR.cs index 7fefa0ff..47459ccb 100644 --- a/src/Numerics/LinearAlgebra/Double/Factorization/UserQR.cs +++ b/src/Numerics/LinearAlgebra/Double/Factorization/UserQR.cs @@ -70,7 +70,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization if (method == QRMethod.Full) { r = matrix.Clone(); - q = matrix.CreateMatrix(matrix.RowCount, matrix.RowCount); + q = Matrix.Build.SameAs(matrix, matrix.RowCount, matrix.RowCount); for (var i = 0; i < matrix.RowCount; i++) { diff --git a/src/Numerics/LinearAlgebra/Double/Factorization/UserSvd.cs b/src/Numerics/LinearAlgebra/Double/Factorization/UserSvd.cs index 186b799d..9aa2397b 100644 --- a/src/Numerics/LinearAlgebra/Double/Factorization/UserSvd.cs +++ b/src/Numerics/LinearAlgebra/Double/Factorization/UserSvd.cs @@ -62,9 +62,9 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization var nm = Math.Min(matrix.RowCount + 1, matrix.ColumnCount); var matrixCopy = matrix.Clone(); - var s = matrixCopy.CreateVector(nm); - var u = matrixCopy.CreateMatrix(matrixCopy.RowCount, matrixCopy.RowCount); - var vt = matrixCopy.CreateMatrix(matrixCopy.ColumnCount, matrixCopy.ColumnCount); + var s = Vector.Build.SameAs(matrixCopy, nm); + var u = Matrix.Build.SameAs(matrixCopy, matrixCopy.RowCount, matrixCopy.RowCount); + var vt = Matrix.Build.SameAs(matrixCopy, matrixCopy.ColumnCount, matrixCopy.ColumnCount); const int maxiter = 1000; var e = new double[matrixCopy.ColumnCount]; @@ -570,7 +570,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Factorization if (matrixCopy.RowCount < matrixCopy.ColumnCount) { nm--; - var tmp = matrixCopy.CreateVector(nm); + var tmp = Vector.Build.SameAs(matrixCopy, nm); for (i = 0; i < nm; i++) { tmp[i] = s[i]; diff --git a/src/Numerics/LinearAlgebra/Double/SparseMatrix.cs b/src/Numerics/LinearAlgebra/Double/SparseMatrix.cs index 80a1cf4b..8ccb916a 100644 --- a/src/Numerics/LinearAlgebra/Double/SparseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Double/SparseMatrix.cs @@ -375,7 +375,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// The lower triangle of this matrix. public override Matrix LowerTriangle() { - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); LowerTriangleImpl(result); return result; } @@ -400,7 +400,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double if (ReferenceEquals(this, result)) { - var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); + var tmp = Build.SameAs(result); LowerTriangle(tmp); tmp.CopyTo(result); } @@ -440,7 +440,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// The upper triangle of this matrix. public override Matrix UpperTriangle() { - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); UpperTriangleImpl(result); return result; } @@ -465,7 +465,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double if (ReferenceEquals(this, result)) { - var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); + var tmp = Build.SameAs(result); UpperTriangle(tmp); tmp.CopyTo(result); } @@ -506,7 +506,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// The lower triangle of this matrix. public override Matrix StrictlyLowerTriangle() { - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); StrictlyLowerTriangleImpl(result); return result; } @@ -531,7 +531,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double if (ReferenceEquals(this, result)) { - var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); + var tmp = Build.SameAs(result); StrictlyLowerTriangle(tmp); tmp.CopyTo(result); } @@ -572,7 +572,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// The upper triangle of this matrix. public override Matrix StrictlyUpperTriangle() { - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); StrictlyUpperTriangleImpl(result); return result; } @@ -597,7 +597,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double if (ReferenceEquals(this, result)) { - var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); + var tmp = Build.SameAs(result); StrictlyUpperTriangle(tmp); tmp.CopyTo(result); } diff --git a/src/Numerics/LinearAlgebra/Factorization/Cholesky.cs b/src/Numerics/LinearAlgebra/Factorization/Cholesky.cs index 07939b4a..40398ba9 100644 --- a/src/Numerics/LinearAlgebra/Factorization/Cholesky.cs +++ b/src/Numerics/LinearAlgebra/Factorization/Cholesky.cs @@ -72,7 +72,7 @@ namespace MathNet.Numerics.LinearAlgebra.Factorization /// The left hand side , X. public virtual Matrix Solve(Matrix input) { - var x = input.CreateMatrix(input.RowCount, input.ColumnCount); + var x = Matrix.Build.SameAs(input, input.RowCount, input.ColumnCount); Solve(input, x); return x; } @@ -91,7 +91,7 @@ namespace MathNet.Numerics.LinearAlgebra.Factorization /// The left hand side , x. public virtual Vector Solve(Vector input) { - var x = input.CreateVector(input.Count); + var x = Vector.Build.SameAs(input, input.Count); Solve(input, x); return x; } diff --git a/src/Numerics/LinearAlgebra/Factorization/Evd.cs b/src/Numerics/LinearAlgebra/Factorization/Evd.cs index 28785ef5..dd9260a1 100644 --- a/src/Numerics/LinearAlgebra/Factorization/Evd.cs +++ b/src/Numerics/LinearAlgebra/Factorization/Evd.cs @@ -109,9 +109,9 @@ namespace MathNet.Numerics.LinearAlgebra.Factorization /// The left hand side , X. public virtual Matrix Solve(Matrix input) { - var result = EigenVectors.CreateMatrix(EigenVectors.ColumnCount, input.ColumnCount); - Solve(input, result); - return result; + var x = Matrix.Build.SameAs(EigenVectors, EigenVectors.ColumnCount, input.ColumnCount); + Solve(input, x); + return x; } /// @@ -128,7 +128,7 @@ namespace MathNet.Numerics.LinearAlgebra.Factorization /// The left hand side , x. public virtual Vector Solve(Vector input) { - var x = EigenVectors.CreateVector(EigenVectors.ColumnCount); + var x = Vector.Build.SameAs(EigenVectors, EigenVectors.ColumnCount); Solve(input, x); return x; } diff --git a/src/Numerics/LinearAlgebra/Factorization/LU.cs b/src/Numerics/LinearAlgebra/Factorization/LU.cs index abcf0ff2..9161e13c 100644 --- a/src/Numerics/LinearAlgebra/Factorization/LU.cs +++ b/src/Numerics/LinearAlgebra/Factorization/LU.cs @@ -111,7 +111,7 @@ namespace MathNet.Numerics.LinearAlgebra.Factorization /// The left hand side , X. public virtual Matrix Solve(Matrix input) { - var x = input.CreateMatrix(input.RowCount, input.ColumnCount); + var x = Matrix.Build.SameAs(input, input.RowCount, input.ColumnCount); Solve(input, x); return x; } @@ -130,7 +130,7 @@ namespace MathNet.Numerics.LinearAlgebra.Factorization /// The left hand side , x. public virtual Vector Solve(Vector input) { - var x = input.CreateVector(input.Count); + var x = Vector.Build.SameAs(input, input.Count); Solve(input, x); return x; } diff --git a/src/Numerics/LinearAlgebra/Factorization/QR.cs b/src/Numerics/LinearAlgebra/Factorization/QR.cs index 6535b0c6..d7b069a1 100644 --- a/src/Numerics/LinearAlgebra/Factorization/QR.cs +++ b/src/Numerics/LinearAlgebra/Factorization/QR.cs @@ -109,9 +109,9 @@ namespace MathNet.Numerics.LinearAlgebra.Factorization /// The left hand side , X. public virtual Matrix Solve(Matrix input) { - var matrixX = input.CreateMatrix(FullR.ColumnCount, input.ColumnCount); - Solve(input, matrixX); - return matrixX; + var x = Matrix.Build.SameAs(input, FullR.ColumnCount, input.ColumnCount); + Solve(input, x); + return x; } /// @@ -128,7 +128,7 @@ namespace MathNet.Numerics.LinearAlgebra.Factorization /// The left hand side , x. public virtual Vector Solve(Vector input) { - var x = input.CreateVector(FullR.ColumnCount); + var x = Vector.Build.SameAs(input, FullR.ColumnCount); Solve(input, x); return x; } diff --git a/src/Numerics/LinearAlgebra/Factorization/Svd.cs b/src/Numerics/LinearAlgebra/Factorization/Svd.cs index 0e3dc2f7..fb762493 100644 --- a/src/Numerics/LinearAlgebra/Factorization/Svd.cs +++ b/src/Numerics/LinearAlgebra/Factorization/Svd.cs @@ -71,7 +71,7 @@ namespace MathNet.Numerics.LinearAlgebra.Factorization { var rows = U.RowCount; var columns = VT.ColumnCount; - var result = U.CreateMatrix(rows, columns); + var result = Matrix.Build.SameAs(U, rows, columns); for (var i = 0; i < rows; i++) { for (var j = 0; j < columns; j++) @@ -145,9 +145,9 @@ namespace MathNet.Numerics.LinearAlgebra.Factorization throw new InvalidOperationException(Resources.SingularVectorsNotComputed); } - var result = U.CreateMatrix(VT.ColumnCount, input.ColumnCount); - Solve(input, result); - return result; + var x = Matrix.Build.SameAs(U, VT.ColumnCount, input.ColumnCount); + Solve(input, x); + return x; } /// @@ -169,7 +169,7 @@ namespace MathNet.Numerics.LinearAlgebra.Factorization throw new InvalidOperationException(Resources.SingularVectorsNotComputed); } - var x = U.CreateVector(VT.ColumnCount); + var x = Vector.Build.SameAs(U, VT.ColumnCount); Solve(input, x); return x; } diff --git a/src/Numerics/LinearAlgebra/Matrix.Arithmetic.cs b/src/Numerics/LinearAlgebra/Matrix.Arithmetic.cs index 3a976ecc..5a997cdf 100644 --- a/src/Numerics/LinearAlgebra/Matrix.Arithmetic.cs +++ b/src/Numerics/LinearAlgebra/Matrix.Arithmetic.cs @@ -204,7 +204,7 @@ namespace MathNet.Numerics.LinearAlgebra return Clone(); } - var result = Build.SameType(this, RowCount, ColumnCount); + var result = Build.SameAs(this); DoAdd(scalar, result); return result; } @@ -244,7 +244,7 @@ namespace MathNet.Numerics.LinearAlgebra throw DimensionsDontMatch(this, other); } - var result = Build.SameType(this, other, RowCount, ColumnCount); + var result = Build.SameAs(this, other, RowCount, ColumnCount); DoAdd(other, result); return result; } @@ -282,7 +282,7 @@ namespace MathNet.Numerics.LinearAlgebra return Clone(); } - var result = Build.SameType(this, RowCount, ColumnCount); + var result = Build.SameAs(this); DoSubtract(scalar, result); return result; } @@ -316,7 +316,7 @@ namespace MathNet.Numerics.LinearAlgebra /// A new matrix containing the subtraction of the scalar and this matrix. public Matrix SubtractFrom(T scalar) { - var result = Build.SameType(this, RowCount, ColumnCount); + var result = Build.SameAs(this); DoSubtractFrom(scalar, result); return result; } @@ -350,7 +350,7 @@ namespace MathNet.Numerics.LinearAlgebra throw DimensionsDontMatch(this, other); } - var result = Build.SameType(this, other, RowCount, ColumnCount); + var result = Build.SameAs(this, other, RowCount, ColumnCount); DoSubtract(other, result); return result; } @@ -390,10 +390,10 @@ namespace MathNet.Numerics.LinearAlgebra if (scalar.Equals(Zero)) { - return CreateMatrix(RowCount, ColumnCount); + return Build.SameAs(this); } - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); DoMultiply(scalar, result); return result; } @@ -448,7 +448,7 @@ namespace MathNet.Numerics.LinearAlgebra throw new DivideByZeroException(); } - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); DoDivide(scalar, result); return result; } @@ -492,7 +492,7 @@ namespace MathNet.Numerics.LinearAlgebra /// The result of the division. public Matrix DivideByThis(T scalar) { - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); DoDivideByThis(scalar, result); return result; } @@ -531,7 +531,7 @@ namespace MathNet.Numerics.LinearAlgebra throw DimensionsDontMatch(this, rightSide, "rightSide"); } - var ret = CreateVector(RowCount); + var ret = Vector.Build.SameAs(this, RowCount); DoMultiply(rightSide, ret); return ret; } @@ -580,7 +580,7 @@ namespace MathNet.Numerics.LinearAlgebra throw DimensionsDontMatch(this, leftSide, "leftSide"); } - var ret = CreateVector(ColumnCount); + var ret = Vector.Build.SameAs(this, ColumnCount); DoLeftMultiply(leftSide, ret); return ret; } @@ -642,7 +642,7 @@ namespace MathNet.Numerics.LinearAlgebra if (ReferenceEquals(this, result) || ReferenceEquals(other, result)) { - var tmp = Build.SameType(result, result.RowCount, result.ColumnCount); + var tmp = Build.SameAs(result); DoMultiply(other, tmp); tmp.CopyTo(result); } @@ -665,7 +665,7 @@ namespace MathNet.Numerics.LinearAlgebra throw DimensionsDontMatch(this, other); } - var result = Build.SameType(this, other, RowCount, other.ColumnCount); + var result = Build.SameAs(this, other, RowCount, other.ColumnCount); DoMultiply(other, result); return result; } @@ -686,7 +686,7 @@ namespace MathNet.Numerics.LinearAlgebra if (ReferenceEquals(this, result) || ReferenceEquals(other, result)) { - var tmp = Build.SameType(result, result.RowCount, result.ColumnCount); + var tmp = Build.SameAs(result); DoTransposeAndMultiply(other, tmp); tmp.CopyTo(result); } @@ -709,7 +709,7 @@ namespace MathNet.Numerics.LinearAlgebra throw DimensionsDontMatch(this, other); } - var result = Build.SameType(this, other, RowCount, other.RowCount); + var result = Build.SameAs(this, other, RowCount, other.RowCount); DoTransposeAndMultiply(other, result); return result; } @@ -727,7 +727,7 @@ namespace MathNet.Numerics.LinearAlgebra throw DimensionsDontMatch(this, rightSide, "rightSide"); } - var result = CreateVector(ColumnCount); + var result = Vector.Build.SameAs(this, ColumnCount); DoTransposeThisAndMultiply(rightSide, result); return result; } @@ -779,7 +779,7 @@ namespace MathNet.Numerics.LinearAlgebra if (ReferenceEquals(this, result) || ReferenceEquals(other, result)) { - var tmp = Build.SameType(result, result.RowCount, result.ColumnCount); + var tmp = Build.SameAs(result); DoTransposeThisAndMultiply(other, tmp); tmp.CopyTo(result); } @@ -802,7 +802,7 @@ namespace MathNet.Numerics.LinearAlgebra throw DimensionsDontMatch(this, other); } - var result = Build.SameType(this, other, ColumnCount, other.ColumnCount); + var result = Build.SameAs(this, other, ColumnCount, other.ColumnCount); DoTransposeThisAndMultiply(other, result); return result; } @@ -813,7 +813,7 @@ namespace MathNet.Numerics.LinearAlgebra /// A matrix containing the negated values. public Matrix Negate() { - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); DoNegate(result); return result; } @@ -839,7 +839,7 @@ namespace MathNet.Numerics.LinearAlgebra /// A matrix containing the conjugated values. public Matrix Conjugate() { - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); DoConjugate(result); return result; } @@ -866,7 +866,7 @@ namespace MathNet.Numerics.LinearAlgebra /// A matrix containing the results. public Matrix Modulus(T divisor) { - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); DoModulus(divisor, result); return result; } @@ -893,7 +893,7 @@ namespace MathNet.Numerics.LinearAlgebra /// A matrix containing the results. public Matrix ModulusByThis(T dividend) { - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); DoModulusByThis(dividend, result); return result; } @@ -926,7 +926,7 @@ namespace MathNet.Numerics.LinearAlgebra throw DimensionsDontMatch(this, other, "other"); } - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); DoPointwiseMultiply(other, result); return result; } @@ -961,7 +961,7 @@ namespace MathNet.Numerics.LinearAlgebra throw DimensionsDontMatch(this, divisor); } - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); DoPointwiseDivide(divisor, result); return result; } @@ -996,7 +996,7 @@ namespace MathNet.Numerics.LinearAlgebra throw DimensionsDontMatch(this, divisor); } - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); DoPointwiseModulus(divisor, result); return result; } @@ -1074,7 +1074,7 @@ namespace MathNet.Numerics.LinearAlgebra /// The kronecker product of the two matrices. public Matrix KroneckerProduct(Matrix other) { - var result = CreateMatrix(RowCount*other.RowCount, ColumnCount*other.ColumnCount); + var result = Build.SameAs(this, RowCount*other.RowCount, ColumnCount*other.ColumnCount); KroneckerProduct(other, result); return result; } @@ -1115,14 +1115,13 @@ namespace MathNet.Numerics.LinearAlgebra throw new ArgumentOutOfRangeException("p", Resources.ArgumentMustBePositive); } - var ret = CreateMatrix(RowCount, ColumnCount); - + var result = Build.SameAs(this); for (var index = 0; index < ColumnCount; index++) { - ret.SetColumn(index, Column(index).Normalize(p)); + result.SetColumn(index, Column(index).Normalize(p)); } - return ret; + return result; } /// @@ -1138,8 +1137,7 @@ namespace MathNet.Numerics.LinearAlgebra throw new ArgumentOutOfRangeException("p", Resources.ArgumentMustBePositive); } - var ret = CreateMatrix(RowCount, ColumnCount); - + var ret = Build.SameAs(this); for (var index = 0; index < RowCount; index++) { ret.SetRow(index, Row(index).Normalize(p)); diff --git a/src/Numerics/LinearAlgebra/Matrix.Solve.cs b/src/Numerics/LinearAlgebra/Matrix.Solve.cs index 5deafc03..2bee0ca2 100644 --- a/src/Numerics/LinearAlgebra/Matrix.Solve.cs +++ b/src/Numerics/LinearAlgebra/Matrix.Solve.cs @@ -127,9 +127,9 @@ namespace MathNet.Numerics.LinearAlgebra /// The left hand side , X. public Matrix Solve(Matrix input) { - var matrixX = CreateMatrix(ColumnCount, input.ColumnCount); - Solve(input, matrixX); - return matrixX; + var x = Build.SameAs(this, ColumnCount, input.ColumnCount); + Solve(input, x); + return x; } @@ -140,7 +140,7 @@ namespace MathNet.Numerics.LinearAlgebra /// The left hand side , x. public Vector Solve(Vector input) { - var x = CreateVector(ColumnCount); + var x = Vector.Build.SameAs(this, ColumnCount); Solve(input, x); return x; } diff --git a/src/Numerics/LinearAlgebra/Matrix.cs b/src/Numerics/LinearAlgebra/Matrix.cs index b6c18723..543799bf 100644 --- a/src/Numerics/LinearAlgebra/Matrix.cs +++ b/src/Numerics/LinearAlgebra/Matrix.cs @@ -204,7 +204,7 @@ namespace MathNet.Numerics.LinearAlgebra /// public Matrix Clone() { - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); Storage.CopyToUnchecked(result.Storage, skipClearing: true); return result; } @@ -231,31 +231,6 @@ namespace MathNet.Numerics.LinearAlgebra Storage.CopyTo(target.Storage); } - /// - /// Create a matrix of the same kind for the given number of rows and columns. - /// - /// The number of rows. - /// The number of columns. - /// Creates a matrix of the same matrix type as the current matrix. - public Matrix CreateMatrix(int rows, int columns) - { - return Storage.IsDense - ? Build.Dense(rows, columns) - : Build.Sparse(rows, columns); - } - - /// - /// Create a vector of the same kind with the given size. - /// - /// The size of the vector. - /// Creates a vector of the same type as the current matrix. - public Vector CreateVector(int size) - { - return Storage.IsDense - ? Vector.Build.Dense(size) - : Vector.Build.Sparse(size); - } - /// /// Copies a row into an Vector. /// @@ -270,7 +245,7 @@ namespace MathNet.Numerics.LinearAlgebra throw new ArgumentOutOfRangeException("index"); } - var ret = CreateVector(ColumnCount); + var ret = Vector.Build.SameAs(this, ColumnCount); Storage.CopySubRowToUnchecked(ret.Storage, index, 0, 0, ColumnCount); return ret; } @@ -310,7 +285,7 @@ namespace MathNet.Numerics.LinearAlgebra /// If is not positive. public Vector Row(int rowIndex, int columnIndex, int length) { - var ret = CreateVector(length); + var ret = Vector.Build.SameAs(this, length); Storage.CopySubRowTo(ret.Storage, rowIndex, columnIndex, 0, length); return ret; } @@ -355,7 +330,7 @@ namespace MathNet.Numerics.LinearAlgebra throw new ArgumentOutOfRangeException("index"); } - var ret = CreateVector(RowCount); + var ret = Vector.Build.SameAs(this, RowCount); Storage.CopySubColumnToUnchecked(ret.Storage, index, 0, 0, RowCount); return ret; } @@ -396,7 +371,7 @@ namespace MathNet.Numerics.LinearAlgebra /// If is not positive. public Vector Column(int columnIndex, int rowIndex, int length) { - var ret = CreateVector(length); + var ret = Vector.Build.SameAs(this, length); Storage.CopySubColumnTo(ret.Storage, columnIndex, rowIndex, 0, length); return ret; } @@ -433,17 +408,15 @@ namespace MathNet.Numerics.LinearAlgebra /// The upper triangle of this matrix. public virtual Matrix UpperTriangle() { - var ret = CreateMatrix(RowCount, ColumnCount); - + var result = Build.SameAs(this); for (var row = 0; row < RowCount; row++) { for (var column = row; column < ColumnCount; column++) { - ret.At(row, column, At(row, column)); + result.At(row, column, At(row, column)); } } - - return ret; + return result; } /// @@ -452,17 +425,15 @@ namespace MathNet.Numerics.LinearAlgebra /// The lower triangle of this matrix. public virtual Matrix LowerTriangle() { - var ret = CreateMatrix(RowCount, ColumnCount); - + var result = Build.SameAs(this); for (var row = 0; row < RowCount; row++) { for (var column = 0; column <= row && column < ColumnCount; column++) { - ret.At(row, column, At(row, column)); + result.At(row, column, At(row, column)); } } - - return ret; + return result; } /// @@ -537,9 +508,9 @@ namespace MathNet.Numerics.LinearAlgebra /// is not positive. public virtual Matrix SubMatrix(int rowIndex, int rowCount, int columnIndex, int columnCount) { - var target = CreateMatrix(rowCount, columnCount); - Storage.CopySubMatrixTo(target.Storage, rowIndex, 0, rowCount, columnIndex, 0, columnCount, skipClearing: true); - return target; + var result = Build.SameAs(this, rowCount, columnCount); + Storage.CopySubMatrixTo(result.Storage, rowIndex, 0, rowCount, columnIndex, 0, columnCount, skipClearing: true); + return result; } /// @@ -551,7 +522,7 @@ namespace MathNet.Numerics.LinearAlgebra public virtual Vector Diagonal() { var min = Math.Min(RowCount, ColumnCount); - var diagonal = CreateVector(min); + var diagonal = Vector.Build.SameAs(this, min); for (var i = 0; i < min; i++) { @@ -568,8 +539,7 @@ namespace MathNet.Numerics.LinearAlgebra /// The lower triangle of this matrix. public virtual Matrix StrictlyLowerTriangle() { - var result = CreateMatrix(RowCount, ColumnCount); - + var result = Build.SameAs(this); for (var row = 0; row < RowCount; row++) { for (var column = 0; column < row; column++) @@ -577,7 +547,6 @@ namespace MathNet.Numerics.LinearAlgebra result.At(row, column, At(row, column)); } } - return result; } @@ -615,8 +584,7 @@ namespace MathNet.Numerics.LinearAlgebra /// The upper triangle of this matrix. public virtual Matrix StrictlyUpperTriangle() { - var result = CreateMatrix(RowCount, ColumnCount); - + var result = Build.SameAs(this); for (var row = 0; row < RowCount; row++) { for (var column = row + 1; column < ColumnCount; column++) @@ -624,7 +592,6 @@ namespace MathNet.Numerics.LinearAlgebra result.At(row, column, At(row, column)); } } - return result; } @@ -681,7 +648,7 @@ namespace MathNet.Numerics.LinearAlgebra throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension, "column"); } - var result = CreateMatrix(RowCount, ColumnCount + 1); + var result = Build.SameAs(this, RowCount, ColumnCount + 1); for (var i = 0; i < columnIndex; i++) { @@ -788,7 +755,7 @@ namespace MathNet.Numerics.LinearAlgebra throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension, "row"); } - var result = CreateMatrix(RowCount + 1, ColumnCount); + var result = Build.SameAs(this, RowCount + 1, ColumnCount); for (var i = 0; i < rowIndex; i++) { @@ -997,16 +964,15 @@ namespace MathNet.Numerics.LinearAlgebra /// The transpose of this matrix. public virtual Matrix Transpose() { - var ret = CreateMatrix(ColumnCount, RowCount); + var result = Build.SameAs(this, ColumnCount, RowCount); for (var j = 0; j < ColumnCount; j++) { for (var i = 0; i < RowCount; i++) { - ret.At(j, i, At(i, j)); + result.At(j, i, At(i, j)); } } - - return ret; + return result; } /// @@ -1092,7 +1058,7 @@ namespace MathNet.Numerics.LinearAlgebra throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension); } - var result = CreateMatrix(RowCount, ColumnCount + right.ColumnCount); + var result = Build.SameAs(this, right, RowCount, ColumnCount + right.ColumnCount, fullyMutable: true); Storage.CopySubMatrixToUnchecked(result.Storage, 0, 0, RowCount, 0, 0, ColumnCount, skipClearing: true); right.Storage.CopySubMatrixToUnchecked(result.Storage, 0, 0, right.RowCount, 0, ColumnCount, right.ColumnCount, skipClearing: true); return result; @@ -1152,7 +1118,7 @@ namespace MathNet.Numerics.LinearAlgebra throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension, "lower"); } - var result = CreateMatrix(RowCount + lower.RowCount, ColumnCount); + var result = Build.SameAs(this, lower, RowCount + lower.RowCount, ColumnCount, fullyMutable: true); Storage.CopySubMatrixToUnchecked(result.Storage, 0, 0, RowCount, 0, 0, ColumnCount, skipClearing: true); lower.Storage.CopySubMatrixToUnchecked(result.Storage, 0, RowCount, lower.RowCount, 0, 0, lower.ColumnCount, skipClearing: true); return result; @@ -1210,7 +1176,7 @@ namespace MathNet.Numerics.LinearAlgebra throw new ArgumentNullException("lower"); } - var result = CreateMatrix(RowCount + lower.RowCount, ColumnCount + lower.ColumnCount); + var result = Build.SameAs(this, lower, RowCount + lower.RowCount, ColumnCount + lower.ColumnCount); Storage.CopySubMatrixToUnchecked(result.Storage, 0, 0, RowCount, 0, 0, ColumnCount); lower.Storage.CopySubMatrixToUnchecked(result.Storage, 0, RowCount, lower.RowCount, 0, ColumnCount, lower.ColumnCount); return result; diff --git a/src/Numerics/LinearAlgebra/Single/Factorization/UserEvd.cs b/src/Numerics/LinearAlgebra/Single/Factorization/UserEvd.cs index 4c8640cd..3d5f10cb 100644 --- a/src/Numerics/LinearAlgebra/Single/Factorization/UserEvd.cs +++ b/src/Numerics/LinearAlgebra/Single/Factorization/UserEvd.cs @@ -73,8 +73,8 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization var order = matrix.RowCount; // Initialize matricies for eigenvalues and eigenvectors - var eigenVectors = matrix.CreateMatrix(order, order); - var blockDiagonal = matrix.CreateMatrix(order, order); + var eigenVectors = Matrix.Build.SameAs(matrix, order, order); + var blockDiagonal = Matrix.Build.SameAs(matrix, order, order); var eigenValues = new LinearAlgebra.Complex.DenseVector(order); var isSymmetric = true; diff --git a/src/Numerics/LinearAlgebra/Single/Factorization/UserGramSchmidt.cs b/src/Numerics/LinearAlgebra/Single/Factorization/UserGramSchmidt.cs index 63f7f242..974f39d7 100644 --- a/src/Numerics/LinearAlgebra/Single/Factorization/UserGramSchmidt.cs +++ b/src/Numerics/LinearAlgebra/Single/Factorization/UserGramSchmidt.cs @@ -58,7 +58,7 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization } var q = matrix.Clone(); - var r = matrix.CreateMatrix(matrix.ColumnCount, matrix.ColumnCount); + var r = Matrix.Build.SameAs(matrix, matrix.ColumnCount, matrix.ColumnCount); for (var k = 0; k < q.ColumnCount; k++) { diff --git a/src/Numerics/LinearAlgebra/Single/Factorization/UserLU.cs b/src/Numerics/LinearAlgebra/Single/Factorization/UserLU.cs index 38d0cae1..895e81d4 100644 --- a/src/Numerics/LinearAlgebra/Single/Factorization/UserLU.cs +++ b/src/Numerics/LinearAlgebra/Single/Factorization/UserLU.cs @@ -295,7 +295,7 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization public override Matrix Inverse() { var order = Factors.RowCount; - var inverse = Factors.CreateMatrix(order, order); + var inverse = Matrix.Build.SameAs(Factors, order, order); for (var i = 0; i < order; i++) { inverse.At(i, i, 1.0f); diff --git a/src/Numerics/LinearAlgebra/Single/Factorization/UserQR.cs b/src/Numerics/LinearAlgebra/Single/Factorization/UserQR.cs index 4b2abd1c..21a2d4df 100644 --- a/src/Numerics/LinearAlgebra/Single/Factorization/UserQR.cs +++ b/src/Numerics/LinearAlgebra/Single/Factorization/UserQR.cs @@ -70,7 +70,7 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization if (method == QRMethod.Full) { r = matrix.Clone(); - q = matrix.CreateMatrix(matrix.RowCount, matrix.RowCount); + q = Matrix.Build.SameAs(matrix, matrix.RowCount, matrix.RowCount); for (var i = 0; i < matrix.RowCount; i++) { diff --git a/src/Numerics/LinearAlgebra/Single/Factorization/UserSvd.cs b/src/Numerics/LinearAlgebra/Single/Factorization/UserSvd.cs index 2d8098a2..c3a5eec5 100644 --- a/src/Numerics/LinearAlgebra/Single/Factorization/UserSvd.cs +++ b/src/Numerics/LinearAlgebra/Single/Factorization/UserSvd.cs @@ -62,9 +62,9 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization var nm = Math.Min(matrix.RowCount + 1, matrix.ColumnCount); var matrixCopy = matrix.Clone(); - var s = matrixCopy.CreateVector(nm); - var u = matrixCopy.CreateMatrix(matrixCopy.RowCount, matrixCopy.RowCount); - var vt = matrixCopy.CreateMatrix(matrixCopy.ColumnCount, matrixCopy.ColumnCount); + var s = Vector.Build.SameAs(matrixCopy, nm); + var u = Matrix.Build.SameAs(matrixCopy, matrixCopy.RowCount, matrixCopy.RowCount); + var vt = Matrix.Build.SameAs(matrixCopy, matrixCopy.ColumnCount, matrixCopy.ColumnCount); const int maxiter = 1000; var e = new float[matrixCopy.ColumnCount]; @@ -570,7 +570,7 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Factorization if (matrixCopy.RowCount < matrixCopy.ColumnCount) { nm--; - var tmp = matrixCopy.CreateVector(nm); + var tmp = Vector.Build.SameAs(matrixCopy, nm); for (i = 0; i < nm; i++) { tmp[i] = s[i]; diff --git a/src/Numerics/LinearAlgebra/Single/SparseMatrix.cs b/src/Numerics/LinearAlgebra/Single/SparseMatrix.cs index ca0bc1ed..9d3adb85 100644 --- a/src/Numerics/LinearAlgebra/Single/SparseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Single/SparseMatrix.cs @@ -375,7 +375,7 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// The lower triangle of this matrix. public override Matrix LowerTriangle() { - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); LowerTriangleImpl(result); return result; } @@ -400,7 +400,7 @@ namespace MathNet.Numerics.LinearAlgebra.Single if (ReferenceEquals(this, result)) { - var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); + var tmp = Build.SameAs(result); LowerTriangle(tmp); tmp.CopyTo(result); } @@ -440,7 +440,7 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// The upper triangle of this matrix. public override Matrix UpperTriangle() { - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); UpperTriangleImpl(result); return result; } @@ -465,7 +465,7 @@ namespace MathNet.Numerics.LinearAlgebra.Single if (ReferenceEquals(this, result)) { - var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); + var tmp = Build.SameAs(result); UpperTriangle(tmp); tmp.CopyTo(result); } @@ -506,7 +506,7 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// The lower triangle of this matrix. public override Matrix StrictlyLowerTriangle() { - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); StrictlyLowerTriangleImpl(result); return result; } @@ -531,7 +531,7 @@ namespace MathNet.Numerics.LinearAlgebra.Single if (ReferenceEquals(this, result)) { - var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); + var tmp = Build.SameAs(result); StrictlyLowerTriangle(tmp); tmp.CopyTo(result); } @@ -572,7 +572,7 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// The upper triangle of this matrix. public override Matrix StrictlyUpperTriangle() { - var result = CreateMatrix(RowCount, ColumnCount); + var result = Build.SameAs(this); StrictlyUpperTriangleImpl(result); return result; } @@ -597,7 +597,7 @@ namespace MathNet.Numerics.LinearAlgebra.Single if (ReferenceEquals(this, result)) { - var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); + var tmp = Build.SameAs(result); StrictlyUpperTriangle(tmp); tmp.CopyTo(result); } diff --git a/src/UnitTests/LinearAlgebraTests/MatrixStructureTheory.cs b/src/UnitTests/LinearAlgebraTests/MatrixStructureTheory.cs index 26333d05..158a31c5 100644 --- a/src/UnitTests/LinearAlgebraTests/MatrixStructureTheory.cs +++ b/src/UnitTests/LinearAlgebraTests/MatrixStructureTheory.cs @@ -155,7 +155,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests [Theory] public void CanGetHashCode(Matrix matrix) { - Assert.That(matrix.GetHashCode(), Is.Not.EqualTo(matrix.CreateMatrix(matrix.RowCount, matrix.ColumnCount).GetHashCode())); + Assert.That(matrix.GetHashCode(), Is.Not.EqualTo(Matrix.Build.SameAs(matrix).GetHashCode())); } [Theory] @@ -163,7 +163,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests { var cleared = matrix.Clone(); cleared.Clear(); - Assert.That(cleared, Is.EqualTo(matrix.CreateMatrix(matrix.RowCount, matrix.ColumnCount))); + Assert.That(cleared, Is.EqualTo(Matrix.Build.SameAs(matrix))); } [Theory] @@ -220,13 +220,13 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests [Theory] public void CanCreateSameKind(Matrix matrix) { - var empty = matrix.CreateMatrix(5, 6); + var empty = Matrix.Build.SameAs(matrix, 5, 6); Assert.That(empty, Is.EqualTo(CreateDenseZero(5, 6))); Assert.That(empty.Storage.IsDense, Is.EqualTo(matrix.Storage.IsDense)); - Assert.That(() => matrix.CreateMatrix(0, 2), Throws.InstanceOf()); - Assert.That(() => matrix.CreateMatrix(2, 0), Throws.InstanceOf()); - Assert.That(() => matrix.CreateMatrix(-1, -1), Throws.InstanceOf()); + Assert.That(() => Matrix.Build.SameAs(matrix, 0, 2), Throws.InstanceOf()); + Assert.That(() => Matrix.Build.SameAs(matrix, 2, 0), Throws.InstanceOf()); + Assert.That(() => Matrix.Build.SameAs(matrix, -1, -1), Throws.InstanceOf()); } [Test] From a0a861e790276a901e207b6b741f399642904120 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Mon, 2 Dec 2013 21:22:28 +0100 Subject: [PATCH 027/109] LA: phase out Vector.CreateMatrix/CreateVector (no longer needed) --- src/FSharp/LinearAlgebra.Vector.fs | 4 +-- .../LinearAlgebra/Complex/SparseVector.cs | 2 +- .../LinearAlgebra/Complex32/SparseVector.cs | 2 +- .../LinearAlgebra/Matrix.Arithmetic.cs | 6 ++-- .../LinearAlgebra/Vector.Arithmetic.cs | 35 +++++++++---------- src/Numerics/LinearAlgebra/Vector.cs | 33 +++-------------- .../Complex/DenseVectorTests.cs | 3 +- .../Complex/SparseVectorTest.cs | 3 +- .../LinearAlgebraTests/Complex/VectorTests.cs | 4 +-- .../Complex32/DenseVectorTests.cs | 3 +- .../Complex32/SparseVectorTest.cs | 3 +- .../Complex32/VectorTests.cs | 4 +-- .../Double/DenseVectorTests.cs | 3 +- .../Double/SparseVectorTest.cs | 3 +- .../LinearAlgebraTests/Double/VectorTests.cs | 4 +-- .../Single/DenseVectorTests.cs | 3 +- .../Single/SparseVectorTest.cs | 3 +- .../LinearAlgebraTests/Single/VectorTests.cs | 4 +-- .../VectorArithmeticTheory.cs | 2 +- 19 files changed, 53 insertions(+), 71 deletions(-) diff --git a/src/FSharp/LinearAlgebra.Vector.fs b/src/FSharp/LinearAlgebra.Vector.fs index 93f0ff18..9a6be296 100644 --- a/src/FSharp/LinearAlgebra.Vector.fs +++ b/src/FSharp/LinearAlgebra.Vector.fs @@ -200,8 +200,8 @@ module Vector = } /// Creates a new vector and inserts the given value at the given index. - let inline insert index value (v: #Vector<_>) = - let newV = v.CreateVector(v.Count + 1) + let inline insert index value (v: #Vector<'T>) = + let newV = Vector<'T>.Build.SameAs(v, v.Count + 1) v.CopySubVectorTo(newV, 0, 0, index) v.CopySubVectorTo(newV, index, index+1, v.Count - index) newV.At(index, value) diff --git a/src/Numerics/LinearAlgebra/Complex/SparseVector.cs b/src/Numerics/LinearAlgebra/Complex/SparseVector.cs index 48e02636..b75cdbda 100644 --- a/src/Numerics/LinearAlgebra/Complex/SparseVector.cs +++ b/src/Numerics/LinearAlgebra/Complex/SparseVector.cs @@ -141,7 +141,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex { if (ReferenceEquals(this, result)) { - var tmp = CreateVector(Count); + var tmp = Build.SameAs(this); DoConjugate(tmp); tmp.CopyTo(result); } diff --git a/src/Numerics/LinearAlgebra/Complex32/SparseVector.cs b/src/Numerics/LinearAlgebra/Complex32/SparseVector.cs index 86bc894f..2f912994 100644 --- a/src/Numerics/LinearAlgebra/Complex32/SparseVector.cs +++ b/src/Numerics/LinearAlgebra/Complex32/SparseVector.cs @@ -136,7 +136,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 { if (ReferenceEquals(this, result)) { - var tmp = CreateVector(Count); + var tmp = Build.SameAs(this); DoConjugate(tmp); tmp.CopyTo(result); } diff --git a/src/Numerics/LinearAlgebra/Matrix.Arithmetic.cs b/src/Numerics/LinearAlgebra/Matrix.Arithmetic.cs index 5a997cdf..7d4f31a2 100644 --- a/src/Numerics/LinearAlgebra/Matrix.Arithmetic.cs +++ b/src/Numerics/LinearAlgebra/Matrix.Arithmetic.cs @@ -557,7 +557,7 @@ namespace MathNet.Numerics.LinearAlgebra if (ReferenceEquals(rightSide, result)) { - var tmp = result.CreateVector(result.Count); + var tmp = Vector.Build.SameAs(result); DoMultiply(rightSide, tmp); tmp.CopyTo(result); } @@ -606,7 +606,7 @@ namespace MathNet.Numerics.LinearAlgebra if (ReferenceEquals(leftSide, result)) { - var tmp = result.CreateVector(result.Count); + var tmp = Vector.Build.SameAs(result); DoLeftMultiply(leftSide, tmp); tmp.CopyTo(result); } @@ -753,7 +753,7 @@ namespace MathNet.Numerics.LinearAlgebra if (ReferenceEquals(rightSide, result)) { - var tmp = result.CreateVector(result.Count); + var tmp = Vector.Build.SameAs(result); DoTransposeThisAndMultiply(rightSide, tmp); tmp.CopyTo(result); } diff --git a/src/Numerics/LinearAlgebra/Vector.Arithmetic.cs b/src/Numerics/LinearAlgebra/Vector.Arithmetic.cs index 696355c7..53f6859a 100644 --- a/src/Numerics/LinearAlgebra/Vector.Arithmetic.cs +++ b/src/Numerics/LinearAlgebra/Vector.Arithmetic.cs @@ -178,7 +178,7 @@ namespace MathNet.Numerics.LinearAlgebra return Clone(); } - var result = CreateVector(Count); + var result = Build.SameAs(this); DoAdd(scalar, result); return result; } @@ -218,7 +218,7 @@ namespace MathNet.Numerics.LinearAlgebra throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other"); } - var result = CreateVector(Count); + var result = Build.SameAs(this); DoAdd(other, result); return result; } @@ -252,7 +252,7 @@ namespace MathNet.Numerics.LinearAlgebra return Clone(); } - var result = CreateVector(Count); + var result = Build.SameAs(this); DoSubtract(scalar, result); return result; } @@ -286,7 +286,7 @@ namespace MathNet.Numerics.LinearAlgebra /// A new vector containing the subtraction of the scalar and this vector. public Vector SubtractFrom(T scalar) { - var result = CreateVector(Count); + var result = Build.SameAs(this); DoSubtractFrom(scalar, result); return result; } @@ -314,7 +314,7 @@ namespace MathNet.Numerics.LinearAlgebra /// Added as an alternative to the unary negation operator. public Vector Negate() { - var retrunVector = CreateVector(Count); + var retrunVector = Build.SameAs(this); DoNegate(retrunVector); return retrunVector; } @@ -346,7 +346,7 @@ namespace MathNet.Numerics.LinearAlgebra throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other"); } - var result = CreateVector(Count); + var result = Build.SameAs(this); DoSubtract(other, result); return result; } @@ -374,7 +374,7 @@ namespace MathNet.Numerics.LinearAlgebra /// Conjugated vector public Vector Conjugate() { - var retrunVector = CreateVector(Count); + var retrunVector = Build.SameAs(this); DoConjugate(retrunVector); return retrunVector; } @@ -407,10 +407,10 @@ namespace MathNet.Numerics.LinearAlgebra if (scalar.Equals(Zero)) { - return CreateVector(Count); + return Build.SameAs(this); } - var result = CreateVector(Count); + var result = Build.SameAs(this); DoMultiply(scalar, result); return result; } @@ -484,7 +484,7 @@ namespace MathNet.Numerics.LinearAlgebra return Clone(); } - var result = CreateVector(Count); + var result = Build.SameAs(this); DoDivide(scalar, result); return result; } @@ -518,7 +518,7 @@ namespace MathNet.Numerics.LinearAlgebra /// A new vector that is the division of the vector and the scalar. public Vector DevideByThis(T scalar) { - var result = CreateVector(Count); + var result = Build.SameAs(this); DoDivideByThis(scalar, result); return result; } @@ -546,7 +546,7 @@ namespace MathNet.Numerics.LinearAlgebra /// A vector containing the result. public Vector Modulus(T divisor) { - var result = CreateVector(Count); + var result = Build.SameAs(this); DoModulus(divisor, result); return result; } @@ -573,7 +573,7 @@ namespace MathNet.Numerics.LinearAlgebra /// A vector containing the result. public Vector ModulusByThis(T dividend) { - var result = CreateVector(Count); + var result = Build.SameAs(this); DoModulusByThis(dividend, result); return result; } @@ -606,7 +606,7 @@ namespace MathNet.Numerics.LinearAlgebra throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other"); } - var result = CreateVector(Count); + var result = Build.SameAs(this); DoPointwiseMultiply(other, result); return result; } @@ -646,7 +646,7 @@ namespace MathNet.Numerics.LinearAlgebra throw new ArgumentException(Resources.ArgumentVectorsSameLength, "divisor"); } - var result = CreateVector(Count); + var result = Build.SameAs(this); DoPointwiseDivide(divisor, result); return result; } @@ -686,7 +686,7 @@ namespace MathNet.Numerics.LinearAlgebra throw new ArgumentException(Resources.ArgumentVectorsSameLength, "divisor"); } - var result = CreateVector(Count); + var result = Build.SameAs(this); DoPointwiseModulus(divisor, result); return result; } @@ -721,8 +721,7 @@ namespace MathNet.Numerics.LinearAlgebra /// Matrix M[i,j] = u[i]*v[j] public static Matrix OuterProduct(Vector u, Vector v) { - var matrix = u.CreateMatrix(u.Count, v.Count); - + var matrix = Matrix.Build.SameAs(u, u.Count, v.Count); for (var i = 0; i < u.Count; i++) { matrix.SetRow(i, v.Multiply(u.At(i))); diff --git a/src/Numerics/LinearAlgebra/Vector.cs b/src/Numerics/LinearAlgebra/Vector.cs index f4b5b8de..ba8199b1 100644 --- a/src/Numerics/LinearAlgebra/Vector.cs +++ b/src/Numerics/LinearAlgebra/Vector.cs @@ -132,38 +132,13 @@ namespace MathNet.Numerics.LinearAlgebra Storage.Clear(index, count); } - /// - /// Create a matrix of the same kind with the provided number of rows and columns. - /// - /// The number of rows. - /// The number of columns. - /// Creates a matrix of the same matrix type as the current matrix. - public Matrix CreateMatrix(int rows, int columns) - { - return Storage.IsDense - ? Matrix.Build.Dense(rows, columns) - : Matrix.Build.Sparse(rows, columns); - } - - /// - /// Create a vector of the same kind with the provided dimension. - /// - /// The size of the vector. - /// Creates a vector of the same type as the current matrix. - public Vector CreateVector(int size) - { - return Storage.IsDense - ? Build.Dense(size) - : Build.Sparse(size); - } - /// /// Returns a deep-copy clone of the vector. /// /// A deep-copy clone of the vector. public Vector Clone() { - var result = CreateVector(Count); + var result = Build.SameAs(this); Storage.CopyToUnchecked(result.Storage, skipClearing: true); return result; } @@ -209,7 +184,7 @@ namespace MathNet.Numerics.LinearAlgebra /// If is not positive. public Vector SubVector(int index, int count) { - var target = CreateVector(count); + var target = Build.SameAs(this, count); Storage.CopySubVectorTo(target.Storage, index, 0, count, skipClearing: true); return target; } @@ -270,7 +245,7 @@ namespace MathNet.Numerics.LinearAlgebra /// public Matrix ToColumnMatrix() { - var result = CreateMatrix(Count, 1); + var result = Matrix.Build.SameAs(this, Count, 1); Storage.CopyToColumnUnchecked(result.Storage, 0, skipClearing: true); return result; } @@ -283,7 +258,7 @@ namespace MathNet.Numerics.LinearAlgebra /// public Matrix ToRowMatrix() { - var result = CreateMatrix(1, Count); + var result = Matrix.Build.SameAs(this, 1, Count); Storage.CopyToRowUnchecked(result.Storage, 0, skipClearing: true); return result; } diff --git a/src/UnitTests/LinearAlgebraTests/Complex/DenseVectorTests.cs b/src/UnitTests/LinearAlgebraTests/Complex/DenseVectorTests.cs index fe342e98..dd0015b1 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex/DenseVectorTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex/DenseVectorTests.cs @@ -159,7 +159,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex public void CanCreateDenseMatrix() { var vector = new DenseVector(3); - var matrix = vector.CreateMatrix(2, 3); + var matrix = Matrix.Build.SameAs(vector, 2, 3); + Assert.IsInstanceOf(matrix); Assert.AreEqual(2, matrix.RowCount); Assert.AreEqual(3, matrix.ColumnCount); } diff --git a/src/UnitTests/LinearAlgebraTests/Complex/SparseVectorTest.cs b/src/UnitTests/LinearAlgebraTests/Complex/SparseVectorTest.cs index 7b7d4d8a..312862e6 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex/SparseVectorTest.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex/SparseVectorTest.cs @@ -144,7 +144,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex public void CanCreateSparseMatrix() { var vector = new SparseVector(3); - var matrix = vector.CreateMatrix(2, 3); + var matrix = Matrix.Build.SameAs(vector, 2, 3); + Assert.IsInstanceOf(matrix); Assert.AreEqual(2, matrix.RowCount); Assert.AreEqual(3, matrix.ColumnCount); } diff --git a/src/UnitTests/LinearAlgebraTests/Complex/VectorTests.cs b/src/UnitTests/LinearAlgebraTests/Complex/VectorTests.cs index 264c018f..6b597e08 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex/VectorTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex/VectorTests.cs @@ -150,7 +150,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex public void CanCreateMatrix() { var vector = CreateVector(Data); - var matrix = vector.CreateMatrix(10, 10); + var matrix = Matrix.Build.SameAs(vector, 10, 10); Assert.AreEqual(matrix.RowCount, 10); Assert.AreEqual(matrix.ColumnCount, 10); } @@ -162,7 +162,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex public void CanCreateVector() { var expected = CreateVector(5); - var actual = expected.CreateVector(5); + var actual = Vector.Build.SameAs(expected, 5); Assert.AreEqual(expected.Storage.IsDense, actual.Storage.IsDense, "vectors are same kind."); } diff --git a/src/UnitTests/LinearAlgebraTests/Complex32/DenseVectorTests.cs b/src/UnitTests/LinearAlgebraTests/Complex32/DenseVectorTests.cs index 542aed16..e14f74d3 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex32/DenseVectorTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex32/DenseVectorTests.cs @@ -155,7 +155,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32 public void CanCreateDenseMatrix() { var vector = new DenseVector(3); - var matrix = vector.CreateMatrix(2, 3); + var matrix = Matrix.Build.SameAs(vector, 2, 3); + Assert.IsInstanceOf(matrix); Assert.AreEqual(2, matrix.RowCount); Assert.AreEqual(3, matrix.ColumnCount); } diff --git a/src/UnitTests/LinearAlgebraTests/Complex32/SparseVectorTest.cs b/src/UnitTests/LinearAlgebraTests/Complex32/SparseVectorTest.cs index ad20d01a..e20d7c75 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex32/SparseVectorTest.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex32/SparseVectorTest.cs @@ -140,7 +140,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32 public void CanCreateSparseMatrix() { var vector = new SparseVector(3); - var matrix = vector.CreateMatrix(2, 3); + var matrix = Matrix.Build.SameAs(vector, 2, 3); + Assert.IsInstanceOf(matrix); Assert.AreEqual(2, matrix.RowCount); Assert.AreEqual(3, matrix.ColumnCount); } diff --git a/src/UnitTests/LinearAlgebraTests/Complex32/VectorTests.cs b/src/UnitTests/LinearAlgebraTests/Complex32/VectorTests.cs index b47d5a49..d40a45ba 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex32/VectorTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex32/VectorTests.cs @@ -146,7 +146,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32 public void CanCreateMatrix() { var vector = CreateVector(Data); - var matrix = vector.CreateMatrix(10, 10); + var matrix = Matrix.Build.SameAs(vector, 10, 10); Assert.AreEqual(matrix.RowCount, 10); Assert.AreEqual(matrix.ColumnCount, 10); } @@ -158,7 +158,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32 public void CanCreateVector() { var expected = CreateVector(5); - var actual = expected.CreateVector(5); + var actual = Vector.Build.SameAs(expected, 5); Assert.AreEqual(expected.Storage.IsDense, actual.Storage.IsDense, "vectors are same kind."); } diff --git a/src/UnitTests/LinearAlgebraTests/Double/DenseVectorTests.cs b/src/UnitTests/LinearAlgebraTests/Double/DenseVectorTests.cs index e9405253..daf0e5c4 100644 --- a/src/UnitTests/LinearAlgebraTests/Double/DenseVectorTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Double/DenseVectorTests.cs @@ -153,7 +153,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double public void CanCreateDenseMatrix() { var vector = new DenseVector(3); - var matrix = vector.CreateMatrix(2, 3); + var matrix = Matrix.Build.SameAs(vector, 2, 3); + Assert.IsInstanceOf(matrix); Assert.AreEqual(2, matrix.RowCount); Assert.AreEqual(3, matrix.ColumnCount); } diff --git a/src/UnitTests/LinearAlgebraTests/Double/SparseVectorTest.cs b/src/UnitTests/LinearAlgebraTests/Double/SparseVectorTest.cs index 1a4f9c45..65d2383d 100644 --- a/src/UnitTests/LinearAlgebraTests/Double/SparseVectorTest.cs +++ b/src/UnitTests/LinearAlgebraTests/Double/SparseVectorTest.cs @@ -138,7 +138,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double public void CanCreateSparseMatrix() { var vector = new SparseVector(3); - var matrix = vector.CreateMatrix(2, 3); + var matrix = Matrix.Build.SameAs(vector, 2, 3); + Assert.IsInstanceOf(matrix); Assert.AreEqual(2, matrix.RowCount); Assert.AreEqual(3, matrix.ColumnCount); } diff --git a/src/UnitTests/LinearAlgebraTests/Double/VectorTests.cs b/src/UnitTests/LinearAlgebraTests/Double/VectorTests.cs index 90b25062..716960b3 100644 --- a/src/UnitTests/LinearAlgebraTests/Double/VectorTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Double/VectorTests.cs @@ -143,7 +143,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double public void CanCreateMatrix() { var vector = CreateVector(Data); - var matrix = vector.CreateMatrix(10, 10); + var matrix = Matrix.Build.SameAs(vector, 10, 10); Assert.AreEqual(matrix.RowCount, 10); Assert.AreEqual(matrix.ColumnCount, 10); } @@ -155,7 +155,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double public void CanCreateVector() { var expected = CreateVector(5); - var actual = expected.CreateVector(5); + var actual = Vector.Build.SameAs(expected, 5); Assert.AreEqual(expected.Storage.IsDense, actual.Storage.IsDense, "vectors are same kind."); } diff --git a/src/UnitTests/LinearAlgebraTests/Single/DenseVectorTests.cs b/src/UnitTests/LinearAlgebraTests/Single/DenseVectorTests.cs index 9b407d36..9fd4d679 100644 --- a/src/UnitTests/LinearAlgebraTests/Single/DenseVectorTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Single/DenseVectorTests.cs @@ -153,7 +153,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single public void CanCreateDenseMatrix() { var vector = new DenseVector(3); - var matrix = vector.CreateMatrix(2, 3); + var matrix = Matrix.Build.SameAs(vector, 2, 3); + Assert.IsInstanceOf(matrix); Assert.AreEqual(2, matrix.RowCount); Assert.AreEqual(3, matrix.ColumnCount); } diff --git a/src/UnitTests/LinearAlgebraTests/Single/SparseVectorTest.cs b/src/UnitTests/LinearAlgebraTests/Single/SparseVectorTest.cs index f5d32321..aa9fcdc9 100644 --- a/src/UnitTests/LinearAlgebraTests/Single/SparseVectorTest.cs +++ b/src/UnitTests/LinearAlgebraTests/Single/SparseVectorTest.cs @@ -138,7 +138,8 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single public void CanCreateSparseMatrix() { var vector = new SparseVector(3); - var matrix = vector.CreateMatrix(2, 3); + var matrix = Matrix.Build.SameAs(vector, 2, 3); + Assert.IsInstanceOf(matrix); Assert.AreEqual(2, matrix.RowCount); Assert.AreEqual(3, matrix.ColumnCount); } diff --git a/src/UnitTests/LinearAlgebraTests/Single/VectorTests.cs b/src/UnitTests/LinearAlgebraTests/Single/VectorTests.cs index 0d4e64dd..88cd7ee4 100644 --- a/src/UnitTests/LinearAlgebraTests/Single/VectorTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Single/VectorTests.cs @@ -143,7 +143,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single public void CanCreateMatrix() { var vector = CreateVector(Data); - var matrix = vector.CreateMatrix(10, 10); + var matrix = Matrix.Build.SameAs(vector, 10, 10); Assert.AreEqual(matrix.RowCount, 10); Assert.AreEqual(matrix.ColumnCount, 10); } @@ -155,7 +155,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single public void CanCreateVector() { var expected = CreateVector(5); - var actual = expected.CreateVector(5); + var actual = Vector.Build.SameAs(expected, 5); Assert.AreEqual(expected.Storage.IsDense, actual.Storage.IsDense, "vectors are same kind."); } diff --git a/src/UnitTests/LinearAlgebraTests/VectorArithmeticTheory.cs b/src/UnitTests/LinearAlgebraTests/VectorArithmeticTheory.cs index ea3d90de..0516af38 100644 --- a/src/UnitTests/LinearAlgebraTests/VectorArithmeticTheory.cs +++ b/src/UnitTests/LinearAlgebraTests/VectorArithmeticTheory.cs @@ -72,7 +72,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests var c = vector.Clone(); c.Subtract(vector, c); - Assert.That(c.Equals(vector.CreateVector(vector.Count))); + Assert.That(c.Equals(Vector.Build.SameAs(vector))); } [Theory, Timeout(200)] From b281e58bb2b9524d1c0e9cbed366683dac0673d7 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Mon, 2 Dec 2013 21:48:23 +0100 Subject: [PATCH 028/109] Update release notes, contributors --- CONTRIBUTORS.md | 1 + RELEASENOTES.md | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 1fc532af..29ed544a 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -32,6 +32,7 @@ Feel free to add a link to your personal site/blog and/or twitter handle.* - Robin Neatherway - Anders Gustafsson (cureos) - Andrew Kazyrevich +- Ethar Alali - Iain McDonald (lifebeyondfife) - Candy Chiu - Gauthier Segay diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 81421dcf..8be2e83c 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -52,9 +52,11 @@ Changes as of now: - F#: all functions in the modules now fully generic, including the `matrix` function. - F#: `SkipZeros` instead of the cryptic `nz` suffix for clarity. - Add missing scalar-matrix routines. +- Optimized mixed dense-diagonal and diagonal-dense operations (500x faster on 250k set). - Add point-wise infix operators `.*`, `./`, `.%` where supported (F#) - Vectors explicitly provide proper L1, L2 and L-infinity norms. - All norms return the result as double (instead of the specific value type of the matrix/vector). +- Matrix L-infinity norm now cache-optimized (8-10x faster). - Vectors have a `ConjugateDotProduct` in addition to `DotProduct`. - Matrix Factorization types fully generic, easily accessed by new `Matrix` member methods (replacing the extension methods). Discrete implementations no longer visible. - QR factorization is thin by default. @@ -70,16 +72,21 @@ Changes as of now: - Matrix/Vector creation routines have been simplified and usually no longer require explicit dimensions. New variants to create diagonal matrices, or such where all fields have the same value. All functions that take a params array now have an overload accepting an enumerable (e.g. `OfColumnVectors`). - Generic Matrix/Vector creation using builders, e.g. `Matrix.Build.DenseOfEnumerable(...)` - Create a matrix from a 2D-array of matrices (top-left aligned within the grid). +- Create a matrix or vector with the same structural type as an example (`.Build.SameAs(...)`) +- Removed non-static Matrix/Vector.CreateMatrix/CreateVector routines (no longer needed) - Add Vector.OfArray (copying the array, consistent with Matrix.OfArray - you can still use the dense vector constructor if you want to use the array directly without copying). - More convenient and one more powerful overload of `Matrix.SetSubMatrix`. - Matrices/Vectors expose whether storage is dense with a new IsDense property. - Various minor performance work. +- Matrix.ClearSubMatrix no longer throws on 0 or negative col/row count (nop) - BUG: Fix bug in routine to copy a vector into a sub-row of a matrix. ### Statistics - Pearson and Spearman correlation matrix of a set of arrays. +- Spearman ranked correlation optimized (4x faster on 100k set) - Single-pass `MeanVariance` method (as used often together). +- Some overloads for single-precision values. ### Probability Distributions @@ -97,6 +104,9 @@ Changes as of now: - Reworked `Fit` class, supporting more simple scenarios. - New `.LinearRegression` namespace with more options. - Better support for simple regression in multiple dimensions. +- Goodness of Fit: R, RSquared *~Ethar Alali* +- Weighted polynomial and multi-dim fitting. +- Use more efficient LA routines *~Thomas Ibel* ### Build & Packages @@ -108,7 +118,7 @@ Changes as of now: ### Misc -- New distance functions in `Distance`: euclidean, manhattan, chebychev distance of arrays or generic vectors. SAD, MAE, SSD, MSE metrics. Hamming distance. +- New distance functions in `Distance`: euclidean, manhattan, chebychev distance of arrays or generic vectors. SAD, MAE, SSD, MSE metrics. Pearson's, Canberra and Minkowski distance. Hamming distance. - Interpolation: return tuple instead of out parameter - Integration: simplification of the double-exponential transformation api design. - More robust complex Asin/Acos for large real numbers. From fa1c97abbcc777969e08b98ef80d0b31344be2c5 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Wed, 4 Dec 2013 22:02:36 +0100 Subject: [PATCH 029/109] LA: more sensible choice of return storage type, with tests #146 --- src/Numerics/LinearAlgebra/Builder.cs | 12 +- .../LinearAlgebra/Matrix.Arithmetic.cs | 14 +- src/Numerics/LinearAlgebra/Matrix.cs | 2 +- .../LinearAlgebra/Vector.Arithmetic.cs | 10 +- .../Complex/ReturnTypeTests.cs | 226 ++++++++++++++++++ .../Complex32/ReturnTypeTests.cs | 222 +++++++++++++++++ .../Double/ReturnTypeTests.cs | 220 +++++++++++++++++ .../Single/ReturnTypeTests.cs | 220 +++++++++++++++++ src/UnitTests/UnitTests.csproj | 4 + 9 files changed, 915 insertions(+), 15 deletions(-) create mode 100644 src/UnitTests/LinearAlgebraTests/Complex/ReturnTypeTests.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Complex32/ReturnTypeTests.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Double/ReturnTypeTests.cs create mode 100644 src/UnitTests/LinearAlgebraTests/Single/ReturnTypeTests.cs diff --git a/src/Numerics/LinearAlgebra/Builder.cs b/src/Numerics/LinearAlgebra/Builder.cs index 615c6552..955ede5d 100644 --- a/src/Numerics/LinearAlgebra/Builder.cs +++ b/src/Numerics/LinearAlgebra/Builder.cs @@ -395,7 +395,7 @@ namespace MathNet.Numerics.LinearAlgebra { var storage = example.Storage; if (storage is DenseColumnMajorMatrixStorage) return Dense(rows, columns); - if (!fullyMutable && storage is DiagonalMatrixStorage) return Diagonal(rows, columns); + if (storage is DiagonalMatrixStorage) return fullyMutable ? Sparse(rows, columns) : Diagonal(rows, columns); if (storage is SparseCompressedRowMatrixStorage) return Sparse(rows, columns); return Dense(rows, columns); } @@ -424,7 +424,7 @@ namespace MathNet.Numerics.LinearAlgebra var storage1 = example.Storage; var storage2 = otherExample.Storage; if (storage1 is DenseColumnMajorMatrixStorage || storage2 is DenseColumnMajorMatrixStorage) return Dense(rows, columns); - if (!fullyMutable && storage1 is DiagonalMatrixStorage && storage2 is DiagonalMatrixStorage) return Diagonal(rows, columns); + if (storage1 is DiagonalMatrixStorage && storage2 is DiagonalMatrixStorage) return fullyMutable ? Sparse(rows, columns) : Diagonal(rows, columns); if (storage1 is SparseCompressedRowMatrixStorage || storage2 is SparseCompressedRowMatrixStorage) return Sparse(rows, columns); return Dense(rows, columns); } @@ -1331,6 +1331,14 @@ namespace MathNet.Numerics.LinearAlgebra return example.Storage.IsDense || otherExample.Storage.IsDense ? Dense(example.Count) : Sparse(example.Count); } + /// + /// Create a new vector with a type that can represent and is closest to both provided samples. + /// + public Vector SameAs(Matrix matrix, Vector vector, int length) + { + return matrix.Storage.IsDense || vector.Storage.IsDense ? Dense(length) : Sparse(length); + } + /// /// Create a new dense vector with values sampled from the provided random distribution. /// diff --git a/src/Numerics/LinearAlgebra/Matrix.Arithmetic.cs b/src/Numerics/LinearAlgebra/Matrix.Arithmetic.cs index 7d4f31a2..b6e3b44d 100644 --- a/src/Numerics/LinearAlgebra/Matrix.Arithmetic.cs +++ b/src/Numerics/LinearAlgebra/Matrix.Arithmetic.cs @@ -531,7 +531,7 @@ namespace MathNet.Numerics.LinearAlgebra throw DimensionsDontMatch(this, rightSide, "rightSide"); } - var ret = Vector.Build.SameAs(this, RowCount); + var ret = Vector.Build.SameAs(this, rightSide, RowCount); DoMultiply(rightSide, ret); return ret; } @@ -580,7 +580,7 @@ namespace MathNet.Numerics.LinearAlgebra throw DimensionsDontMatch(this, leftSide, "leftSide"); } - var ret = Vector.Build.SameAs(this, ColumnCount); + var ret = Vector.Build.SameAs(this, leftSide, ColumnCount); DoLeftMultiply(leftSide, ret); return ret; } @@ -727,7 +727,7 @@ namespace MathNet.Numerics.LinearAlgebra throw DimensionsDontMatch(this, rightSide, "rightSide"); } - var result = Vector.Build.SameAs(this, ColumnCount); + var result = Vector.Build.SameAs(this, rightSide, ColumnCount); DoTransposeThisAndMultiply(rightSide, result); return result; } @@ -926,7 +926,7 @@ namespace MathNet.Numerics.LinearAlgebra throw DimensionsDontMatch(this, other, "other"); } - var result = Build.SameAs(this); + var result = Build.SameAs(this, other); DoPointwiseMultiply(other, result); return result; } @@ -961,7 +961,7 @@ namespace MathNet.Numerics.LinearAlgebra throw DimensionsDontMatch(this, divisor); } - var result = Build.SameAs(this); + var result = Build.SameAs(this, divisor); DoPointwiseDivide(divisor, result); return result; } @@ -996,7 +996,7 @@ namespace MathNet.Numerics.LinearAlgebra throw DimensionsDontMatch(this, divisor); } - var result = Build.SameAs(this); + var result = Build.SameAs(this, divisor); DoPointwiseModulus(divisor, result); return result; } @@ -1074,7 +1074,7 @@ namespace MathNet.Numerics.LinearAlgebra /// The kronecker product of the two matrices. public Matrix KroneckerProduct(Matrix other) { - var result = Build.SameAs(this, RowCount*other.RowCount, ColumnCount*other.ColumnCount); + var result = Build.SameAs(this, other, RowCount*other.RowCount, ColumnCount*other.ColumnCount); KroneckerProduct(other, result); return result; } diff --git a/src/Numerics/LinearAlgebra/Matrix.cs b/src/Numerics/LinearAlgebra/Matrix.cs index 543799bf..b349f593 100644 --- a/src/Numerics/LinearAlgebra/Matrix.cs +++ b/src/Numerics/LinearAlgebra/Matrix.cs @@ -1176,7 +1176,7 @@ namespace MathNet.Numerics.LinearAlgebra throw new ArgumentNullException("lower"); } - var result = Build.SameAs(this, lower, RowCount + lower.RowCount, ColumnCount + lower.ColumnCount); + var result = Build.SameAs(this, lower, RowCount + lower.RowCount, ColumnCount + lower.ColumnCount, RowCount != ColumnCount); Storage.CopySubMatrixToUnchecked(result.Storage, 0, 0, RowCount, 0, 0, ColumnCount); lower.Storage.CopySubMatrixToUnchecked(result.Storage, 0, RowCount, lower.RowCount, 0, ColumnCount, lower.ColumnCount); return result; diff --git a/src/Numerics/LinearAlgebra/Vector.Arithmetic.cs b/src/Numerics/LinearAlgebra/Vector.Arithmetic.cs index 53f6859a..5f3f5531 100644 --- a/src/Numerics/LinearAlgebra/Vector.Arithmetic.cs +++ b/src/Numerics/LinearAlgebra/Vector.Arithmetic.cs @@ -218,7 +218,7 @@ namespace MathNet.Numerics.LinearAlgebra throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other"); } - var result = Build.SameAs(this); + var result = Build.SameAs(this, other); DoAdd(other, result); return result; } @@ -346,7 +346,7 @@ namespace MathNet.Numerics.LinearAlgebra throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other"); } - var result = Build.SameAs(this); + var result = Build.SameAs(this, other); DoSubtract(other, result); return result; } @@ -606,7 +606,7 @@ namespace MathNet.Numerics.LinearAlgebra throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other"); } - var result = Build.SameAs(this); + var result = Build.SameAs(this, other); DoPointwiseMultiply(other, result); return result; } @@ -646,7 +646,7 @@ namespace MathNet.Numerics.LinearAlgebra throw new ArgumentException(Resources.ArgumentVectorsSameLength, "divisor"); } - var result = Build.SameAs(this); + var result = Build.SameAs(this, divisor); DoPointwiseDivide(divisor, result); return result; } @@ -686,7 +686,7 @@ namespace MathNet.Numerics.LinearAlgebra throw new ArgumentException(Resources.ArgumentVectorsSameLength, "divisor"); } - var result = Build.SameAs(this); + var result = Build.SameAs(this, divisor); DoPointwiseModulus(divisor, result); return result; } diff --git a/src/UnitTests/LinearAlgebraTests/Complex/ReturnTypeTests.cs b/src/UnitTests/LinearAlgebraTests/Complex/ReturnTypeTests.cs new file mode 100644 index 00000000..17cf89f9 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Complex/ReturnTypeTests.cs @@ -0,0 +1,226 @@ +using MathNet.Numerics.LinearAlgebra; +using MathNet.Numerics.LinearAlgebra.Complex; +using NUnit.Framework; + +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex +{ +#if NOSYSNUMERICS + using Complex = Numerics.Complex; +#else + using Complex = System.Numerics.Complex; +#endif + + [TestFixture] + public class ReturnTypeTests + { + readonly Vector _vectorDense = Vector.Build.Dense(3); + readonly Vector _vectorSparse = Vector.Build.Sparse(3); + readonly Matrix _matrixDense = Matrix.Build.Dense(3, 3); + readonly Matrix _matrixSparse = Matrix.Build.Sparse(3, 3); + readonly Matrix _matrixDiagonal = Matrix.Build.Diagonal(3, 3); + + [Test] + public void VerifyExamples() + { + Assert.That(_vectorDense, Is.TypeOf()); + Assert.That(_vectorSparse, Is.TypeOf()); + Assert.That(_matrixDense, Is.TypeOf()); + Assert.That(_matrixSparse, Is.TypeOf()); + Assert.That(_matrixDiagonal, Is.TypeOf()); + } + + [Test] + public void Negate() + { + Assert.That(_vectorDense.Negate(), Is.InstanceOf()); + Assert.That(_vectorSparse.Negate(), Is.InstanceOf()); + Assert.That(_matrixDense.Negate(), Is.InstanceOf()); + Assert.That(_matrixSparse.Negate(), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Negate(), Is.InstanceOf()); + } + + [Test] + public void Conjugate() + { + Assert.That(_vectorDense.Conjugate(), Is.InstanceOf()); + Assert.That(_vectorSparse.Conjugate(), Is.InstanceOf()); + Assert.That(_matrixDense.Conjugate(), Is.InstanceOf()); + Assert.That(_matrixSparse.Conjugate(), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Conjugate(), Is.InstanceOf()); + } + + [Test] + public void Transpose() + { + Assert.That(_matrixDense.Transpose(), Is.InstanceOf()); + Assert.That(_matrixSparse.Transpose(), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Transpose(), Is.InstanceOf()); + } + + [Test] + public void ConjugateTranspose() + { + Assert.That(_matrixDense.ConjugateTranspose(), Is.InstanceOf()); + Assert.That(_matrixSparse.ConjugateTranspose(), Is.InstanceOf()); + Assert.That(_matrixDiagonal.ConjugateTranspose(), Is.InstanceOf()); + } + + [Test] + public void Add() + { + Assert.That(_vectorDense + _vectorDense, Is.InstanceOf()); + Assert.That(_vectorDense + _vectorSparse, Is.InstanceOf()); + Assert.That(_vectorSparse + _vectorDense, Is.InstanceOf()); + Assert.That(_vectorSparse + _vectorSparse, Is.InstanceOf()); + Assert.That(_matrixDense + _matrixDense, Is.InstanceOf()); + Assert.That(_matrixDense + _matrixSparse, Is.InstanceOf()); + Assert.That(_matrixDense + _matrixDiagonal, Is.InstanceOf()); + Assert.That(_matrixSparse + _matrixDense, Is.InstanceOf()); + Assert.That(_matrixSparse + _matrixSparse, Is.InstanceOf()); + Assert.That(_matrixSparse + _matrixDiagonal, Is.InstanceOf()); + Assert.That(_matrixDiagonal + _matrixDense, Is.InstanceOf()); + Assert.That(_matrixDiagonal + _matrixSparse, Is.InstanceOf()); + Assert.That(_matrixDiagonal + _matrixDiagonal, Is.InstanceOf()); + } + + [Test] + public void Subtract() + { + Assert.That(_vectorDense - _vectorDense, Is.InstanceOf()); + Assert.That(_vectorDense - _vectorSparse, Is.InstanceOf()); + Assert.That(_vectorSparse - _vectorDense, Is.InstanceOf()); + Assert.That(_vectorSparse - _vectorSparse, Is.InstanceOf()); + Assert.That(_matrixDense - _matrixDense, Is.InstanceOf()); + Assert.That(_matrixDense - _matrixSparse, Is.InstanceOf()); + Assert.That(_matrixDense - _matrixDiagonal, Is.InstanceOf()); + Assert.That(_matrixSparse - _matrixDense, Is.InstanceOf()); + Assert.That(_matrixSparse - _matrixSparse, Is.InstanceOf()); + Assert.That(_matrixSparse - _matrixDiagonal, Is.InstanceOf()); + Assert.That(_matrixDiagonal - _matrixDense, Is.InstanceOf()); + Assert.That(_matrixDiagonal - _matrixSparse, Is.InstanceOf()); + Assert.That(_matrixDiagonal - _matrixDiagonal, Is.InstanceOf()); + } + + [Test] + public void PointwiseMultiply() + { + Assert.That(_vectorDense.PointwiseMultiply(_vectorDense), Is.InstanceOf()); + Assert.That(_vectorDense.PointwiseMultiply(_vectorSparse), Is.InstanceOf()); + Assert.That(_vectorSparse.PointwiseMultiply(_vectorDense), Is.InstanceOf()); + Assert.That(_vectorSparse.PointwiseMultiply(_vectorSparse), Is.InstanceOf()); + Assert.That(_matrixDense.PointwiseMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDense.PointwiseMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDense.PointwiseMultiply(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixSparse.PointwiseMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixSparse.PointwiseMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.PointwiseMultiply(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixDiagonal.PointwiseMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.PointwiseMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.PointwiseMultiply(_matrixDiagonal), Is.InstanceOf()); + } + + [Test] + public void MatrixMultiply() + { + Assert.That(_matrixDense*_matrixDense, Is.InstanceOf()); + Assert.That(_matrixDense*_matrixSparse, Is.InstanceOf()); + Assert.That(_matrixDense*_matrixDiagonal, Is.InstanceOf()); + Assert.That(_matrixSparse*_matrixDense, Is.InstanceOf()); + Assert.That(_matrixSparse*_matrixSparse, Is.InstanceOf()); + Assert.That(_matrixSparse*_matrixDiagonal, Is.InstanceOf()); + Assert.That(_matrixDiagonal*_matrixDense, Is.InstanceOf()); + Assert.That(_matrixDiagonal*_matrixSparse, Is.InstanceOf()); + Assert.That(_matrixDiagonal*_matrixDiagonal, Is.InstanceOf()); + + Assert.That(_matrixDense.TransposeThisAndMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDense.TransposeThisAndMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDense.TransposeThisAndMultiply(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeThisAndMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeThisAndMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeThisAndMultiply(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeThisAndMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeThisAndMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeThisAndMultiply(_matrixDiagonal), Is.InstanceOf()); + + Assert.That(_matrixDense.TransposeAndMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDense.TransposeAndMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDense.TransposeAndMultiply(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeAndMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeAndMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeAndMultiply(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeAndMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeAndMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeAndMultiply(_matrixDiagonal), Is.InstanceOf()); + } + + [Test] + public void MatrixVectorMultiply() + { + Assert.That(_matrixDense*_vectorDense, Is.InstanceOf()); + Assert.That(_matrixDense*_vectorSparse, Is.InstanceOf()); + Assert.That(_matrixSparse*_vectorDense, Is.InstanceOf()); + Assert.That(_matrixSparse*_vectorSparse, Is.InstanceOf()); + Assert.That(_matrixDiagonal*_vectorDense, Is.InstanceOf()); + Assert.That(_matrixDiagonal*_vectorSparse, Is.InstanceOf()); + + Assert.That(_vectorDense*_matrixDense, Is.InstanceOf()); + Assert.That(_vectorSparse*_matrixDense, Is.InstanceOf()); + Assert.That(_vectorDense*_matrixSparse, Is.InstanceOf()); + Assert.That(_vectorSparse*_matrixSparse, Is.InstanceOf()); + Assert.That(_vectorDense*_matrixDiagonal, Is.InstanceOf()); + Assert.That(_vectorSparse*_matrixDiagonal, Is.InstanceOf()); + + Assert.That(_matrixDense.TransposeThisAndMultiply(_vectorDense), Is.InstanceOf()); + Assert.That(_matrixDense.TransposeThisAndMultiply(_vectorSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeThisAndMultiply(_vectorDense), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeThisAndMultiply(_vectorSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeThisAndMultiply(_vectorDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeThisAndMultiply(_vectorSparse), Is.InstanceOf()); + } + + [Test] + public void Append() + { + Assert.That(_matrixDense.Append(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDense.Append(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDense.Append(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixSparse.Append(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixSparse.Append(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.Append(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Append(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Append(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Append(_matrixDiagonal), Is.InstanceOf()); + } + + [Test] + public void Stack() + { + Assert.That(_matrixDense.Stack(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDense.Stack(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDense.Stack(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixSparse.Stack(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixSparse.Stack(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.Stack(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Stack(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Stack(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Stack(_matrixDiagonal), Is.InstanceOf()); + } + + [Test] + public void DiagonalStack() + { + Assert.That(_matrixDense.DiagonalStack(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDense.DiagonalStack(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDense.DiagonalStack(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixSparse.DiagonalStack(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixSparse.DiagonalStack(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.DiagonalStack(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixDiagonal.DiagonalStack(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.DiagonalStack(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.DiagonalStack(_matrixDiagonal), Is.InstanceOf()); + + // Special Case + Assert.That(Matrix.Build.DiagonalIdentity(2, 4).DiagonalStack(_matrixDiagonal), Is.InstanceOf()); + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Complex32/ReturnTypeTests.cs b/src/UnitTests/LinearAlgebraTests/Complex32/ReturnTypeTests.cs new file mode 100644 index 00000000..264c6ef7 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Complex32/ReturnTypeTests.cs @@ -0,0 +1,222 @@ +using MathNet.Numerics.LinearAlgebra; +using MathNet.Numerics.LinearAlgebra.Complex32; +using NUnit.Framework; + +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32 +{ + using Numerics; + + [TestFixture] + public class ReturnTypeTests + { + readonly Vector _vectorDense = Vector.Build.Dense(3); + readonly Vector _vectorSparse = Vector.Build.Sparse(3); + readonly Matrix _matrixDense = Matrix.Build.Dense(3, 3); + readonly Matrix _matrixSparse = Matrix.Build.Sparse(3, 3); + readonly Matrix _matrixDiagonal = Matrix.Build.Diagonal(3, 3); + + [Test] + public void VerifyExamples() + { + Assert.That(_vectorDense, Is.TypeOf()); + Assert.That(_vectorSparse, Is.TypeOf()); + Assert.That(_matrixDense, Is.TypeOf()); + Assert.That(_matrixSparse, Is.TypeOf()); + Assert.That(_matrixDiagonal, Is.TypeOf()); + } + + [Test] + public void Negate() + { + Assert.That(_vectorDense.Negate(), Is.InstanceOf()); + Assert.That(_vectorSparse.Negate(), Is.InstanceOf()); + Assert.That(_matrixDense.Negate(), Is.InstanceOf()); + Assert.That(_matrixSparse.Negate(), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Negate(), Is.InstanceOf()); + } + + [Test] + public void Conjugate() + { + Assert.That(_vectorDense.Conjugate(), Is.InstanceOf()); + Assert.That(_vectorSparse.Conjugate(), Is.InstanceOf()); + Assert.That(_matrixDense.Conjugate(), Is.InstanceOf()); + Assert.That(_matrixSparse.Conjugate(), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Conjugate(), Is.InstanceOf()); + } + + [Test] + public void Transpose() + { + Assert.That(_matrixDense.Transpose(), Is.InstanceOf()); + Assert.That(_matrixSparse.Transpose(), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Transpose(), Is.InstanceOf()); + } + + [Test] + public void ConjugateTranspose() + { + Assert.That(_matrixDense.ConjugateTranspose(), Is.InstanceOf()); + Assert.That(_matrixSparse.ConjugateTranspose(), Is.InstanceOf()); + Assert.That(_matrixDiagonal.ConjugateTranspose(), Is.InstanceOf()); + } + + [Test] + public void Add() + { + Assert.That(_vectorDense + _vectorDense, Is.InstanceOf()); + Assert.That(_vectorDense + _vectorSparse, Is.InstanceOf()); + Assert.That(_vectorSparse + _vectorDense, Is.InstanceOf()); + Assert.That(_vectorSparse + _vectorSparse, Is.InstanceOf()); + Assert.That(_matrixDense + _matrixDense, Is.InstanceOf()); + Assert.That(_matrixDense + _matrixSparse, Is.InstanceOf()); + Assert.That(_matrixDense + _matrixDiagonal, Is.InstanceOf()); + Assert.That(_matrixSparse + _matrixDense, Is.InstanceOf()); + Assert.That(_matrixSparse + _matrixSparse, Is.InstanceOf()); + Assert.That(_matrixSparse + _matrixDiagonal, Is.InstanceOf()); + Assert.That(_matrixDiagonal + _matrixDense, Is.InstanceOf()); + Assert.That(_matrixDiagonal + _matrixSparse, Is.InstanceOf()); + Assert.That(_matrixDiagonal + _matrixDiagonal, Is.InstanceOf()); + } + + [Test] + public void Subtract() + { + Assert.That(_vectorDense - _vectorDense, Is.InstanceOf()); + Assert.That(_vectorDense - _vectorSparse, Is.InstanceOf()); + Assert.That(_vectorSparse - _vectorDense, Is.InstanceOf()); + Assert.That(_vectorSparse - _vectorSparse, Is.InstanceOf()); + Assert.That(_matrixDense - _matrixDense, Is.InstanceOf()); + Assert.That(_matrixDense - _matrixSparse, Is.InstanceOf()); + Assert.That(_matrixDense - _matrixDiagonal, Is.InstanceOf()); + Assert.That(_matrixSparse - _matrixDense, Is.InstanceOf()); + Assert.That(_matrixSparse - _matrixSparse, Is.InstanceOf()); + Assert.That(_matrixSparse - _matrixDiagonal, Is.InstanceOf()); + Assert.That(_matrixDiagonal - _matrixDense, Is.InstanceOf()); + Assert.That(_matrixDiagonal - _matrixSparse, Is.InstanceOf()); + Assert.That(_matrixDiagonal - _matrixDiagonal, Is.InstanceOf()); + } + + [Test] + public void PointwiseMultiply() + { + Assert.That(_vectorDense.PointwiseMultiply(_vectorDense), Is.InstanceOf()); + Assert.That(_vectorDense.PointwiseMultiply(_vectorSparse), Is.InstanceOf()); + Assert.That(_vectorSparse.PointwiseMultiply(_vectorDense), Is.InstanceOf()); + Assert.That(_vectorSparse.PointwiseMultiply(_vectorSparse), Is.InstanceOf()); + Assert.That(_matrixDense.PointwiseMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDense.PointwiseMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDense.PointwiseMultiply(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixSparse.PointwiseMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixSparse.PointwiseMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.PointwiseMultiply(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixDiagonal.PointwiseMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.PointwiseMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.PointwiseMultiply(_matrixDiagonal), Is.InstanceOf()); + } + + [Test] + public void MatrixMultiply() + { + Assert.That(_matrixDense*_matrixDense, Is.InstanceOf()); + Assert.That(_matrixDense*_matrixSparse, Is.InstanceOf()); + Assert.That(_matrixDense*_matrixDiagonal, Is.InstanceOf()); + Assert.That(_matrixSparse*_matrixDense, Is.InstanceOf()); + Assert.That(_matrixSparse*_matrixSparse, Is.InstanceOf()); + Assert.That(_matrixSparse*_matrixDiagonal, Is.InstanceOf()); + Assert.That(_matrixDiagonal*_matrixDense, Is.InstanceOf()); + Assert.That(_matrixDiagonal*_matrixSparse, Is.InstanceOf()); + Assert.That(_matrixDiagonal*_matrixDiagonal, Is.InstanceOf()); + + Assert.That(_matrixDense.TransposeThisAndMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDense.TransposeThisAndMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDense.TransposeThisAndMultiply(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeThisAndMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeThisAndMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeThisAndMultiply(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeThisAndMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeThisAndMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeThisAndMultiply(_matrixDiagonal), Is.InstanceOf()); + + Assert.That(_matrixDense.TransposeAndMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDense.TransposeAndMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDense.TransposeAndMultiply(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeAndMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeAndMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeAndMultiply(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeAndMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeAndMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeAndMultiply(_matrixDiagonal), Is.InstanceOf()); + } + + [Test] + public void MatrixVectorMultiply() + { + Assert.That(_matrixDense*_vectorDense, Is.InstanceOf()); + Assert.That(_matrixDense*_vectorSparse, Is.InstanceOf()); + Assert.That(_matrixSparse*_vectorDense, Is.InstanceOf()); + Assert.That(_matrixSparse*_vectorSparse, Is.InstanceOf()); + Assert.That(_matrixDiagonal*_vectorDense, Is.InstanceOf()); + Assert.That(_matrixDiagonal*_vectorSparse, Is.InstanceOf()); + + Assert.That(_vectorDense*_matrixDense, Is.InstanceOf()); + Assert.That(_vectorSparse*_matrixDense, Is.InstanceOf()); + Assert.That(_vectorDense*_matrixSparse, Is.InstanceOf()); + Assert.That(_vectorSparse*_matrixSparse, Is.InstanceOf()); + Assert.That(_vectorDense*_matrixDiagonal, Is.InstanceOf()); + Assert.That(_vectorSparse*_matrixDiagonal, Is.InstanceOf()); + + Assert.That(_matrixDense.TransposeThisAndMultiply(_vectorDense), Is.InstanceOf()); + Assert.That(_matrixDense.TransposeThisAndMultiply(_vectorSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeThisAndMultiply(_vectorDense), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeThisAndMultiply(_vectorSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeThisAndMultiply(_vectorDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeThisAndMultiply(_vectorSparse), Is.InstanceOf()); + } + + [Test] + public void Append() + { + Assert.That(_matrixDense.Append(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDense.Append(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDense.Append(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixSparse.Append(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixSparse.Append(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.Append(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Append(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Append(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Append(_matrixDiagonal), Is.InstanceOf()); + } + + [Test] + public void Stack() + { + Assert.That(_matrixDense.Stack(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDense.Stack(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDense.Stack(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixSparse.Stack(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixSparse.Stack(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.Stack(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Stack(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Stack(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Stack(_matrixDiagonal), Is.InstanceOf()); + } + + [Test] + public void DiagonalStack() + { + Assert.That(_matrixDense.DiagonalStack(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDense.DiagonalStack(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDense.DiagonalStack(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixSparse.DiagonalStack(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixSparse.DiagonalStack(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.DiagonalStack(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixDiagonal.DiagonalStack(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.DiagonalStack(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.DiagonalStack(_matrixDiagonal), Is.InstanceOf()); + + // Special Case + Assert.That(Matrix.Build.DiagonalIdentity(2, 4).DiagonalStack(_matrixDiagonal), Is.InstanceOf()); + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Double/ReturnTypeTests.cs b/src/UnitTests/LinearAlgebraTests/Double/ReturnTypeTests.cs new file mode 100644 index 00000000..482c4ce4 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Double/ReturnTypeTests.cs @@ -0,0 +1,220 @@ +using MathNet.Numerics.LinearAlgebra; +using MathNet.Numerics.LinearAlgebra.Double; +using NUnit.Framework; + +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double +{ + [TestFixture] + public class ReturnTypeTests + { + readonly Vector _vectorDense = Vector.Build.Dense(3); + readonly Vector _vectorSparse = Vector.Build.Sparse(3); + readonly Matrix _matrixDense = Matrix.Build.Dense(3, 3); + readonly Matrix _matrixSparse = Matrix.Build.Sparse(3, 3); + readonly Matrix _matrixDiagonal = Matrix.Build.Diagonal(3, 3); + + [Test] + public void VerifyExamples() + { + Assert.That(_vectorDense, Is.TypeOf()); + Assert.That(_vectorSparse, Is.TypeOf()); + Assert.That(_matrixDense, Is.TypeOf()); + Assert.That(_matrixSparse, Is.TypeOf()); + Assert.That(_matrixDiagonal, Is.TypeOf()); + } + + [Test] + public void Negate() + { + Assert.That(_vectorDense.Negate(), Is.InstanceOf()); + Assert.That(_vectorSparse.Negate(), Is.InstanceOf()); + Assert.That(_matrixDense.Negate(), Is.InstanceOf()); + Assert.That(_matrixSparse.Negate(), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Negate(), Is.InstanceOf()); + } + + [Test] + public void Conjugate() + { + Assert.That(_vectorDense.Conjugate(), Is.InstanceOf()); + Assert.That(_vectorSparse.Conjugate(), Is.InstanceOf()); + Assert.That(_matrixDense.Conjugate(), Is.InstanceOf()); + Assert.That(_matrixSparse.Conjugate(), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Conjugate(), Is.InstanceOf()); + } + + [Test] + public void Transpose() + { + Assert.That(_matrixDense.Transpose(), Is.InstanceOf()); + Assert.That(_matrixSparse.Transpose(), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Transpose(), Is.InstanceOf()); + } + + [Test] + public void ConjugateTranspose() + { + Assert.That(_matrixDense.ConjugateTranspose(), Is.InstanceOf()); + Assert.That(_matrixSparse.ConjugateTranspose(), Is.InstanceOf()); + Assert.That(_matrixDiagonal.ConjugateTranspose(), Is.InstanceOf()); + } + + [Test] + public void Add() + { + Assert.That(_vectorDense + _vectorDense, Is.InstanceOf()); + Assert.That(_vectorDense + _vectorSparse, Is.InstanceOf()); + Assert.That(_vectorSparse + _vectorDense, Is.InstanceOf()); + Assert.That(_vectorSparse + _vectorSparse, Is.InstanceOf()); + Assert.That(_matrixDense + _matrixDense, Is.InstanceOf()); + Assert.That(_matrixDense + _matrixSparse, Is.InstanceOf()); + Assert.That(_matrixDense + _matrixDiagonal, Is.InstanceOf()); + Assert.That(_matrixSparse + _matrixDense, Is.InstanceOf()); + Assert.That(_matrixSparse + _matrixSparse, Is.InstanceOf()); + Assert.That(_matrixSparse + _matrixDiagonal, Is.InstanceOf()); + Assert.That(_matrixDiagonal + _matrixDense, Is.InstanceOf()); + Assert.That(_matrixDiagonal + _matrixSparse, Is.InstanceOf()); + Assert.That(_matrixDiagonal + _matrixDiagonal, Is.InstanceOf()); + } + + [Test] + public void Subtract() + { + Assert.That(_vectorDense - _vectorDense, Is.InstanceOf()); + Assert.That(_vectorDense - _vectorSparse, Is.InstanceOf()); + Assert.That(_vectorSparse - _vectorDense, Is.InstanceOf()); + Assert.That(_vectorSparse - _vectorSparse, Is.InstanceOf()); + Assert.That(_matrixDense - _matrixDense, Is.InstanceOf()); + Assert.That(_matrixDense - _matrixSparse, Is.InstanceOf()); + Assert.That(_matrixDense - _matrixDiagonal, Is.InstanceOf()); + Assert.That(_matrixSparse - _matrixDense, Is.InstanceOf()); + Assert.That(_matrixSparse - _matrixSparse, Is.InstanceOf()); + Assert.That(_matrixSparse - _matrixDiagonal, Is.InstanceOf()); + Assert.That(_matrixDiagonal - _matrixDense, Is.InstanceOf()); + Assert.That(_matrixDiagonal - _matrixSparse, Is.InstanceOf()); + Assert.That(_matrixDiagonal - _matrixDiagonal, Is.InstanceOf()); + } + + [Test] + public void PointwiseMultiply() + { + Assert.That(_vectorDense.PointwiseMultiply(_vectorDense), Is.InstanceOf()); + Assert.That(_vectorDense.PointwiseMultiply(_vectorSparse), Is.InstanceOf()); + Assert.That(_vectorSparse.PointwiseMultiply(_vectorDense), Is.InstanceOf()); + Assert.That(_vectorSparse.PointwiseMultiply(_vectorSparse), Is.InstanceOf()); + Assert.That(_matrixDense.PointwiseMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDense.PointwiseMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDense.PointwiseMultiply(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixSparse.PointwiseMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixSparse.PointwiseMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.PointwiseMultiply(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixDiagonal.PointwiseMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.PointwiseMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.PointwiseMultiply(_matrixDiagonal), Is.InstanceOf()); + } + + [Test] + public void MatrixMultiply() + { + Assert.That(_matrixDense*_matrixDense, Is.InstanceOf()); + Assert.That(_matrixDense*_matrixSparse, Is.InstanceOf()); + Assert.That(_matrixDense*_matrixDiagonal, Is.InstanceOf()); + Assert.That(_matrixSparse*_matrixDense, Is.InstanceOf()); + Assert.That(_matrixSparse*_matrixSparse, Is.InstanceOf()); + Assert.That(_matrixSparse*_matrixDiagonal, Is.InstanceOf()); + Assert.That(_matrixDiagonal*_matrixDense, Is.InstanceOf()); + Assert.That(_matrixDiagonal*_matrixSparse, Is.InstanceOf()); + Assert.That(_matrixDiagonal*_matrixDiagonal, Is.InstanceOf()); + + Assert.That(_matrixDense.TransposeThisAndMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDense.TransposeThisAndMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDense.TransposeThisAndMultiply(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeThisAndMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeThisAndMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeThisAndMultiply(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeThisAndMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeThisAndMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeThisAndMultiply(_matrixDiagonal), Is.InstanceOf()); + + Assert.That(_matrixDense.TransposeAndMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDense.TransposeAndMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDense.TransposeAndMultiply(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeAndMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeAndMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeAndMultiply(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeAndMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeAndMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeAndMultiply(_matrixDiagonal), Is.InstanceOf()); + } + + [Test] + public void MatrixVectorMultiply() + { + Assert.That(_matrixDense*_vectorDense, Is.InstanceOf()); + Assert.That(_matrixDense*_vectorSparse, Is.InstanceOf()); + Assert.That(_matrixSparse*_vectorDense, Is.InstanceOf()); + Assert.That(_matrixSparse*_vectorSparse, Is.InstanceOf()); + Assert.That(_matrixDiagonal*_vectorDense, Is.InstanceOf()); + Assert.That(_matrixDiagonal*_vectorSparse, Is.InstanceOf()); + + Assert.That(_vectorDense*_matrixDense, Is.InstanceOf()); + Assert.That(_vectorSparse*_matrixDense, Is.InstanceOf()); + Assert.That(_vectorDense*_matrixSparse, Is.InstanceOf()); + Assert.That(_vectorSparse*_matrixSparse, Is.InstanceOf()); + Assert.That(_vectorDense*_matrixDiagonal, Is.InstanceOf()); + Assert.That(_vectorSparse*_matrixDiagonal, Is.InstanceOf()); + + Assert.That(_matrixDense.TransposeThisAndMultiply(_vectorDense), Is.InstanceOf()); + Assert.That(_matrixDense.TransposeThisAndMultiply(_vectorSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeThisAndMultiply(_vectorDense), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeThisAndMultiply(_vectorSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeThisAndMultiply(_vectorDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeThisAndMultiply(_vectorSparse), Is.InstanceOf()); + } + + [Test] + public void Append() + { + Assert.That(_matrixDense.Append(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDense.Append(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDense.Append(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixSparse.Append(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixSparse.Append(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.Append(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Append(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Append(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Append(_matrixDiagonal), Is.InstanceOf()); + } + + [Test] + public void Stack() + { + Assert.That(_matrixDense.Stack(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDense.Stack(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDense.Stack(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixSparse.Stack(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixSparse.Stack(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.Stack(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Stack(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Stack(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Stack(_matrixDiagonal), Is.InstanceOf()); + } + + [Test] + public void DiagonalStack() + { + Assert.That(_matrixDense.DiagonalStack(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDense.DiagonalStack(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDense.DiagonalStack(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixSparse.DiagonalStack(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixSparse.DiagonalStack(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.DiagonalStack(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixDiagonal.DiagonalStack(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.DiagonalStack(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.DiagonalStack(_matrixDiagonal), Is.InstanceOf()); + + // Special Case + Assert.That(Matrix.Build.DiagonalIdentity(2, 4).DiagonalStack(_matrixDiagonal), Is.InstanceOf()); + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/ReturnTypeTests.cs b/src/UnitTests/LinearAlgebraTests/Single/ReturnTypeTests.cs new file mode 100644 index 00000000..e43c668a --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/ReturnTypeTests.cs @@ -0,0 +1,220 @@ +using MathNet.Numerics.LinearAlgebra; +using MathNet.Numerics.LinearAlgebra.Single; +using NUnit.Framework; + +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single +{ + [TestFixture] + public class ReturnTypeTests + { + readonly Vector _vectorDense = Vector.Build.Dense(3); + readonly Vector _vectorSparse = Vector.Build.Sparse(3); + readonly Matrix _matrixDense = Matrix.Build.Dense(3, 3); + readonly Matrix _matrixSparse = Matrix.Build.Sparse(3, 3); + readonly Matrix _matrixDiagonal = Matrix.Build.Diagonal(3, 3); + + [Test] + public void VerifyExamples() + { + Assert.That(_vectorDense, Is.TypeOf()); + Assert.That(_vectorSparse, Is.TypeOf()); + Assert.That(_matrixDense, Is.TypeOf()); + Assert.That(_matrixSparse, Is.TypeOf()); + Assert.That(_matrixDiagonal, Is.TypeOf()); + } + + [Test] + public void Negate() + { + Assert.That(_vectorDense.Negate(), Is.InstanceOf()); + Assert.That(_vectorSparse.Negate(), Is.InstanceOf()); + Assert.That(_matrixDense.Negate(), Is.InstanceOf()); + Assert.That(_matrixSparse.Negate(), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Negate(), Is.InstanceOf()); + } + + [Test] + public void Conjugate() + { + Assert.That(_vectorDense.Conjugate(), Is.InstanceOf()); + Assert.That(_vectorSparse.Conjugate(), Is.InstanceOf()); + Assert.That(_matrixDense.Conjugate(), Is.InstanceOf()); + Assert.That(_matrixSparse.Conjugate(), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Conjugate(), Is.InstanceOf()); + } + + [Test] + public void Transpose() + { + Assert.That(_matrixDense.Transpose(), Is.InstanceOf()); + Assert.That(_matrixSparse.Transpose(), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Transpose(), Is.InstanceOf()); + } + + [Test] + public void ConjugateTranspose() + { + Assert.That(_matrixDense.ConjugateTranspose(), Is.InstanceOf()); + Assert.That(_matrixSparse.ConjugateTranspose(), Is.InstanceOf()); + Assert.That(_matrixDiagonal.ConjugateTranspose(), Is.InstanceOf()); + } + + [Test] + public void Add() + { + Assert.That(_vectorDense + _vectorDense, Is.InstanceOf()); + Assert.That(_vectorDense + _vectorSparse, Is.InstanceOf()); + Assert.That(_vectorSparse + _vectorDense, Is.InstanceOf()); + Assert.That(_vectorSparse + _vectorSparse, Is.InstanceOf()); + Assert.That(_matrixDense + _matrixDense, Is.InstanceOf()); + Assert.That(_matrixDense + _matrixSparse, Is.InstanceOf()); + Assert.That(_matrixDense + _matrixDiagonal, Is.InstanceOf()); + Assert.That(_matrixSparse + _matrixDense, Is.InstanceOf()); + Assert.That(_matrixSparse + _matrixSparse, Is.InstanceOf()); + Assert.That(_matrixSparse + _matrixDiagonal, Is.InstanceOf()); + Assert.That(_matrixDiagonal + _matrixDense, Is.InstanceOf()); + Assert.That(_matrixDiagonal + _matrixSparse, Is.InstanceOf()); + Assert.That(_matrixDiagonal + _matrixDiagonal, Is.InstanceOf()); + } + + [Test] + public void Subtract() + { + Assert.That(_vectorDense - _vectorDense, Is.InstanceOf()); + Assert.That(_vectorDense - _vectorSparse, Is.InstanceOf()); + Assert.That(_vectorSparse - _vectorDense, Is.InstanceOf()); + Assert.That(_vectorSparse - _vectorSparse, Is.InstanceOf()); + Assert.That(_matrixDense - _matrixDense, Is.InstanceOf()); + Assert.That(_matrixDense - _matrixSparse, Is.InstanceOf()); + Assert.That(_matrixDense - _matrixDiagonal, Is.InstanceOf()); + Assert.That(_matrixSparse - _matrixDense, Is.InstanceOf()); + Assert.That(_matrixSparse - _matrixSparse, Is.InstanceOf()); + Assert.That(_matrixSparse - _matrixDiagonal, Is.InstanceOf()); + Assert.That(_matrixDiagonal - _matrixDense, Is.InstanceOf()); + Assert.That(_matrixDiagonal - _matrixSparse, Is.InstanceOf()); + Assert.That(_matrixDiagonal - _matrixDiagonal, Is.InstanceOf()); + } + + [Test] + public void PointwiseMultiply() + { + Assert.That(_vectorDense.PointwiseMultiply(_vectorDense), Is.InstanceOf()); + Assert.That(_vectorDense.PointwiseMultiply(_vectorSparse), Is.InstanceOf()); + Assert.That(_vectorSparse.PointwiseMultiply(_vectorDense), Is.InstanceOf()); + Assert.That(_vectorSparse.PointwiseMultiply(_vectorSparse), Is.InstanceOf()); + Assert.That(_matrixDense.PointwiseMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDense.PointwiseMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDense.PointwiseMultiply(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixSparse.PointwiseMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixSparse.PointwiseMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.PointwiseMultiply(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixDiagonal.PointwiseMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.PointwiseMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.PointwiseMultiply(_matrixDiagonal), Is.InstanceOf()); + } + + [Test] + public void MatrixMultiply() + { + Assert.That(_matrixDense*_matrixDense, Is.InstanceOf()); + Assert.That(_matrixDense*_matrixSparse, Is.InstanceOf()); + Assert.That(_matrixDense*_matrixDiagonal, Is.InstanceOf()); + Assert.That(_matrixSparse*_matrixDense, Is.InstanceOf()); + Assert.That(_matrixSparse*_matrixSparse, Is.InstanceOf()); + Assert.That(_matrixSparse*_matrixDiagonal, Is.InstanceOf()); + Assert.That(_matrixDiagonal*_matrixDense, Is.InstanceOf()); + Assert.That(_matrixDiagonal*_matrixSparse, Is.InstanceOf()); + Assert.That(_matrixDiagonal*_matrixDiagonal, Is.InstanceOf()); + + Assert.That(_matrixDense.TransposeThisAndMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDense.TransposeThisAndMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDense.TransposeThisAndMultiply(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeThisAndMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeThisAndMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeThisAndMultiply(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeThisAndMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeThisAndMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeThisAndMultiply(_matrixDiagonal), Is.InstanceOf()); + + Assert.That(_matrixDense.TransposeAndMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDense.TransposeAndMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDense.TransposeAndMultiply(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeAndMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeAndMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeAndMultiply(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeAndMultiply(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeAndMultiply(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeAndMultiply(_matrixDiagonal), Is.InstanceOf()); + } + + [Test] + public void MatrixVectorMultiply() + { + Assert.That(_matrixDense*_vectorDense, Is.InstanceOf()); + Assert.That(_matrixDense*_vectorSparse, Is.InstanceOf()); + Assert.That(_matrixSparse*_vectorDense, Is.InstanceOf()); + Assert.That(_matrixSparse*_vectorSparse, Is.InstanceOf()); + Assert.That(_matrixDiagonal*_vectorDense, Is.InstanceOf()); + Assert.That(_matrixDiagonal*_vectorSparse, Is.InstanceOf()); + + Assert.That(_vectorDense*_matrixDense, Is.InstanceOf()); + Assert.That(_vectorSparse*_matrixDense, Is.InstanceOf()); + Assert.That(_vectorDense*_matrixSparse, Is.InstanceOf()); + Assert.That(_vectorSparse*_matrixSparse, Is.InstanceOf()); + Assert.That(_vectorDense*_matrixDiagonal, Is.InstanceOf()); + Assert.That(_vectorSparse*_matrixDiagonal, Is.InstanceOf()); + + Assert.That(_matrixDense.TransposeThisAndMultiply(_vectorDense), Is.InstanceOf()); + Assert.That(_matrixDense.TransposeThisAndMultiply(_vectorSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeThisAndMultiply(_vectorDense), Is.InstanceOf()); + Assert.That(_matrixSparse.TransposeThisAndMultiply(_vectorSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeThisAndMultiply(_vectorDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.TransposeThisAndMultiply(_vectorSparse), Is.InstanceOf()); + } + + [Test] + public void Append() + { + Assert.That(_matrixDense.Append(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDense.Append(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDense.Append(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixSparse.Append(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixSparse.Append(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.Append(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Append(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Append(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Append(_matrixDiagonal), Is.InstanceOf()); + } + + [Test] + public void Stack() + { + Assert.That(_matrixDense.Stack(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDense.Stack(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDense.Stack(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixSparse.Stack(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixSparse.Stack(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.Stack(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Stack(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Stack(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.Stack(_matrixDiagonal), Is.InstanceOf()); + } + + [Test] + public void DiagonalStack() + { + Assert.That(_matrixDense.DiagonalStack(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDense.DiagonalStack(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDense.DiagonalStack(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixSparse.DiagonalStack(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixSparse.DiagonalStack(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixSparse.DiagonalStack(_matrixDiagonal), Is.InstanceOf()); + Assert.That(_matrixDiagonal.DiagonalStack(_matrixDense), Is.InstanceOf()); + Assert.That(_matrixDiagonal.DiagonalStack(_matrixSparse), Is.InstanceOf()); + Assert.That(_matrixDiagonal.DiagonalStack(_matrixDiagonal), Is.InstanceOf()); + + // Special Case + Assert.That(Matrix.Build.DiagonalIdentity(2, 4).DiagonalStack(_matrixDiagonal), Is.InstanceOf()); + } + } +} diff --git a/src/UnitTests/UnitTests.csproj b/src/UnitTests/UnitTests.csproj index e0868287..fd6ba3bd 100644 --- a/src/UnitTests/UnitTests.csproj +++ b/src/UnitTests/UnitTests.csproj @@ -174,6 +174,7 @@ + @@ -222,6 +223,7 @@ + @@ -250,6 +252,7 @@ + @@ -320,6 +323,7 @@ + From 26b30e590af13ffbaceea03ed33867f4483782a0 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Thu, 5 Dec 2013 00:00:26 +0100 Subject: [PATCH 030/109] Inline docs --- .../Complex/Solvers/MILU0Preconditioner.cs | 1 + .../Complex32/Solvers/MILU0Preconditioner.cs | 1 + .../Double/Solvers/MILU0Preconditioner.cs | 1 + .../Single/Solvers/MILU0Preconditioner.cs | 1 + .../LinearRegression/MultipleRegression.cs | 6 +++--- .../LinearRegression/WeightedRegression.cs | 19 ++++++++++++++++--- src/Numerics/Precision.Equality.cs | 1 + 7 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/Numerics/LinearAlgebra/Complex/Solvers/MILU0Preconditioner.cs b/src/Numerics/LinearAlgebra/Complex/Solvers/MILU0Preconditioner.cs index bc6970f9..bdfef1c7 100644 --- a/src/Numerics/LinearAlgebra/Complex/Solvers/MILU0Preconditioner.cs +++ b/src/Numerics/LinearAlgebra/Complex/Solvers/MILU0Preconditioner.cs @@ -170,6 +170,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex.Solvers /// Matrix values in MSR format (output). /// Row pointers and column indices (output). /// Pointer to diagonal elements (output). + /// True if the modified/MILU algorithm should be used (recommended) /// Returns 0 on success or k > 0 if a zero pivot was encountered at step k. private int Compute(int n, Complex[] a, int[] ja, int[] ia, Complex[] alu, int[] jlu, int[] ju, bool modified) { diff --git a/src/Numerics/LinearAlgebra/Complex32/Solvers/MILU0Preconditioner.cs b/src/Numerics/LinearAlgebra/Complex32/Solvers/MILU0Preconditioner.cs index bfba1c97..bfabc36d 100644 --- a/src/Numerics/LinearAlgebra/Complex32/Solvers/MILU0Preconditioner.cs +++ b/src/Numerics/LinearAlgebra/Complex32/Solvers/MILU0Preconditioner.cs @@ -165,6 +165,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32.Solvers /// Matrix values in MSR format (output). /// Row pointers and column indices (output). /// Pointer to diagonal elements (output). + /// True if the modified/MILU algorithm should be used (recommended) /// Returns 0 on success or k > 0 if a zero pivot was encountered at step k. private int Compute(int n, Complex32[] a, int[] ja, int[] ia, Complex32[] alu, int[] jlu, int[] ju, bool modified) { diff --git a/src/Numerics/LinearAlgebra/Double/Solvers/MILU0Preconditioner.cs b/src/Numerics/LinearAlgebra/Double/Solvers/MILU0Preconditioner.cs index a127d284..5f9683af 100644 --- a/src/Numerics/LinearAlgebra/Double/Solvers/MILU0Preconditioner.cs +++ b/src/Numerics/LinearAlgebra/Double/Solvers/MILU0Preconditioner.cs @@ -163,6 +163,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double.Solvers /// Matrix values in MSR format (output). /// Row pointers and column indices (output). /// Pointer to diagonal elements (output). + /// True if the modified/MILU algorithm should be used (recommended) /// Returns 0 on success or k > 0 if a zero pivot was encountered at step k. private int Compute(int n, double[] a, int[] ja, int[] ia, double[] alu, int[] jlu, int[] ju, bool modified) { diff --git a/src/Numerics/LinearAlgebra/Single/Solvers/MILU0Preconditioner.cs b/src/Numerics/LinearAlgebra/Single/Solvers/MILU0Preconditioner.cs index c81d1bfd..2e26e4dd 100644 --- a/src/Numerics/LinearAlgebra/Single/Solvers/MILU0Preconditioner.cs +++ b/src/Numerics/LinearAlgebra/Single/Solvers/MILU0Preconditioner.cs @@ -163,6 +163,7 @@ namespace MathNet.Numerics.LinearAlgebra.Single.Solvers /// Matrix values in MSR format (output). /// Row pointers and column indices (output). /// Pointer to diagonal elements (output). + /// True if the modified/MILU algorithm should be used (recommended) /// Returns 0 on success or k > 0 if a zero pivot was encountered at step k. private int Compute(int n, float[] a, int[] ja, int[] ia, float[] alu, int[] jlu, int[] ju, bool modified) { diff --git a/src/Numerics/LinearRegression/MultipleRegression.cs b/src/Numerics/LinearRegression/MultipleRegression.cs index f33419d3..819ce7a9 100644 --- a/src/Numerics/LinearRegression/MultipleRegression.cs +++ b/src/Numerics/LinearRegression/MultipleRegression.cs @@ -53,7 +53,7 @@ namespace MathNet.Numerics.LinearRegression /// Uses the cholesky decomposition of the normal equations. /// /// Predictor matrix X - /// Response vector Y + /// Response matrix Y /// Best fitting vector for model parameters β public static Matrix NormalEquations(Matrix x, Matrix y) where T : struct, IEquatable, IFormattable { @@ -109,7 +109,7 @@ namespace MathNet.Numerics.LinearRegression /// Uses an orthogonal decomposition and is therefore more numerically stable than the normal equations but also slower. /// /// Predictor matrix X - /// Response vector Y + /// Response matrix Y /// Best fitting vector for model parameters β public static Matrix QR(Matrix x, Matrix y) where T : struct, IEquatable, IFormattable { @@ -164,7 +164,7 @@ namespace MathNet.Numerics.LinearRegression /// Uses a singular value decomposition and is therefore more numerically stable (especially if ill-conditioned) than the normal equations or QR but also slower. /// /// Predictor matrix X - /// Response vector Y + /// Response matrix Y /// Best fitting vector for model parameters β public static Matrix Svd(Matrix x, Matrix y) where T : struct, IEquatable, IFormattable { diff --git a/src/Numerics/LinearRegression/WeightedRegression.cs b/src/Numerics/LinearRegression/WeightedRegression.cs index 9d7ec732..0271e874 100644 --- a/src/Numerics/LinearRegression/WeightedRegression.cs +++ b/src/Numerics/LinearRegression/WeightedRegression.cs @@ -31,7 +31,6 @@ using System; using System.Collections.Generic; using MathNet.Numerics.LinearAlgebra; -using MathNet.Numerics.LinearAlgebra.Storage; namespace MathNet.Numerics.LinearRegression { @@ -40,6 +39,9 @@ namespace MathNet.Numerics.LinearRegression /// /// Weighted Linear Regression using normal equations. /// + /// Predictor matrix X + /// Response vector Y + /// Weight matrix W, usually diagonal with an entry for each predictor (row). public static Vector Weighted(Matrix x, Vector y, Matrix w) where T : struct, IEquatable, IFormattable { return x.TransposeThisAndMultiply(w*x).Cholesky().Solve(x.TransposeThisAndMultiply(w*y)); @@ -48,6 +50,9 @@ namespace MathNet.Numerics.LinearRegression /// /// Weighted Linear Regression using normal equations. /// + /// Predictor matrix X + /// Response matrix Y + /// Weight matrix W, usually diagonal with an entry for each predictor (row). public static Matrix Weighted(Matrix x, Matrix y, Matrix w) where T : struct, IEquatable, IFormattable { return x.TransposeThisAndMultiply(w*x).Cholesky().Solve(x.TransposeThisAndMultiply(w*y)); @@ -56,6 +61,9 @@ namespace MathNet.Numerics.LinearRegression /// /// Weighted Linear Regression using normal equations. /// + /// Predictor matrix X + /// Response vector Y + /// Weight matrix W, usually diagonal with an entry for each predictor (row). /// True if an intercept should be added as first artificial perdictor value. Default = false. public static T[] Weighted(T[][] x, T[] y, T[] w, bool intercept = false) where T : struct, IEquatable, IFormattable { @@ -72,16 +80,19 @@ namespace MathNet.Numerics.LinearRegression /// /// Weighted Linear Regression using normal equations. /// + /// List of sample vectors (predictor) together with their response. + /// List of weights, one for each sample. /// True if an intercept should be added as first artificial perdictor value. Default = false. - public static T[] Weighted(IEnumerable> samples, T[] w, bool intercept = false) where T : struct, IEquatable, IFormattable + public static T[] Weighted(IEnumerable> samples, T[] weights, bool intercept = false) where T : struct, IEquatable, IFormattable { var xy = samples.UnpackSinglePass(); - return Weighted(xy.Item1, xy.Item2, w, intercept); + return Weighted(xy.Item1, xy.Item2, weights, intercept); } /// /// Locally-Weighted Linear Regression using normal equations. /// + [Obsolete("Warning: This function is here to stay but its signature will likely change.")] public static Vector Local(Matrix x, Vector y, Vector t, double radius, Func kernel) where T : struct, IEquatable, IFormattable { // TODO: Weird kernel definition @@ -96,6 +107,7 @@ namespace MathNet.Numerics.LinearRegression /// /// Locally-Weighted Linear Regression using normal equations. /// + [Obsolete("Warning: This function is here to stay but its signature will likely change.")] public static Matrix Local(Matrix x, Matrix y, Vector t, double radius, Func kernel) where T : struct, IEquatable, IFormattable { // TODO: Weird kernel definition @@ -107,6 +119,7 @@ namespace MathNet.Numerics.LinearRegression return Weighted(x, y, w); } + [Obsolete("Warning: This function is here to stay but will likely be refactored and/or moved to another place.")] public static double GaussianKernel(double normalizedDistance) { return Math.Exp(-0.5*normalizedDistance*normalizedDistance); diff --git a/src/Numerics/Precision.Equality.cs b/src/Numerics/Precision.Equality.cs index e36945bf..870ac5cc 100644 --- a/src/Numerics/Precision.Equality.cs +++ b/src/Numerics/Precision.Equality.cs @@ -397,6 +397,7 @@ namespace MathNet.Numerics /// The norm of the first value (can be negative). /// The norm of the second value (can be negative). /// The norm of the difference of the two values (can be negative). + /// The number of decimal places. /// Thrown if is smaller than zero. public static bool AlmostEqualNormRelative(this double a, double b, double diff, int decimalPlaces) { From 7c6d83464f509b12150d06df2ff943a9ef17dd19 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Thu, 5 Dec 2013 01:38:40 +0100 Subject: [PATCH 031/109] Build: .Net 3.5 variant of Numerics project in portable solution --- MathNet.Numerics.Portable.sln | 8 ++ packages/.gitattributes | 1 - .../TaskParallelLibrary.1.0.2856.0.nupkg | Bin 0 -> 1736532 bytes .../TaskParallelLibrary.1.0.2856.0.nuspec | 24 ++++ .../lib/Net35/System.Threading.chm | Bin 0 -> 1542346 bytes .../lib/Net35/System.Threading.dll | Bin 0 -> 387408 bytes .../lib/Net35/redist.txt | 11 ++ packages/repositories.config | 11 +- src/Numerics/Compatibility.cs | 91 ++++++++++++++ src/Numerics/Complex64.cs | 6 +- src/Numerics/Numerics-Net35.csproj | 111 ++++++++++++++++++ src/Numerics/Numerics.csproj | 2 +- src/Numerics/PortableAttributes.cs | 28 ----- src/Numerics/Properties/Resources.Designer.cs | 2 +- src/Numerics/Random/SystemCrypto.cs | 2 + src/Numerics/Threading/CommonParallel.cs | 16 +-- src/Numerics/packages.config | 4 + 17 files changed, 272 insertions(+), 45 deletions(-) create mode 100644 packages/TaskParallelLibrary.1.0.2856.0/TaskParallelLibrary.1.0.2856.0.nupkg create mode 100644 packages/TaskParallelLibrary.1.0.2856.0/TaskParallelLibrary.1.0.2856.0.nuspec create mode 100644 packages/TaskParallelLibrary.1.0.2856.0/lib/Net35/System.Threading.chm create mode 100644 packages/TaskParallelLibrary.1.0.2856.0/lib/Net35/System.Threading.dll create mode 100644 packages/TaskParallelLibrary.1.0.2856.0/lib/Net35/redist.txt create mode 100644 src/Numerics/Compatibility.cs create mode 100644 src/Numerics/Numerics-Net35.csproj delete mode 100644 src/Numerics/PortableAttributes.cs create mode 100644 src/Numerics/packages.config diff --git a/MathNet.Numerics.Portable.sln b/MathNet.Numerics.Portable.sln index 7a4ad61d..258bde7a 100644 --- a/MathNet.Numerics.Portable.sln +++ b/MathNet.Numerics.Portable.sln @@ -48,6 +48,8 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharpUnitTests-Portable47" EndProject Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharpUnitTests-Portable136", "src\FSharpUnitTests\FSharpUnitTests-Portable136.fsproj", "{1A8315AC-BCDA-4294-B994-FDC4FED45BEE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Numerics-Net35", "src\Numerics\Numerics-Net35.csproj", "{E54E712D-EB6B-4FBF-B29A-6BB95E719BAC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -129,6 +131,12 @@ Global {1A8315AC-BCDA-4294-B994-FDC4FED45BEE}.Release|Any CPU.ActiveCfg = Release|Any CPU {1A8315AC-BCDA-4294-B994-FDC4FED45BEE}.Release|Any CPU.Build.0 = Release|Any CPU {1A8315AC-BCDA-4294-B994-FDC4FED45BEE}.Release-Signed|Any CPU.ActiveCfg = Release|Any CPU + {E54E712D-EB6B-4FBF-B29A-6BB95E719BAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E54E712D-EB6B-4FBF-B29A-6BB95E719BAC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E54E712D-EB6B-4FBF-B29A-6BB95E719BAC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E54E712D-EB6B-4FBF-B29A-6BB95E719BAC}.Release|Any CPU.Build.0 = Release|Any CPU + {E54E712D-EB6B-4FBF-B29A-6BB95E719BAC}.Release-Signed|Any CPU.ActiveCfg = Release-Signed|Any CPU + {E54E712D-EB6B-4FBF-B29A-6BB95E719BAC}.Release-Signed|Any CPU.Build.0 = Release-Signed|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/packages/.gitattributes b/packages/.gitattributes index 374c9471..06a58639 100644 --- a/packages/.gitattributes +++ b/packages/.gitattributes @@ -1,3 +1,2 @@ repositories.config text * -text - diff --git a/packages/TaskParallelLibrary.1.0.2856.0/TaskParallelLibrary.1.0.2856.0.nupkg b/packages/TaskParallelLibrary.1.0.2856.0/TaskParallelLibrary.1.0.2856.0.nupkg new file mode 100644 index 0000000000000000000000000000000000000000..bacb7313cec78d5a614b6ed3651fe930bc1b6353 GIT binary patch literal 1736532 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 |G;^Bz!C<-&{OyBlwb;*zP+C8>*_>}j2` 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

-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

6*;oiQ# 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�=57N zRrO33Es5uWvBev<&Qu$oXotn^xK}gl-HXdb%1$@%Rq!t@lK}pQ2AuG6Q|)Nu2mdk3 z6XR-hBzwTcu-D;s2+o3t+#$bE@0-oXB&n0+_9I*T;Q8rkV|4TclFmETZ9krAg=gm+ z1gd`z?EfiJz61ANU&r)a8$BztE@2d$Y-fJAz~{e08|qkleZCx-y>1a~GSC>18U1q9 z4);!tZ&pbiWhpTB3a~Y*Rc5MIt`!?2!I|Bvby5v7TkndP-i7D$g{iMN@>{FqMow0V zc_xz@4`v;dVY8@MSsh0E#ktvNR8YxDJ1yY^JzeUvujZrMUZ>e!c40%KQ;)*8O>mO@ zt1hk>uM9lnJVvZ&7jRE*JVtSLH=t~EB1-zGF4neUS8~fgmKod}|9nMY0YJ|G+uw8W zlsKR&cV$U_9s4*9xpJfqi$&Pit{Csi+GyF@!)4go!dFNB-4ED%?f?AOn?sEW@bz+8 z_fPojZ-}E3i)*5z5$~~?N!_i}frhbf?5o?XX?O8Iwlws3!U?8q+Gt(AGQU|rWSUKM z2QRX$JC#wz83NMl%!FwjTNOdYUy}@B#AjK7J^&yz0EFpmuYEc_nzydDni@2>R4htL z5*Musg^MjZ*`RsA4-kDpB7~&`kO+W4s5W|Xn0Q2HA`q!L7@@QT_>m{DPG=)O1?Pmh zKHf(5G)O-`DkWKTL^lk@GE5{@N_hfNW-of@O6);=QifO4(wt3}#`Poh2DOh<^hQv9 z7g*RP_>>EFqwg9OCfmH)-2M>}h18@LPxA#H`tc%5Vz{$&_ivn8;8ss@QHYuksY)6) z%+<<=){v>(4~arGAJ@f1*{e13I=o8D7emKoHS0d?ZGJ7n#m!!05id<2QG1q!<(4T?*( z=|@7s3NS!-p5*!msT-;r9FvYT37E+pDC+mqc{VCuFush3nkAECbl<+dh_`H*wjos$ z$)*f&f$dF3i)?!7f=s@~ka^Dh1_>y`^u##X)CUp|=NGRE>REkf$mell+0W;}#JdHw zAtiEA)eJCZ9t**}mbbkDUiHjSU86WPd(3Z3>)Mskj^7r2r%rqEed*`@!1U)$XtfId zMCCClHsmAMTOqtQU_*z%1!rY4wG8T{_OI8=$AKeUwhRU>Db_Ky6Qsn-{FcVgrX9p8 za!OCFk3WTj0_nJPtlCTN8aLlRZBEc-L&16vd5D4e4w=Oc*{35g-u-J&=8#LR% zm2|mfYvn;Je|$VH=2y_aaO|9nQ+h65NvCd`(ii@)5Y~ z`HWk@>4mox#=0o3+9IEviAOh3 z?3HD%phBQ=*0RB_9!c~^=4BeiM$1?8T!^XmUa$4ET`}VyC|^JSASqnA!vcNn#+pq8Z#rA&|CY{jCTw6xI)R*KB;(SN*w<^{o;yx ztF~aE{RQQ_=PA0qNQx0T)L>BFE~oF~UsLLQ7Tn8FKB})K_boC&SH&pH-b$lwMDba7 z0A?Vm65(n-sWQf3MXBS;0cHxs%DFJ$C^PxiZvZs7p56$l*;JC%lj?jxmL0b3U`X<+q7tCsAh}d5qqt%6U`!4(#e%CYQ2d1kNpEHdC zDX-b$Ig+IlWi`Bt(L207ouU^v`}Z~{`|nHvG)YqwXx5npU_lImDy)$GJqO!t=%B?f zEs4;OhK3CbV=D6$vh`snisKH`kRj=NP>3py`((b5Ynrecf}5L?2Qa$(zwHbOI(OeK z$ny$kJmGBm=LyGaFP_0a-oxH*_I{o}ee!oqFe_sLA1M2$4hRamaYL%__?YssRzvnK zac4u^cdW`DbRCh%Q~sSi0~NO-NL$R$4ZMs9mMrt-3yr2dk-eX>y?YZ5R`4VnE@K&! zYFTGn*XrFpz&+Eu8EaB+T5MlIAD6*VVu6*e{WZP5uB5Y*;hUs597=$M z&1lYmy#dqL0uKwpm^WG6%Vc*uL4J~m>cdFI-ghA@I{ou_UK_3~%CBDzO!SfC=tm~? zbvGTp;EU@?*u&sMwTVhLCW#9iO&yR+rXtXN$tIF587++MN9G3C>xzgj+E&VvOLwDZ zP9+3B=NUuFqZ0}`CaI>tKc9{Aq7+EK3y z`DLswIv1XDOr>5D_{ZI5BPkfyrgiOu>P@$*pE4I1=~hE71dbBZcPlAaj7EmkBa1B) z3=D?KRkJG!`lzGzv(KooEBnE8s0%?>%9O;xmh)uXx>1 zt64|CXOQQ2X4|*b3*UnoYqzhzPe6$tHW3jTeCECEZFdP?kJ&_slH>~7r@Ndys>5~X z=NgIIn7Wy{k>z0&33sX~24Umd=CLUt_7wc?v2y)kwn4OQ??2|+(5>lKrs-}QN8VNJ z=tncFx>}p(c6>kqy0t4U-i)YRe4n9ZQnoiI2hU7+)!2KtGkxjb(bzj`!bFa0jh+Ut z#47N|<43|QJz=}o>S0peWb;YCNGLU#i!yU43PJea(*WPtSXiPJA+Ccb^OCI=C2oH}ZLljYz&8?G4w}p~TR^UGOo%ocSjsNa!p-+V zb(9K;Ks&$9{u{lf!xjZl=fFKhJiz%}QT}*o$;)ypw%dJ0_eJlA`T3FuKXZi7PmOt1kJ4iu6!>F8-&^$iB=J=>%#yJ-J=cwdcc3@20#saPOe=zzR5A~XjfOcF~T1{ zvwx`|6uNNU90BM#f+7pmj%jJ?$A9)8vfCfL3p0&`tMpDJ9@f_4M(GKL9su0o{z1Y} z2ovNVf1$;F-GP08baiz@-C~)Vnu;>Ef!m29t&_}ay7i$e8O3zcMYAjTTm52#>qS=~ z$_G58?;9^Nzf;gi0n$hWc#-@`pRo(VYlz%;Sb(nw?LXe!{<)kkB8is=NL_(}0P!o) z&<8dAfgsP=Y$RW1Sd;5X=aNPyf5bknHwii8joG0X{LUCp0d`Mwi;{L&ag;l@jNR&< zzpNwtj?oQ|X~zdecsG8doHT z+N4WA=6SYP%N`}mOI?m%LPU`isKD7CNLA%?5PxY*#jL?IC$`#10ygvQ*wKbd-Lc^V zJ8#CDB*#(R;U5<)39}t`*eK&m%dK%qc)K|ZAqeAmiU2SN0{jL5eK4WM79~IrjP#;Q z|Eq23r#LbdTn&&|SW=%yfoVnfg?K;H;;d`kS&?0|NtGQ9KtlB$l z1t|UdKfy8hFGl5@!b-k60DuilJoXoK<`d5lv}BW$m2lsp$OGVjOt#fKEVCxu7af#J zHx%KY5WB^B0!+iO>Zr+_rP>0{5(0lu$wbt*oHj}rkQTq+ycJ^x2tW=1R6sERU{`c* za#K_UbR~$sqJI0-3XH~s;)h@3-siAkee)U4RGxT!8IE z%Q_oRlw3}M65YaZQh*lBe0M}CdWD+_3c z)M4#g>|oyl^4T(+7i2-rOxXbX|1AQ*4P3^?f~jHs+m14qShgpG@jdGgHbc3IC9`$~ zwN{T%U@l^`oGrC>L}TYbcgtzg?7A#RU2p$TMvVkzb*lEkbKSX0Yr81!omW(YFoe~| z4k)MtO&Ymv8HWKSPwjAVb^af(JN(P*K1LrgsLyt53pvycR+lVMv0LiBVU_B=id66> z-#}g3*PTBzuU4!sXU4;D;qkP1)pP+qV<1cf_zvRTgj?2A$iy?X- zn8mIqWEu#>7eFU@OkLP#?1@XBL#QI=SEdP<<Gv@+mFE39Qs|1ZeMz3W)y|W_&5_a%jd)?^&L`ZPc(f(nOIYz zdoIOoG|s5_KCvzY;vmvunhLipa!b(IQ^k|fKz>N|lhEycOJZ>8w)eFZHzC%*tzx#| zP^vE3Bu0rhk5*6#^6YvPIc`N{xt+k`v0meW>F8Z2E5kc6=5oQ0csTIDykgAIcJW+| z!d(grKMt973N`q!$gY4;S&iPzS`8VdIHy76FUQB^FWbVmrO4MKfRDpOz*SuP!Btw? z?LgT2(qFRpk1PZsm9pk6?TuQD4WH_Y)_fqwU>8RYlycPt>r9xq?NxnnhX^S#IWdt@_1!=G`k@PUBsI z%Gx9+k3~)aLcOc==8pq}%lSW!FpW0*)^gTM+b8wUKC#(kuR7!>T?a1=-K{GyEg;F3 zq}6Gl)-7%-kp;5_2Sn|SGKEBGGU!E!L3%e^hEcfp9Zi0zJ%29S5Qy;Ecd9ey)!44Z z)K(>N!q`ajS)p}5qvx+%erboH+zLLm$=Hv0j1lxw7`LE!@ceajTQ$*7HZqBtnVUiC zOfHzxo^7VVr+rWMxS=H%sTwHcEEXr1B$-$Q2#&PH8qb;4WD z3(r_|TJO>z!K8D}N`@5Jq*GgIWfRr+{x>1U_T-T3Kql}h4XiWq? zO=Tx5({^(b7rxbTT{v}F?)NonJ|7mjFmebZO^Cs~S&HO!?iLECA%k1YhICxjD0BaQ z5(!rlo6&yJ;D;H<{2W0+oT*o67{c_vQE^>ees^k}w;4CN((RzQsXBz6R4g}TTfs|2 zB2rqTM6JaBZT)xLwleQd2^~LT;@ift?i;I3sH70Dz80u-u$fvrw2NqG3d+8~EU-`g_j>b>(Qm z>!VY`CEhemZAxvCJvE37e*wp@bR}l_D%51%`tBS}*<>OL$3F5`AM}m#`{U6_q$#&X z2&`_u$vH*Sv+)ddQjo|cgfx+!KB_C}1kTf!H5}!KO^wR$u$WAqrsqpMH`s~HZ3?HC zLD$^cWgvxXY^+gH`LZKKv(UDC7E-muafe3Kqc6A`y^^G`)3dy(S9NI{t`2`Wu&5CF z`?RlUOwxTt`|0iZ5els{bu1b;ho`m@bR9H!iyIQ;q{bYuhwII^&uK)N<6 z{hO1ACABtTr*$*auv>&0DJIf4;xtXRMQj??pf9*|-<&BrOs&Gk0~IsrHT!k)BwacM zbEasOX0n60F9&E}8a0mH?;ddV90g?v(}P95-|uujey2N(ajoNFZzrtFDo`%$(AZ&W z6FpA7Op_O_!4tpZ@{&A<9Ay$26_t`CDPkE?WD%-+pO&gaDmxL)+<3P3L3rAZ9}$oe zN~E1+5jUxlySE62{>U7~vRP;QRrqv>(>HP~Ses@7I(?&(nh~)Sx7+nf;|}*l1>5y~ z;j;}tJE5sss`R+xjW__d6&oe3U`*P_8^NAO_^F@knQrPGbk0=M1jAxkX=$eX2U2&x zKJOeQySCMi;x+bJvY_4W^f9Q#`ohY~)SI=2AVP>-KoU;f#Ts|OZyIRpV^YTwlyNn2 zF<#?{fVz}1fmP?3EOvECa;L-t!gD9kh(W?1?iehsVVD8#Yaw+>cqb34Sud|E_N}93 zlzYk^?omRr5%F4t<<&!Kc}iM|b76D|mpjjhZ_~wU_c9g!fxjc_CjK9?-YH6#Wr-U7 z+P1aYw(aiKwr$(CZQHAD+qP}4w%z~U=bSsP>Y*NDjH-;th#HkMBQj>Y_|Z|%i4A!w z37l2&{n>$ZaFLiTZnwdaY%U~7hP&H7Tx<66nxs4a>`@kW_3iZ_`O}+|c~(ICkizsa zMoZOnO2QG}-0Ic{Pc&MlmbwP>s;kI&>o%$TKm+FyH`yPwGQ0X`ziS4oJGi%C44>Um0cPFxDcvD1ayTy_m*d$jzH0$%+a)LRr)>)bcnUGi*APeL4Ai)BAB zBaJGR?c{+ZEwvYds_krIS`evhNGz)w~0mJw?nHAB_`^&R2oWqS?WpDp~TTP*eMJE3E=w zR8FdYWpDK1q}_-Ey}5P|Ut*UL&KW%@sHJbbe6I%*SRGaMZ4yq|h&u8;dKinQ-e9+ciz@P_}zL_#@AY>>w-6S{G_K zkE-UF5qj>uJvUQ$U&Usg?0h|5c`M~@UqvKV9ZYyH+s`%p?41*eoDLLsnb z+GJdPTPU}+8IBLbstLLi{37~*rFs|xdO%Zt^%h;)P!A7cKxoG*Z}TxHA%i)3@a~AA$zM_q>;% zRa{H}0mzlVgjkX|_$0RFoieb*fWv{6MGl1#RY@Aq>+9-VJ>Z=9gk_t|b|aDvMsX}k z@8-F{RcTqK`((T%xUGAQ|DlIV6;VOJ5;5O8PASqs^JVOgU%_iJaY-OgsCmHts#C?+ z<3IK+F)3!q8c9J#)ckr*_xc3_NdQ_=j{4QQ z%Boeu9j(5ri)R%O73VUoOH^CufmSM0q6usK=SqG1^BGyxZ7E7yOHobz9m4^##c7R@ zvpQ;dWGXTO5Prrcr?O(5A4az-_DN_Z55IZ8TvXf_(ZfeDZY0GdHq@$|p>6q^}a3giKx zU^v?+>k{0?LcX2sJGN-Q2_6ivMTX-|=6Z@dQLT>cB7pCc@#*_b-J22q1+H`J+;lc+wVE9*_x+ zyVEIQdmvXhAsC+70>o%f-< zQ=5YN6<3+Q-YfRd7C-EDVDZLL(ra`eD|RP?Yuuk@j~gEVum=F30InAuC?|!A=>(zr z2moymiVT^lBv%Q=h5*N|M9w84t)n?Y5rPAzG#27zfk|oq24U-D2c!p z!T)W00}z129Uim;usoQAH}?;Q{6|IsGOA&IS_mo+umnURr1ZZc?5C>F=5bbprdKlA z7vlRwX&$!z!1E3}F1W7ucA7co(UP*K7ib^%ZXv@K{0jZ}l8}Fh{xKT4EkD(+KJ>nK zVS)oOh9x!5gym?^-&FfiaT`0dHF zbc)GE!d26(%%#JEp*C8iRJ?a2wG~sWhg?#R(}H;t@?nJUZq;Y3o$y(R8OH^9h}xyb z#EpE-hll%(B}4-^qVfn0(luxeD2BNFj~7Paf4KlB8V~@b1H6+Dud)zOPu4ZO1_h6& z3dklV2?d&cZ{pz&z%I4nYbEdv6>!D^=Ftihfk9Kmiv z5ZJ5h6UZ}o{wE(V0AKf?ssXbf{Lluo(-3#zd_W%m%`h^x*1gG^uCYIUrFM^0p6v#e z*40F{&Pjk#ZS?s-VGr6EhV$P>_DIDaeNE$_kU}!^_xc|g)6SA&DQU-NRRY_8HP!jA zw){MCsP`%S>ah`oLc=xu`G7q{@D~s(G`yXwkLWr!9e?M(WdNA8iDnLf;s0NkA9d3wZ+-paH^`?urDg5oitVheCo)w zf^Rb^@uI}|X9K_>S&YtN+Qv_10Q-Ul;X8D+8AC)ig8! z=&9M>TOG=)7ivzTrJ2AJs{_k(ZBr>~HqWNp(wRbVwsB3Yh1Ucq+a0(&vZg}7^Q$_z z^96SuD&Q5d8^^gJi%q)e&IMUE$~!-<(7UvAD+SXiZfE+WZLH_GpK4qF{ua!!tvUTD zq!nuspY{RRYJoxSE@+WJovBYzSj(d>=3D+(oY-@94>Zg-6SdI z&)GX{a@S~#C%5*L_^$7+ZcG(dUQMGOXsudBrf5dHl9MgOI~iZv=OlKvgeo+uP^>z) z?jS8e08aKrC1a$y@^K;%gpnQMoy#*jPIvk(;Zj$U}e^rZ%@Z&1g z67#*p5P@)53`!tMyd&SW9z||^D6+4I*MiFK7VBYGHyxTwO@cdgwq;#Q%eJ3f(~2oh z*1;xzT@%JuXe{zKPs7FFGnq=yP`Oz3Pu^pbAD}Vn5#n>I{;GZ4xd{`{digV{d#lIP zC~OPjqNr73R|XNwo@ENFV1yE6E8;=^psYi!HYJewJmUCwLb}}SfuZDhzF7anQkc?q z9GNCTYCk>$<^&P1*~TFIPm|lc3sKz2JW|&+0B0u+PhO?6W^{IKR?^Y<74sh11SSsd z$|+j?1t$*no>uo;*&?)`1oe0a>;Z4P9vDsDb#b$B`l-rjj$Wze~TB_^=EwSLEblK=s|kIxKaNQ$1fgaJA9-bnud}F3`UT&d!D6ZW)Bc|vn*6k#ETcJNlHre->2_T2~w6&$v zP=WK*69~g{rN?IPR#d;vt&KVgi)SNsRoRJWA|i6D!ETW6%Q*C8m(dw6Q9S?-WTjRs zOr1E7vjAb%MgeX2XF?%4p7v`LIV7!vsQQ0B4MD3lb)S}?a< z2H1PNuwJ@cF|Aap;#La+;w==E!(WzHidUP)oRKmNyM` zrDV0lRZ{IZ?WAOC3nTGY(<1c-1x2hOGte_G-V>;0mU z^w(+Nw@Z8FLlJzaZ&g;oq&F=kr-*MApx@lBk^QZft47#U!Uc0Ee||XsVtH`HVs_Y2C`{X!(dY{64kWs|< zo%RT%8M~XeYZnxixTgPy(l&TN%Fm;(p>~xSxad$9Ph@k_0!j&eH8u_#bS8}gujo`X zXyCwokAe7w=5U1N6!$UbRDpy`1-?4dTA5OB9@6`SHcQYkbBY>S#y`5MbamY_^RWHz zyvUQvPKJK1;fKHC--e*zGRCO`%?;EoT;*`}fpu23)Csv{$M8%WJE+)G``0Udm#{qS z{e#r?xWhuxwa)=m1z~QSxg`HQ(6sIZO z=-PJPZ-+LfFovq`?qLXhVXP1T>WAA8zCQ?!%P_J+aNs(2aU&Kh% z*e$AKA`O+e=j9X{T7^hOX|~1@g>K~<;)oO1l1c*7mzYJ0HB6 z>xU)SGK2h1H}ZjRtV*iu-E%4vcz#ac@ewd<#|2_*0||1^6;7ww6R+kC9pdz3GDQ7j z-P<=6mGIJ(W*uLQ91ltW*!xeXcpw0PE0kq4#y=kh0ABs=jKx*st>IVaRy!)u*1!5w0(i3of&xH){9BQp4G)0y0Cfqfi@3bY8>8>% zWn3zR?hZa601XUyhb}2eVL)sfME1+%?b8lii2M+KYY^EKW##3JxSByNl&~7^B!RrM z1SaFE9e$1+>Us{4SjNIbGL?G~W<{L2_Tyx{b@SGbuc^)Kvs1@?6k$VyW9h>5*Mr{x zNqTh^nEgFw_=hhR@JcT?kodEw5yD8Yy{z=rN?bIX#hZGbCh%W{eF*_zaoMT&;t~A5 z009gj*wjz_Ts9(iUbOb1eK*T-iASVz5E3@y2*Fm;1z`68Ef=y97nmD8ipRTCe*ggH zA84rcZ_e}y0GI_B8<^LL9taWu`~|qk|B>c^xH?Dhh1?0YOEWqo&+XgNMDp`n;KgvY3pj?#bQ9NjZYCyvcPu^Yav0@9QB_hfw z@F_gDM8KW$P<|B!gHjR~+pZmw4z4NHaJ0a96j@y&d>Vxfvu?e5#ky#@8o|r0g-K94 z5E6n3EQ=D%%6Go6ebAT{w_^|d(NK(#?*|_TdtcDUvwD0EyC?doPsdIlx&lT4g9`Ra ziBF#iAW{X>n^aBlc#@=6X(%4#>(L)f-7!BMO;Iavhk^B#;l+h~f1GF{TfRO_${;fA zXxrQtx?TRpd8_a_LsHY4C)X-&z1h-jVzE=|_xa*r9ABNTEnY+ADUZ4DX36AJmq)zt z8|hsn^%EWU7@H0lT7Ic%{0l?ap-M`|h13`bW@5P%hytU+&z_tBN#9A#rXz@@_B`Lz zKNaQ=2LQYSApFJMoVmph($Z_21rdSN0d+S95(L_qsZRJ9cZWIH4h#81QrR*eaZZ?=PU?W4iI=8cX2V=5DJ5@O=kdV<#CA-cR{Ou?Z?gwmbWy#fc0sK3kG#s=@tl z@cyG_8@wSWtl53ns=>bT3FX_t@VG%va?65~P1X*SORJZqdT%rN+TEogPKX=}C5Qzo zUw^Swx;$QYYZ__Y>My7U@VT&Sb|F88t=ukuZV=L_?f*Bg;uv8QhS?A%xwYCuf{Soq z&q%D<67fI(eSkM@;EL^{PPtY?OT74Ek3vrnz@c}*^B>DV%vk{bF7ioD5?>WYv;yG( zps@iA3A0p*oc@W1kqg$^OINLp_N{9#)8&?%9;)DK@LEJEHZ5 z)EE<tdmZw>8R6o8%p94N`58ZdR=fLa`Ze0U&59}XOWAlN<9Tbv*y;;pZEVzPub4T&|L z?S1A6dA`01Ixj~RN$Q3r7BSR$(-_9_Y^4hGIj;KEV4IXPu2kAW6Y7Lh4C5*n>x=K= zivxqBZwtAV+hXF*ws=JISy)HLmv3ijm{t%0hXnEsV}WJ;YMp!%E7tF4HC2&EPYqH* zpF{;ZvX9IT3-$~Jv4{owIlH7ol;a2iy?ZU!e%9g*_nW^fgT>NoDK4F1|KV)oIrPzV zKA7i_Q?hq%t-8~1`|UFy6dvnsf*J_fCZKdu=D__vZj{UPlh>v~tjo`UhU_15yF zBUL)%?ClF~dFgPnmEY!O{oThhN6wYg z@6oXP3#5S=RWs}~ExM2QMjmMKFTk6wq;ptfGufnPkB=%+Mq=a7SX=BRlrhBs!MjN8 z<`lURyWy>Q4Dgz=EHvDsUIz`w))m?*$-dZ?9M)`kDPD8A&pwE#KtiQfdNbz0q*oM1 z*Y&oQ8Z!^pu#ghF`QDgn`XPy1c)iNPjuIgspM;Un(pOiF_&B?y+S-Fgy?s7>r#7P5 z=qhEnX+q8rh1BL0Sr;MX9(r6_)R$1gkEuMB1nLKL50SDw# zrB#k4cOyhtr7vlOekm8KfHo319SIX$btomj-3FiScXC{&pSh7_u`4oKYJI)R^>Txj z-!Hvhz&y=1+XM!;7JB0{*CDl38{|D`*4f_28)}t`U66{W0>ApwsYhvYa$>uc&!S$m z2Kmis>*u6hP<6?w3_gju+h5{cizm0Y@}Dg4MI$=xaG2y(TRt>HZ9YzhR8%cPgY*k> zHA8sa9VAA5*|^85N#}5q1;j;}XDv0xKx~9$h_%k_em8O0WSq@t%48Z(BCLuJYjQX` z(R~Q|1(aY~DN*eF9ankxij7zcP1ApFFi_d&Txf)F#<-{SfYDwHarm?E{Z}hmYt?Au zqvFKpTYBZ5>26{bW|xg6<&2`XYTsWrPoo6Oo|W&?nvej2^iH#xjF%i{QLx;!x?~Yo ziAp15U<3S>rD1rK4-PG&F80ezgb@U;2c?W}DWiUz8LGP^XZ5phO>H!%46fX($BP$N zPKH&>Zs2MucV~rcp*a;RE6(~wS7Wh{vgIF?ziWMvc81g=J`=%GJj7g7(qGAu?5{+v z^%ou}3TnI(kxfZ=Bl7r zw4dCbi1R&xTCoL7MaL-}u0Z8P+6yceWwJd6JLO4)UkWb@(Ih=9`_WBw=nt|A%(Zh~&oelK&!<>89yWQ3ZG#$> zU3GDY7EVdX= zn}W2}=9;NO8ETwXsDNI_yCfc8`tJE4PDWQ*1KXA7EuES$kVC`ps;A{e+G5g3 zWX)@6SsU{rtuKhf3yE8lj!+7D=U&k8-E3=^EYvMS)*ohZZ?-(0Kp~cU@@c6YGtLOL zWtJ|e zHwaY{C2$3*q;7QTRV5ybTX@dOQJ;qHFJS7rr#}d6wo~vG{S2DO;gz`%b|BmKis4Pl zXXu=@Ko|6rc|G&UE*kNp=GFR})rqN-vzrNu9dFDX>}v{_5h4X%@%(E?&3MhWS+~t_;e;H(*8E+2|qWj!xi^gk`!n;9NT(S!mqVt zu*Q^xsn%c4GOlBJ)}eSdSD@R7KJ<3X=vRtCFzGPZMLhd%y2P#8D4~j9#rRW`??v~` z?kNqT*4=G4#%$^o>bfy?mMD(yS*cXEZGDsTO2xY#yxg7NF)|zUdzax`c}Bo&CupC9 zcIGQfw_+;k=JJ&1u&Plj2mM=&`_p~VuVH9pWpnzsGcz%{ua{~DHwpe+tOauF92vPp zZYdABlG5||xUi�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

`uB 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�>kq@ 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

ryoRiYk)R$z{Gmubk<5 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