diff --git a/src/Numerics/Algorithms/LinearAlgebra/ILinearAlgebra.cs b/src/Numerics/Algorithms/LinearAlgebra/ILinearAlgebra.cs index 2cc85450..ebc15ee2 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/ILinearAlgebra.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/ILinearAlgebra.cs @@ -30,10 +30,20 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra public interface ILinearAlgebra { /// - /// Adds the two arrays together: a += c. + /// Adds a scaled vector to another: y += alpha*x. /// - /// One of the arrays to add. - /// The other array to add. - void AddArrays(double[] a, double[] b); + /// The vector to update. + /// The value to scale by. + /// The vector to add to . + /// This is equivalent to the AXPY BLAS routine. + void AddVectorToScaledVector(double[] y, double alpha, double[] x); + + /// + /// Scales an array. Can be used to scale a vector and a matrix. + /// + /// The scalar. + /// The values to scale. + /// This is equivalent to the SCAL BLAS routine. + void ScaleArray(double alpha, double[] x); } } diff --git a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebra.cs b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebra.cs index 1749ef0a..22d5beae 100644 --- a/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebra.cs +++ b/src/Numerics/Algorithms/LinearAlgebra/ManagedLinearAlgebra.cs @@ -31,37 +31,52 @@ namespace MathNet.Numerics.Algorithms.LinearAlgebra /// internal class ManagedLinearAlgebra : ILinearAlgebra { - #region ILinearAlgebra Members - /// - /// Adds the two arrays together: a += c. + /// Adds a scaled vector to another: y += alpha*x. /// - /// - /// One of the arrays to add. - /// - /// - /// The other array to add. - /// - public void AddArrays(double[] a, double[] b) + /// The vector to update. + /// The value to scale by. + /// The vector to add to . + /// This equivalent to the AXPY BLAS routine. + public void AddVectorToScaledVector(double[] y, double alpha, double[] x) { - if (a == null) + if (y == null) { - throw new ArgumentNullException("a"); + throw new ArgumentNullException("y"); } - if (b == null) + if (x == null) { - throw new ArgumentNullException("b"); + throw new ArgumentNullException("x"); } - if (a.Length != b.Length) + if (y.Length != x.Length) { throw new ArgumentException(Properties.Resources.ArgumentVectorsSameLength); } - Parallel.For(0, a.Length, i => a[i] += b[i]); + if (alpha.AlmostZero()) + { + return; + } + + if (alpha.AlmostEqual(1.0)) + { + Parallel.For(0, y.Length, i => y[i] += x[i]); + } + else + { + Parallel.For(0, y.Length, i => y[i] += alpha * x[i]); + } } - #endregion + public void ScaleArray(double alpha, double[] x) + { + if (alpha.AlmostEqual(1.0)) + { + return; + } + Parallel.For(0, x.Length, i => x[i] = alpha * x[i]); + } } } \ No newline at end of file diff --git a/src/Numerics/LinearAlgebra/Double/DenseVector.cs b/src/Numerics/LinearAlgebra/Double/DenseVector.cs index 586fdb98..e846f644 100644 --- a/src/Numerics/LinearAlgebra/Double/DenseVector.cs +++ b/src/Numerics/LinearAlgebra/Double/DenseVector.cs @@ -265,7 +265,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double } else { - _linearAlgebra.AddArrays(Data, denseVector.Data); + _linearAlgebra.AddVectorToScaledVector(Data, 1.0, denseVector.Data); } } @@ -354,5 +354,169 @@ namespace MathNet.Numerics.LinearAlgebra.Double ret.Add(rightSide); return ret; } + + /// + /// Subtracts a scalar from each element of the vector. + /// + /// The scalar to subtract. + public override void Subtract(double scalar) + { + if (scalar.AlmostZero()) + { + return; + } + + Parallel.For(0, Count, i => Data[i] -= scalar); + } + + /// + /// Subtracts a scalar from each element of the vector and stores the result in the result vector. + /// + /// The scalar to subtract. + /// The vector to store the result of the subtraction. + /// If the result vector is . + /// If this vector and are not the same size. + public override void Subtract(double scalar, Vector result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (Count != result.Count) + { + throw new ArgumentException("result", Resources.ArgumentVectorsSameLength); + } + + CopyTo(result); + result.Subtract(scalar); + } + + /// + /// Subtracts another vector from this vector. + /// + /// The vector to subtract from this one. + /// If the other vector is . + /// If this vector and are not the same size. + public override void Subtract(Vector other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (Count != other.Count) + { + throw new ArgumentException("other", Resources.ArgumentVectorsSameLength); + } + + var denseVector = other as DenseVector; + + if (denseVector == null) + { + base.Subtract(other); + } + else + { + _linearAlgebra.AddVectorToScaledVector(Data, -1.0, denseVector.Data); + } + } + + /// + /// Subtracts another vector to this vector and stores the result into the result vector. + /// + /// The vector to subtract from this one. + /// The vector to store the result of the subtraction. + /// If the other vector is . + /// If the result vector is . + /// If this vector and are not the same size. + /// If this vector and are not the same size. + public override void Subtract(Vector other, Vector result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (Count != other.Count) + { + throw new ArgumentException("other", Resources.ArgumentVectorsSameLength); + } + + if (Count != result.Count) + { + throw new ArgumentException("result", Resources.ArgumentVectorsSameLength); + } + + if (ReferenceEquals(this, result) || ReferenceEquals(other, result)) + { + var tmp = result.CreateVector(result.Count); + Subtract(other, tmp); + tmp.CopyTo(result); + } + else + { + CopyTo(result); + result.Subtract(other); + } + } + + /// + /// Returns a Vector containing the negated values of rightSide. + /// + /// The vector to get the values from. + /// A vector containing the negated values as . + /// If is . + public static Vector operator -(DenseVector rightSide) + { + if (rightSide == null) + { + throw new ArgumentNullException("rightSide"); + } + + return rightSide.Negate(); + } + + /// + /// Subtracts two Vectors and returns the results. + /// + /// The vector to subtract from. + /// The vector to subtract. + /// The result of the subtraction. + /// If and are not the same size. + /// If or is . + public static Vector operator -(DenseVector leftSide, DenseVector rightSide) + { + if (rightSide == null) + { + throw new ArgumentNullException("rightSide"); + } + + if (leftSide == null) + { + throw new ArgumentNullException("leftSide"); + } + + if (leftSide.Count != rightSide.Count) + { + throw new ArgumentException("rightSide", Resources.ArgumentVectorsSameLength); + } + + var ret = leftSide.Clone(); + ret.Subtract(rightSide); + return ret; + } + + /// + /// Returns a negated vector. + /// + /// The negated vector. + /// Added as an alternative to the unary negation operator. + public virtual Vector Negate() + { + var result = new DenseVector(Count); + Parallel.For(0, Count, i => result[i] = -Data[i]); + return result; + } } } \ No newline at end of file diff --git a/src/Numerics/LinearAlgebra/Double/Vector.cs b/src/Numerics/LinearAlgebra/Double/Vector.cs index b1f579e6..fcb2625c 100644 --- a/src/Numerics/LinearAlgebra/Double/Vector.cs +++ b/src/Numerics/LinearAlgebra/Double/Vector.cs @@ -329,9 +329,9 @@ namespace MathNet.Numerics.LinearAlgebra.Double } /// - /// Returns a clone of this vector. + /// Returns this vector. /// - /// A clone of this vector. + /// This vector. /// Added as an alternative to the unary addition operator. public virtual Vector Plus() { @@ -398,7 +398,7 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// /// This method is included for completeness. /// The vector to get the values from. - /// A vector containing a the same values as . + /// A vector containing the same values as . /// If is . public static Vector operator +(Vector rightSide) { @@ -440,6 +440,157 @@ namespace MathNet.Numerics.LinearAlgebra.Double return ret; } + /// + /// Subtracts a scalar from each element of the vector. + /// + /// The scalar to subtract. + public virtual void Subtract(double scalar) + { + if (scalar.AlmostZero()) + { + return; + } + + Parallel.For(0, Count, i => this[i] -= scalar); + } + + /// + /// Subtracts a scalar from each element of the vector and stores the result in the result vector. + /// + /// The scalar to subtract. + /// The vector to store the result of the subtraction. + /// If the result vector is . + /// If this vector and are not the same size. + public virtual void Subtract(double scalar, Vector result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (Count != result.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "result"); + } + + CopyTo(result); + result.Subtract(scalar); + } + + /// + /// Returns a negated vector. + /// + /// The negated vector. + /// Added as an alternative to the unary negation operator. + public virtual Vector Negate() + { + var result = CreateVector(Count); + Parallel.For(0, Count, i => result[i] = -this[i]); + return result; + } + + /// + /// Subtracts another vector from this vector. + /// + /// The vector to subtract from this one. + /// If the other vector is . + /// If this vector and are not the same size. + public virtual void Subtract(Vector other) + { + if (other == null) + { + throw new ArgumentNullException("other"); + } + + if (Count != other.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other"); + } + + Parallel.For(0, Count, i => this[i] -= other[i]); + } + + /// + /// Subtracts another vector to this vector and stores the result into the result vector. + /// + /// The vector to subtract from this one. + /// The vector to store the result of the subtraction. + /// If the other vector is . + /// If the result vector is . + /// If this vector and are not the same size. + /// If this vector and are not the same size. + public virtual void Subtract(Vector other, Vector result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + if (Count != result.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "result"); + } + + if (ReferenceEquals(this, result) || ReferenceEquals(other, result)) + { + var tmp = result.CreateVector(result.Count); + Subtract(other, tmp); + tmp.CopyTo(result); + } + else + { + CopyTo(result); + result.Subtract(other); + } + } + + /// + /// Returns a Vector containing the negated values of rightSide. + /// + /// The vector to get the values from. + /// A vector containing the negated values as . + /// If is . + public static Vector operator -(Vector rightSide) + { + if (rightSide == null) + { + throw new ArgumentNullException("rightSide"); + } + + return rightSide.Negate(); + } + + /// + /// Subtracts two Vectors and returns the results. + /// + /// The vector to subtract from. + /// The vector to subtract. + /// The result of the subtraction. + /// If and are not the same size. + /// If or is . + public static Vector operator -(Vector leftSide, Vector rightSide) + { + if (rightSide == null) + { + throw new ArgumentNullException("rightSide"); + } + + if (leftSide == null) + { + throw new ArgumentNullException("leftSide"); + } + + if (leftSide.Count != rightSide.Count) + { + throw new ArgumentException(Resources.ArgumentVectorsSameLength, "rightSide"); + } + + var ret = leftSide.Clone(); + ret.Subtract(rightSide); + return ret; + } + + #region Implemented Interfaces #region ICloneable diff --git a/src/UnitTests/LinearAlgebraTests/Double/DenseVectorTests.cs b/src/UnitTests/LinearAlgebraTests/Double/DenseVectorTests.cs index 23cd89ca..f2e7bac6 100644 --- a/src/UnitTests/LinearAlgebraTests/Double/DenseVectorTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Double/DenseVectorTests.cs @@ -6,7 +6,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double public class DenseVectorTests : VectorTests { - protected override MathNet.Numerics.LinearAlgebra.Double.Vector CreateVector(int size) + protected override Vector CreateVector(int size) { return new DenseVector(size); } diff --git a/src/UnitTests/LinearAlgebraTests/Double/VectorTests.cs b/src/UnitTests/LinearAlgebraTests/Double/VectorTests.cs index 0b4e0590..71dc7b02 100644 --- a/src/UnitTests/LinearAlgebraTests/Double/VectorTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Double/VectorTests.cs @@ -346,7 +346,6 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double } } - [Test] [MultipleAsserts] public void CanAddVectorToItselfUsingResultVector() @@ -377,6 +376,231 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double } } + [Test] + public void CanCallNegate() + { + var vector = CreateVector(_data); + var other = vector.Negate(); + for (var i = 0; i < _data.Length; i++) + { + Assert.AreEqual(-_data[i], other[i]); + } + } + + [Test] + public void OperatorNegateThrowsArgumentNullExceptionWhenCallOnNullVector() + { + Vector vector = null; + Vector other = null; + Assert.Throws(() => other = -vector); + } + + [Test] + public void CanCallUnaryNegationOperator() + { + var vector = CreateVector(_data); + var other = -vector; + for (var i = 0; i < _data.Length; i++) + { + Assert.AreEqual(-_data[i], other[i]); + } + } + + [Test] + [MultipleAsserts] + public void CanSubtractScalarFromVector() + { + var vector = CreateVector(_data); + vector.Subtract(2.0); + + for (var i = 0; i < _data.Length; i++) + { + Assert.AreEqual(_data[i] - 2.0, vector[i]); + } + + vector.Subtract(0.0); + for (var i = 0; i < _data.Length; i++) + { + Assert.AreEqual(_data[i] - 2.0, vector[i]); + } + } + + [Test] + [MultipleAsserts] + public void CanSubtractScalarFromVectorUsingResultVector() + { + var vector = CreateVector(_data); + var result = CreateVector(_data.Length); + vector.Subtract(2.0, result); + + for (var i = 0; i < _data.Length; i++) + { + Assert.AreEqual(_data[i], vector[i], "Making sure the original vector wasn't modified."); + Assert.AreEqual(_data[i] - 2.0, result[i]); + } + + vector.Subtract(0.0, result); + for (var i = 0; i < _data.Length; i++) + { + Assert.AreEqual(_data[i], result[i]); + } + } + + [Test] + public void ThrowsArgumentNullExceptionWhenSubtractingScalarWithNullResultVector() + { + var vector = CreateVector(_data.Length); + Assert.Throws(() => vector.Subtract(0.0, null)); + } + + [Test] + public void ThrowsArgumentExceptionWhenSubtractingScalarWithWrongSizeResultVector() + { + var vector = CreateVector(_data.Length); + var result = CreateVector(_data.Length + 1); + Assert.Throws(() => vector.Subtract(0.0, result)); + } + + [Test] + public void ThrowsArgumentNullExceptionWhenSubtractingTwoVectorsAndOneIsNull() + { + var vector = CreateVector(_data); + Assert.Throws(() => vector.Subtract(null)); + } + + [Test] + public void ThrowsArgumentExceptionWhenSubtractingTwoVectorsOfDifferingSize() + { + var vector = CreateVector(_data.Length); + var other = CreateVector(_data.Length + 1); + Assert.Throws(() => vector.Subtract(other)); + } + + [Test] + public void ThrowsArgumentNullExceptionWhenSubtractingTwoVectorsAndResultIsNull() + { + var vector = CreateVector(_data.Length); + var other = CreateVector(_data.Length + 1); + Assert.Throws(() => vector.Subtract(other, null)); + } + + [Test] + public void ThrowsArgumentExceptionWhenSubtractingTwoVectorsAndResultIsDifferentSize() + { + var vector = CreateVector(_data.Length); + var other = CreateVector(_data.Length); + var result = CreateVector(_data.Length + 1); + Assert.Throws(() => vector.Subtract(other, result)); + } + + [Test] + public void SubtractionOperatorThrowsArgumentNullExpectionIfAVectorIsNull() + { + Vector a = null; + var b = CreateVector(_data.Length); + Assert.Throws(() => a -= b); + + a = b; + b = null; + Assert.Throws(() => a -= b); + } + + [Test] + public void SubtractionOperatorThrowsArgumentExpectionIfVectorsAreDifferentSize() + { + var a = CreateVector(_data.Length); + var b = CreateVector(_data.Length + 1); + Assert.Throws(() => a -= b); + } + + [Test] + public void CanSubtractTwoVectors() + { + var vector = CreateVector(_data); + var other = CreateVector(_data); + vector.Subtract(other); + + for (var i = 0; i < _data.Length; i++) + { + Assert.AreEqual(0.0, vector[i]); + } + } + + [Test] + [MultipleAsserts] + public void CanSubtractTwoVectorsUsingResultVector() + { + var vector = CreateVector(_data); + var other = CreateVector(_data); + var result = CreateVector(_data.Length); + vector.Subtract(other, result); + + for (var i = 0; i < _data.Length; i++) + { + Assert.AreEqual(_data[i], vector[i], "Making sure the original vector wasn't modified."); + Assert.AreEqual(_data[i], other[i], "Making sure the original vector wasn't modified."); + Assert.AreEqual(0.0, result[i]); + } + } + + [Test] + [MultipleAsserts] + public void CanSubtractTwoVectorsUsingOperator() + { + var vector = CreateVector(_data); + var other = CreateVector(_data); + var result = vector - other; + + for (var i = 0; i < _data.Length; i++) + { + Assert.AreEqual(_data[i], vector[i], "Making sure the original vector wasn't modified."); + Assert.AreEqual(_data[i], other[i], "Making sure the original vector wasn't modified."); + Assert.AreEqual(0.0, result[i]); + } + } + + [Test] + public void CanSubtractVectorFromItself() + { + var vector = CreateVector(_data); + vector.Subtract(vector); + + for (var i = 0; i < _data.Length; i++) + { + Assert.AreEqual(0.0, vector[i]); + } + } + + + [Test] + [MultipleAsserts] + public void CanSubtractVectorFromItselfUsingResultVector() + { + var vector = CreateVector(_data); + var result = CreateVector(_data.Length); + vector.Subtract(vector, result); + + for (var i = 0; i < _data.Length; i++) + { + Assert.AreEqual(_data[i], vector[i], "Making sure the original vector wasn't modified."); + Assert.AreEqual(0.0, result[i]); + } + } + + [Test] + [MultipleAsserts] + public void CanSubtractTwoVectorsUsingItselfAsResultVector() + { + var vector = CreateVector(_data); + var other = CreateVector(_data); + vector.Subtract(other, vector); + + for (var i = 0; i < _data.Length; i++) + { + Assert.AreEqual(_data[i], other[i], "Making sure the original vector wasn't modified."); + Assert.AreEqual(0.0, vector[i]); + } + } protected abstract Vector CreateVector(int size);