Browse Source

LA: Matrix FoldRows/FoldColumns

pull/222/head
Christoph Ruegg 12 years ago
parent
commit
9afcf2a98a
  1. 3
      src/Numerics/LinearAlgebra/Builder.cs
  2. 24
      src/Numerics/LinearAlgebra/Matrix.cs
  3. 31
      src/Numerics/LinearAlgebra/Storage/DenseColumnMajorMatrixStorage.cs
  4. 60
      src/Numerics/LinearAlgebra/Storage/DiagonalMatrixStorage.cs
  5. 80
      src/Numerics/LinearAlgebra/Storage/MatrixStorage.cs
  6. 114
      src/Numerics/LinearAlgebra/Storage/SparseCompressedRowMatrixStorage.cs
  7. 763
      src/UnitTests/GenericMath.cs
  8. 0
      src/UnitTests/LinearAlgebraTests/MatrixHelpers.cs
  9. 44
      src/UnitTests/LinearAlgebraTests/MatrixStructureTheory.Functional.cs
  10. 5
      src/UnitTests/UnitTests.csproj

3
src/Numerics/LinearAlgebra/Builder.cs

@ -1363,7 +1363,8 @@ namespace MathNet.Numerics.LinearAlgebra
/// <summary>
/// Create a new vector with the same kind of the provided example.
/// </summary>
public Vector<T> SameAs(Matrix<T> example, int length)
public Vector<T> SameAs<TU>(Matrix<TU> example, int length)
where TU : struct, IEquatable<TU>, IFormattable
{
return example.Storage.IsDense ? Dense(length) : Sparse(length);
}

24
src/Numerics/LinearAlgebra/Matrix.cs

@ -1572,5 +1572,29 @@ namespace MathNet.Numerics.LinearAlgebra
Storage.MapIndexedToUnchecked(result.Storage, f, zeros, ExistingData.AssumeZeros);
return result;
}
/// <summary>
/// For each row, applies a function f to each element of the row, threading an accumulator argument through the computation.
/// Returns a vector with the resulting accumulator states for each row.
/// </summary>
public Vector<TU> FoldRows<TU>(Func<TU, T, TU> f, TU state, Zeros zeros = Zeros.AllowSkip)
where TU : struct, IEquatable<TU>, IFormattable
{
var result = Vector<TU>.Build.SameAs(this, RowCount);
Storage.FoldRowsUnchecked(result.Storage, f, (x, c) => x, DenseVectorStorage<TU>.OfInit(RowCount, i => state), zeros);
return result;
}
/// <summary>
/// For each column, applies a function f to each element of the column, threading an accumulator argument through the computation.
/// Returns a vector with the resulting accumulator states for each column.
/// </summary>
public Vector<TU> FoldColumns<TU>(Func<TU, T, TU> f, TU state, Zeros zeros = Zeros.AllowSkip)
where TU : struct, IEquatable<TU>, IFormattable
{
var result = Vector<TU>.Build.SameAs(this, ColumnCount);
Storage.FoldColumnsUnchecked(result.Storage, f, (x, c) => x, DenseVectorStorage<TU>.OfInit(ColumnCount, i => state), zeros);
return result;
}
}
}

31
src/Numerics/LinearAlgebra/Storage/DenseColumnMajorMatrixStorage.cs

@ -598,7 +598,7 @@ namespace MathNet.Numerics.LinearAlgebra.Storage
}
}
// FUNCTIONAL COMBINATORS
// FUNCTIONAL COMBINATORS: MAP
public override void MapInplace(Func<T, T> f, Zeros zeros = Zeros.AllowSkip)
{
@ -724,5 +724,34 @@ namespace MathNet.Numerics.LinearAlgebra.Storage
}
}
}
// FUNCTIONAL COMBINATORS: FOLD
internal override void FoldRowsUnchecked<TU>(VectorStorage<TU> target, Func<TU, T, TU> f, Func<TU, int, TU> finalize, VectorStorage<TU> state, Zeros zeros = Zeros.AllowSkip)
{
for (int i = 0; i < RowCount; i++)
{
TU s = state.At(i);
for (int j = 0; j < ColumnCount; j++)
{
s = f(s, Data[j*RowCount + i]);
}
target.At(i, finalize(s, ColumnCount));
}
}
internal override void FoldColumnsUnchecked<TU>(VectorStorage<TU> target, Func<TU, T, TU> f, Func<TU, int, TU> finalize, VectorStorage<TU> state, Zeros zeros = Zeros.AllowSkip)
{
for (int j = 0; j < ColumnCount; j++)
{
int offset = j*RowCount;
TU s = state.At(j);
for (int i = 0; i < RowCount; i++)
{
s = f(s, Data[offset + i]);
}
target.At(j, finalize(s, RowCount));
}
}
}
}

60
src/Numerics/LinearAlgebra/Storage/DiagonalMatrixStorage.cs

@ -595,7 +595,7 @@ namespace MathNet.Numerics.LinearAlgebra.Storage
}
}
// FUNCTIONAL COMBINATORS
// FUNCTIONAL COMBINATORS: MAP
public override void MapInplace(Func<T, T> f, Zeros zeros = Zeros.AllowSkip)
{
@ -898,5 +898,63 @@ namespace MathNet.Numerics.LinearAlgebra.Storage
}
}
}
// FUNCTIONAL COMBINATORS: FOLD
internal override void FoldRowsUnchecked<TU>(VectorStorage<TU> target, Func<TU, T, TU> f, Func<TU, int, TU> finalize, VectorStorage<TU> state, Zeros zeros = Zeros.AllowSkip)
{
if (zeros == Zeros.AllowSkip)
{
for (int k = 0; k < Data.Length; k++)
{
target.At(k, finalize(f(state.At(k), Data[k]), 1));
}
for (int k = Data.Length; k < RowCount; k++)
{
target.At(k, finalize(state.At(k), 0));
}
}
else
{
for (int i = 0; i < RowCount; i++)
{
TU s = state.At(i);
for (int j = 0; j < ColumnCount; j++)
{
s = f(s, i == j ? Data[i] : Zero);
}
target.At(i, finalize(s, ColumnCount));
}
}
}
internal override void FoldColumnsUnchecked<TU>(VectorStorage<TU> target, Func<TU, T, TU> f, Func<TU, int, TU> finalize, VectorStorage<TU> state, Zeros zeros = Zeros.AllowSkip)
{
if (zeros == Zeros.AllowSkip)
{
for (int k = 0; k < Data.Length; k++)
{
target.At(k, finalize(f(state.At(k), Data[k]), 1));
}
for (int k = Data.Length; k < ColumnCount; k++)
{
target.At(k, finalize(state.At(k), 0));
}
}
else
{
for (int j = 0; j < ColumnCount; j++)
{
TU s = state.At(j);
for (int i = 0; i < RowCount; i++)
{
s = f(s, i == j ? Data[i] : Zero);
}
target.At(j, finalize(s, RowCount));
}
}
}
}
}

80
src/Numerics/LinearAlgebra/Storage/MatrixStorage.cs

@ -539,7 +539,7 @@ namespace MathNet.Numerics.LinearAlgebra.Storage
}
}
// FUNCTIONAL COMBINATORS
// FUNCTIONAL COMBINATORS: MAP
public virtual void MapInplace(Func<T, T> f, Zeros zeros = Zeros.AllowSkip)
{
@ -667,5 +667,83 @@ namespace MathNet.Numerics.LinearAlgebra.Storage
}
}
}
// FUNCTIONAL COMBINATORS: FOLD
public void FoldRows<TU>(VectorStorage<TU> target, Func<TU, T, TU> f, Func<TU, int, TU> finalize, VectorStorage<TU> state, Zeros zeros = Zeros.AllowSkip)
where TU : struct, IEquatable<TU>, IFormattable
{
if (target == null)
{
throw new ArgumentNullException("target");
}
if (target.Length != RowCount)
{
throw new ArgumentException(Resources.ArgumentVectorsSameLength, "target");
}
if (state == null)
{
throw new ArgumentNullException("state");
}
if (state.Length != RowCount)
{
throw new ArgumentException(Resources.ArgumentVectorsSameLength, "state");
}
FoldRowsUnchecked(target, f, finalize, state, zeros);
}
internal virtual void FoldRowsUnchecked<TU>(VectorStorage<TU> target, Func<TU, T, TU> f, Func<TU, int, TU> finalize, VectorStorage<TU> state, Zeros zeros = Zeros.AllowSkip)
where TU : struct, IEquatable<TU>, IFormattable
{
for (int i = 0; i < RowCount; i++)
{
TU s = state.At(i);
for (int j = 0; j < ColumnCount; j++)
{
s = f(s, At(i, j));
}
target.At(i, finalize(s, ColumnCount));
}
}
public void FoldColumns<TU>(VectorStorage<TU> target, Func<TU, T, TU> f, Func<TU, int, TU> finalize, VectorStorage<TU> state, Zeros zeros = Zeros.AllowSkip)
where TU : struct, IEquatable<TU>, IFormattable
{
if (target == null)
{
throw new ArgumentNullException("target");
}
if (target.Length != ColumnCount)
{
throw new ArgumentException(Resources.ArgumentVectorsSameLength, "target");
}
if (state == null)
{
throw new ArgumentNullException("state");
}
if (state.Length != ColumnCount)
{
throw new ArgumentException(Resources.ArgumentVectorsSameLength, "state");
}
FoldColumnsUnchecked(target, f, finalize, state, zeros);
}
internal virtual void FoldColumnsUnchecked<TU>(VectorStorage<TU> target, Func<TU, T, TU> f, Func<TU, int, TU> finalize, VectorStorage<TU> state, Zeros zeros = Zeros.AllowSkip)
where TU : struct, IEquatable<TU>, IFormattable
{
for (int j = 0; j < ColumnCount; j++)
{
TU s = state.At(j);
for (int i = 0; i < RowCount; i++)
{
s = f(s, At(i, j));
}
target.At(j, finalize(s, RowCount));
}
}
}
}

114
src/Numerics/LinearAlgebra/Storage/SparseCompressedRowMatrixStorage.cs

@ -31,7 +31,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Policy;
using MathNet.Numerics.Properties;
namespace MathNet.Numerics.LinearAlgebra.Storage
@ -1248,7 +1247,7 @@ namespace MathNet.Numerics.LinearAlgebra.Storage
}
}
// FUNCTIONAL COMBINATORS
// FUNCTIONAL COMBINATORS: MAP
public override void MapInplace(Func<T, T> f, Zeros zeros = Zeros.AllowSkip)
{
@ -1425,9 +1424,9 @@ namespace MathNet.Numerics.LinearAlgebra.Storage
var endIndex = RowPointers[row + 1];
for (int j = 0; j < ColumnCount; j++)
{
if (j == ColumnIndices[index])
if (index < endIndex && j == ColumnIndices[index])
{
target.At(row, j, f(Values[j]));
target.At(row, j, f(Values[index]));
index = Math.Min(index + 1, endIndex);
}
else
@ -1520,9 +1519,9 @@ namespace MathNet.Numerics.LinearAlgebra.Storage
var endIndex = RowPointers[row + 1];
for (int j = 0; j < ColumnCount; j++)
{
if (j == ColumnIndices[index])
if (index < endIndex && j == ColumnIndices[index])
{
target.At(row, j, f(row, j, Values[j]));
target.At(row, j, f(row, j, Values[index]));
index = Math.Min(index + 1, endIndex);
}
else
@ -1755,5 +1754,108 @@ namespace MathNet.Numerics.LinearAlgebra.Storage
}
}
}
// FUNCTIONAL COMBINATORS: FOLD
internal override void FoldRowsUnchecked<TU>(VectorStorage<TU> target, Func<TU, T, TU> f, Func<TU, int, TU> finalize, VectorStorage<TU> state, Zeros zeros = Zeros.AllowSkip)
{
if (zeros == Zeros.AllowSkip)
{
for (int row = 0; row < RowCount; row++)
{
var startIndex = RowPointers[row];
var endIndex = RowPointers[row + 1];
TU s = state.At(row);
for (var j = startIndex; j < endIndex; j++)
{
s = f(s, Values[j]);
}
target.At(row, finalize(s, endIndex - startIndex));
}
}
else
{
for (int row = 0; row < RowCount; row++)
{
var index = RowPointers[row];
var endIndex = RowPointers[row + 1];
TU s = state.At(row);
for (int j = 0; j < ColumnCount; j++)
{
if (index < endIndex && j == ColumnIndices[index])
{
s = f(s, Values[index]);
index = Math.Min(index + 1, endIndex);
}
else
{
s = f(s, Zero);
}
}
target.At(row, finalize(s, ColumnCount));
}
}
}
internal override void FoldColumnsUnchecked<TU>(VectorStorage<TU> target, Func<TU, T, TU> f, Func<TU, int, TU> finalize, VectorStorage<TU> state, Zeros zeros = Zeros.AllowSkip)
{
var denseResult = target as DenseVectorStorage<TU>;
if (denseResult == null)
{
denseResult = new DenseVectorStorage<TU>(ColumnCount);
}
state.CopyTo(denseResult);
TU[] result = denseResult.Data;
if (zeros == Zeros.AllowSkip)
{
int[] count = new int[ColumnCount];
for (int row = 0; row < RowCount; row++)
{
var startIndex = RowPointers[row];
var endIndex = RowPointers[row + 1];
for (var j = startIndex; j < endIndex; j++)
{
var column = ColumnIndices[j];
result[column] = f(result[column], Values[j]);
count[column]++;
}
}
for (int j = 0; j < ColumnCount; j++)
{
result[j] = finalize(result[j], count[j]);
}
}
else
{
for (int row = 0; row < RowCount; row++)
{
var index = RowPointers[row];
var endIndex = RowPointers[row + 1];
for (int j = 0; j < ColumnCount; j++)
{
if (index < endIndex && j == ColumnIndices[index])
{
result[j] = f(result[j], Values[index]);
index = Math.Min(index + 1, endIndex);
}
else
{
result[j] = f(result[j], Zero);
}
}
}
for (int j = 0; j < ColumnCount; j++)
{
result[j] = finalize(result[j], RowCount);
}
}
if (!ReferenceEquals(denseResult, target))
{
denseResult.CopyTo(target);
}
}
}
}

763
src/UnitTests/GenericMath.cs

@ -0,0 +1,763 @@
// TAKEN FROM:
// Miscellaneous Utility Library
// http://www.yoda.arachsys.com/csharp/miscutil/
//
// "Miscellaneous Utility Library" Software Licence
//
// Version 1.0
//
// Copyright (c) 2004-2008 Jon Skeet and Marc Gravell.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. The end-user documentation included with the redistribution, if
// any, must include the following acknowledgment:
//
// "This product includes software developed by Jon Skeet
// and Marc Gravell. Contact skeet@pobox.com, or see
// http://www.pobox.com/~skeet/)."
//
// Alternately, this acknowledgment may appear in the software itself,
// if and wherever such third-party acknowledgments normally appear.
//
// 4. The name "Miscellaneous Utility Library" must not be used to endorse
// or promote products derived from this software without prior written
// permission. For written permission, please contact skeet@pobox.com.
//
// 5. Products derived from this software may not be called
// "Miscellaneous Utility Library", nor may "Miscellaneous Utility Library"
// appear in their name, without prior written permission of Jon Skeet.
//
// THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL JON SKEET BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
using System;
using System.Linq.Expressions;
namespace MathNet.Numerics.UnitTests
{
/// <summary>
/// The Operator class provides easy access to the standard operators
/// (addition, etc) for generic types, using type inference to simplify
/// usage.
/// </summary>
internal static class Operator
{
/// <summary>
/// Indicates if the supplied value is non-null,
/// for reference-types or Nullable&lt;T&gt;
/// </summary>
/// <returns>True for non-null values, else false</returns>
public static bool HasValue<T>(T value)
{
return Operator<T>.NullOp.HasValue(value);
}
/// <summary>
/// Increments the accumulator only
/// if the value is non-null. If the accumulator
/// is null, then the accumulator is given the new
/// value; otherwise the accumulator and value
/// are added.
/// </summary>
/// <param name="accumulator">The current total to be incremented (can be null)</param>
/// <param name="value">The value to be tested and added to the accumulator</param>
/// <returns>True if the value is non-null, else false - i.e.
/// "has the accumulator been updated?"</returns>
public static bool AddIfNotNull<T>(ref T accumulator, T value)
{
return Operator<T>.NullOp.AddIfNotNull(ref accumulator, value);
}
/// <summary>
/// Evaluates unary negation (-) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T Negate<T>(T value)
{
return Operator<T>.Negate(value);
}
/// <summary>
/// Evaluates bitwise not (~) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T Not<T>(T value)
{
return Operator<T>.Not(value);
}
/// <summary>
/// Evaluates bitwise or (|) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T Or<T>(T value1, T value2)
{
return Operator<T>.Or(value1, value2);
}
/// <summary>
/// Evaluates bitwise and (&amp;) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T And<T>(T value1, T value2)
{
return Operator<T>.And(value1, value2);
}
/// <summary>
/// Evaluates bitwise xor (^) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T Xor<T>(T value1, T value2)
{
return Operator<T>.Xor(value1, value2);
}
/// <summary>
/// Performs a conversion between the given types; this will throw
/// an InvalidOperationException if the type T does not provide a suitable cast, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this cast.
/// </summary>
public static TTo Convert<TFrom, TTo>(TFrom value)
{
return Operator<TFrom, TTo>.Convert(value);
}
/// <summary>
/// Evaluates binary addition (+) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T Add<T>(T value1, T value2)
{
return Operator<T>.Add(value1, value2);
}
/// <summary>
/// Evaluates binary addition (+) for the given type(s); this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static TArg1 AddAlternative<TArg1, TArg2>(TArg1 value1, TArg2 value2)
{
return Operator<TArg2, TArg1>.Add(value1, value2);
}
/// <summary>
/// Evaluates binary subtraction (-) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T Subtract<T>(T value1, T value2)
{
return Operator<T>.Subtract(value1, value2);
}
/// <summary>
/// Evaluates binary subtraction(-) for the given type(s); this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static TArg1 SubtractAlternative<TArg1, TArg2>(TArg1 value1, TArg2 value2)
{
return Operator<TArg2, TArg1>.Subtract(value1, value2);
}
/// <summary>
/// Evaluates binary multiplication (*) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T Multiply<T>(T value1, T value2)
{
return Operator<T>.Multiply(value1, value2);
}
/// <summary>
/// Evaluates binary multiplication (*) for the given type(s); this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static TArg1 MultiplyAlternative<TArg1, TArg2>(TArg1 value1, TArg2 value2)
{
return Operator<TArg2, TArg1>.Multiply(value1, value2);
}
/// <summary>
/// Evaluates binary division (/) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static T Divide<T>(T value1, T value2)
{
return Operator<T>.Divide(value1, value2);
}
/// <summary>
/// Evaluates binary division (/) for the given type(s); this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static TArg1 DivideAlternative<TArg1, TArg2>(TArg1 value1, TArg2 value2)
{
return Operator<TArg2, TArg1>.Divide(value1, value2);
}
/// <summary>
/// Evaluates binary equality (==) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static bool Equal<T>(T value1, T value2)
{
return Operator<T>.Equal(value1, value2);
}
/// <summary>
/// Evaluates binary inequality (!=) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static bool NotEqual<T>(T value1, T value2)
{
return Operator<T>.NotEqual(value1, value2);
}
/// <summary>
/// Evaluates binary greater-than (&gt;) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static bool GreaterThan<T>(T value1, T value2)
{
return Operator<T>.GreaterThan(value1, value2);
}
/// <summary>
/// Evaluates binary less-than (&lt;) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static bool LessThan<T>(T value1, T value2)
{
return Operator<T>.LessThan(value1, value2);
}
/// <summary>
/// Evaluates binary greater-than-on-eqauls (&gt;=) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static bool GreaterThanOrEqual<T>(T value1, T value2)
{
return Operator<T>.GreaterThanOrEqual(value1, value2);
}
/// <summary>
/// Evaluates binary less-than-or-equal (&lt;=) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static bool LessThanOrEqual<T>(T value1, T value2)
{
return Operator<T>.LessThanOrEqual(value1, value2);
}
/// <summary>
/// Evaluates integer division (/) for the given type; this will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary><remarks>
/// This operation is particularly useful for computing averages and
/// similar aggregates.
/// </remarks>
public static T DivideInt32<T>(T value, int divisor)
{
return Operator<int, T>.Divide(value, divisor);
}
}
/// <summary>
/// Provides standard operators (such as addition) that operate over operands of
/// different types. For operators, the return type is assumed to match the first
/// operand.
/// </summary>
/// <seealso cref="Operator&lt;T&gt;"/>
/// <seealso cref="Operator"/>
internal static class Operator<TValue, TResult>
{
static readonly Func<TValue, TResult> convert;
/// <summary>
/// Returns a delegate to convert a value between two types; this delegate will throw
/// an InvalidOperationException if the type T does not provide a suitable cast, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this cast.
/// </summary>
public static Func<TValue, TResult> Convert
{
get { return convert; }
}
static Operator()
{
convert = ExpressionUtil.CreateExpression<TValue, TResult>(body => Expression.Convert(body, typeof (TResult)));
add = ExpressionUtil.CreateExpression<TResult, TValue, TResult>(Expression.Add, true);
subtract = ExpressionUtil.CreateExpression<TResult, TValue, TResult>(Expression.Subtract, true);
multiply = ExpressionUtil.CreateExpression<TResult, TValue, TResult>(Expression.Multiply, true);
divide = ExpressionUtil.CreateExpression<TResult, TValue, TResult>(Expression.Divide, true);
}
static readonly Func<TResult, TValue, TResult> add, subtract, multiply, divide;
/// <summary>
/// Returns a delegate to evaluate binary addition (+) for the given types; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<TResult, TValue, TResult> Add
{
get { return add; }
}
/// <summary>
/// Returns a delegate to evaluate binary subtraction (-) for the given types; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<TResult, TValue, TResult> Subtract
{
get { return subtract; }
}
/// <summary>
/// Returns a delegate to evaluate binary multiplication (*) for the given types; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<TResult, TValue, TResult> Multiply
{
get { return multiply; }
}
/// <summary>
/// Returns a delegate to evaluate binary division (/) for the given types; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<TResult, TValue, TResult> Divide
{
get { return divide; }
}
}
/// <summary>
/// Provides standard operators (such as addition) over a single type
/// </summary>
/// <seealso cref="Operator"/>
/// <seealso cref="Operator&lt;TValue,TResult&gt;"/>
internal static class Operator<T>
{
static readonly INullOp<T> nullOp;
internal static INullOp<T> NullOp
{
get { return nullOp; }
}
static readonly T zero;
/// <summary>
/// Returns the zero value for value-types (even full Nullable&lt;TInner&gt;) - or null for reference types
/// </summary>
public static T Zero
{
get { return zero; }
}
static readonly Func<T, T> negate, not;
static readonly Func<T, T, T> or, and, xor;
/// <summary>
/// Returns a delegate to evaluate unary negation (-) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T> Negate
{
get { return negate; }
}
/// <summary>
/// Returns a delegate to evaluate bitwise not (~) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T> Not
{
get { return not; }
}
/// <summary>
/// Returns a delegate to evaluate bitwise or (|) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, T> Or
{
get { return or; }
}
/// <summary>
/// Returns a delegate to evaluate bitwise and (&amp;) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, T> And
{
get { return and; }
}
/// <summary>
/// Returns a delegate to evaluate bitwise xor (^) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, T> Xor
{
get { return xor; }
}
static readonly Func<T, T, T> add, subtract, multiply, divide;
/// <summary>
/// Returns a delegate to evaluate binary addition (+) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, T> Add
{
get { return add; }
}
/// <summary>
/// Returns a delegate to evaluate binary subtraction (-) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, T> Subtract
{
get { return subtract; }
}
/// <summary>
/// Returns a delegate to evaluate binary multiplication (*) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, T> Multiply
{
get { return multiply; }
}
/// <summary>
/// Returns a delegate to evaluate binary division (/) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, T> Divide
{
get { return divide; }
}
static readonly Func<T, T, bool> equal, notEqual, greaterThan, lessThan, greaterThanOrEqual, lessThanOrEqual;
/// <summary>
/// Returns a delegate to evaluate binary equality (==) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, bool> Equal
{
get { return equal; }
}
/// <summary>
/// Returns a delegate to evaluate binary inequality (!=) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, bool> NotEqual
{
get { return notEqual; }
}
/// <summary>
/// Returns a delegate to evaluate binary greater-then (&gt;) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, bool> GreaterThan
{
get { return greaterThan; }
}
/// <summary>
/// Returns a delegate to evaluate binary less-than (&lt;) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, bool> LessThan
{
get { return lessThan; }
}
/// <summary>
/// Returns a delegate to evaluate binary greater-than-or-equal (&gt;=) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, bool> GreaterThanOrEqual
{
get { return greaterThanOrEqual; }
}
/// <summary>
/// Returns a delegate to evaluate binary less-than-or-equal (&lt;=) for the given type; this delegate will throw
/// an InvalidOperationException if the type T does not provide this operator, or for
/// Nullable&lt;TInner&gt; if TInner does not provide this operator.
/// </summary>
public static Func<T, T, bool> LessThanOrEqual
{
get { return lessThanOrEqual; }
}
static Operator()
{
add = ExpressionUtil.CreateExpression<T, T, T>(Expression.Add);
subtract = ExpressionUtil.CreateExpression<T, T, T>(Expression.Subtract);
divide = ExpressionUtil.CreateExpression<T, T, T>(Expression.Divide);
multiply = ExpressionUtil.CreateExpression<T, T, T>(Expression.Multiply);
greaterThan = ExpressionUtil.CreateExpression<T, T, bool>(Expression.GreaterThan);
greaterThanOrEqual = ExpressionUtil.CreateExpression<T, T, bool>(Expression.GreaterThanOrEqual);
lessThan = ExpressionUtil.CreateExpression<T, T, bool>(Expression.LessThan);
lessThanOrEqual = ExpressionUtil.CreateExpression<T, T, bool>(Expression.LessThanOrEqual);
equal = ExpressionUtil.CreateExpression<T, T, bool>(Expression.Equal);
notEqual = ExpressionUtil.CreateExpression<T, T, bool>(Expression.NotEqual);
negate = ExpressionUtil.CreateExpression<T, T>(Expression.Negate);
and = ExpressionUtil.CreateExpression<T, T, T>(Expression.And);
or = ExpressionUtil.CreateExpression<T, T, T>(Expression.Or);
not = ExpressionUtil.CreateExpression<T, T>(Expression.Not);
xor = ExpressionUtil.CreateExpression<T, T, T>(Expression.ExclusiveOr);
Type typeT = typeof (T);
if (typeT.IsValueType && typeT.IsGenericType && (typeT.GetGenericTypeDefinition() == typeof (Nullable<>)))
{
// get the *inner* zero (not a null Nullable<TValue>, but default(TValue))
Type nullType = typeT.GetGenericArguments()[0];
zero = (T)Activator.CreateInstance(nullType);
nullOp = (INullOp<T>)Activator.CreateInstance(
typeof (StructNullOp<>).MakeGenericType(nullType));
}
else
{
zero = default(T);
if (typeT.IsValueType)
{
nullOp = (INullOp<T>)Activator.CreateInstance(
typeof (StructNullOp<>).MakeGenericType(typeT));
}
else
{
nullOp = (INullOp<T>)Activator.CreateInstance(
typeof (ClassNullOp<>).MakeGenericType(typeT));
}
}
}
}
/// <summary>
/// General purpose Expression utilities
/// </summary>
internal static class ExpressionUtil
{
/// <summary>
/// Create a function delegate representing a unary operation
/// </summary>
/// <typeparam name="TArg1">The parameter type</typeparam>
/// <typeparam name="TResult">The return type</typeparam>
/// <param name="body">Body factory</param>
/// <returns>Compiled function delegate</returns>
public static Func<TArg1, TResult> CreateExpression<TArg1, TResult>(
Func<Expression, UnaryExpression> body)
{
ParameterExpression inp = Expression.Parameter(typeof (TArg1), "inp");
try
{
return Expression.Lambda<Func<TArg1, TResult>>(body(inp), inp).Compile();
}
catch (Exception ex)
{
string msg = ex.Message; // avoid capture of ex itself
return delegate { throw new InvalidOperationException(msg); };
}
}
/// <summary>
/// Create a function delegate representing a binary operation
/// </summary>
/// <typeparam name="TArg1">The first parameter type</typeparam>
/// <typeparam name="TArg2">The second parameter type</typeparam>
/// <typeparam name="TResult">The return type</typeparam>
/// <param name="body">Body factory</param>
/// <returns>Compiled function delegate</returns>
public static Func<TArg1, TArg2, TResult> CreateExpression<TArg1, TArg2, TResult>(
Func<Expression, Expression, BinaryExpression> body)
{
return CreateExpression<TArg1, TArg2, TResult>(body, false);
}
/// <summary>
/// Create a function delegate representing a binary operation
/// </summary>
/// <param name="castArgsToResultOnFailure">
/// If no matching operation is possible, attempt to convert
/// TArg1 and TArg2 to TResult for a match? For example, there is no
/// "decimal operator /(decimal, int)", but by converting TArg2 (int) to
/// TResult (decimal) a match is found.
/// </param>
/// <typeparam name="TArg1">The first parameter type</typeparam>
/// <typeparam name="TArg2">The second parameter type</typeparam>
/// <typeparam name="TResult">The return type</typeparam>
/// <param name="body">Body factory</param>
/// <returns>Compiled function delegate</returns>
public static Func<TArg1, TArg2, TResult> CreateExpression<TArg1, TArg2, TResult>(
Func<Expression, Expression, BinaryExpression> body, bool castArgsToResultOnFailure)
{
ParameterExpression lhs = Expression.Parameter(typeof (TArg1), "lhs");
ParameterExpression rhs = Expression.Parameter(typeof (TArg2), "rhs");
try
{
try
{
return Expression.Lambda<Func<TArg1, TArg2, TResult>>(body(lhs, rhs), lhs, rhs).Compile();
}
catch (InvalidOperationException)
{
if (castArgsToResultOnFailure && !( // if we show retry
typeof (TArg1) == typeof (TResult) && // and the args aren't
typeof (TArg2) == typeof (TResult)))
{
// already "TValue, TValue, TValue"...
// convert both lhs and rhs to TResult (as appropriate)
Expression castLhs = typeof (TArg1) == typeof (TResult) ?
(Expression)lhs :
(Expression)Expression.Convert(lhs, typeof (TResult));
Expression castRhs = typeof (TArg2) == typeof (TResult) ?
(Expression)rhs :
(Expression)Expression.Convert(rhs, typeof (TResult));
return Expression.Lambda<Func<TArg1, TArg2, TResult>>(
body(castLhs, castRhs), lhs, rhs).Compile();
}
else throw;
}
}
catch (Exception ex)
{
string msg = ex.Message; // avoid capture of ex itself
return delegate { throw new InvalidOperationException(msg); };
}
}
}
internal interface INullOp<T>
{
bool HasValue(T value);
bool AddIfNotNull(ref T accumulator, T value);
}
internal sealed class StructNullOp<T>
: INullOp<T>, INullOp<T?>
where T : struct
{
public bool HasValue(T value)
{
return true;
}
public bool AddIfNotNull(ref T accumulator, T value)
{
accumulator = Operator<T>.Add(accumulator, value);
return true;
}
public bool HasValue(T? value)
{
return value.HasValue;
}
public bool AddIfNotNull(ref T? accumulator, T? value)
{
if (value.HasValue)
{
accumulator = accumulator.HasValue ?
Operator<T>.Add(
accumulator.GetValueOrDefault(),
value.GetValueOrDefault())
: value;
return true;
}
return false;
}
}
internal sealed class ClassNullOp<T>
: INullOp<T>
where T : class
{
public bool HasValue(T value)
{
return value != null;
}
public bool AddIfNotNull(ref T accumulator, T value)
{
if (value != null)
{
accumulator = accumulator == null ?
value : Operator<T>.Add(accumulator, value);
return true;
}
return false;
}
}
}

0
src/UnitTests/MatrixHelpers.cs → src/UnitTests/LinearAlgebraTests/MatrixHelpers.cs

44
src/UnitTests/LinearAlgebraTests/MatrixStructureTheory.Map.cs → src/UnitTests/LinearAlgebraTests/MatrixStructureTheory.Functional.cs

@ -1,4 +1,4 @@
// <copyright file="MatrixStructureTheory.Map.cs" company="Math.NET">
// <copyright file="MatrixStructureTheory.Functional.cs" company="Math.NET">
// Math.NET Numerics, part of the Math.NET Project
// http://numerics.mathdotnet.com
// http://github.com/mathnet/mathnet-numerics
@ -256,5 +256,47 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests
sparse.SetSubMatrix(1, 0, matrix.RowCount - 1, 1, 0, matrix.ColumnCount, Matrix<T>.Build.Dense(matrix.RowCount - 1, matrix.ColumnCount, one));
Assert.That(sparse.Enumerate().All(one.Equals), Is.True);
}
[Theory]
public void CanFoldRows(Matrix<T> matrix)
{
// not forced
var rowSum = matrix.FoldRows((s, x) => Operator<T>.Add(s, x), Operator<T>.Zero, Zeros.AllowSkip);
for (int i = 0; i < rowSum.Count; i++)
{
Assert.That(rowSum.At(i), Is.EqualTo(matrix.Row(i).Enumerate().Aggregate((a, b) => Operator<T>.Add(a, b))), "not forced");
}
// forced
rowSum = matrix.FoldRows((s, x) => Operator<T>.Add(s, x), Operator<T>.Zero, Zeros.Include);
for (int i = 0; i < rowSum.Count; i++)
{
Assert.That(rowSum.At(i), Is.EqualTo(matrix.Row(i).Enumerate().Aggregate((a, b) => Operator<T>.Add(a, b))), "forced");
}
Assert.That(matrix.FoldRows((s, x) => s + 1.0, 0.0, Zeros.Include),
Is.EqualTo(Vector<double>.Build.Dense(matrix.RowCount, matrix.ColumnCount)), "forced - full coverage");
}
[Theory]
public void CanFoldColumns(Matrix<T> matrix)
{
// not forced
var colSum = matrix.FoldColumns((s, x) => Operator<T>.Add(s, x), Operator<T>.Zero, Zeros.AllowSkip);
for (int i = 0; i < colSum.Count; i++)
{
Assert.That(colSum.At(i), Is.EqualTo(matrix.Column(i).Enumerate().Aggregate((a, b) => Operator<T>.Add(a, b))), "not forced");
}
// forced
colSum = matrix.FoldColumns((s, x) => Operator<T>.Add(s, x), Operator<T>.Zero, Zeros.Include);
for (int i = 0; i < colSum.Count; i++)
{
Assert.That(colSum.At(i), Is.EqualTo(matrix.Column(i).Enumerate().Aggregate((a, b) => Operator<T>.Add(a, b))), "forced");
}
Assert.That(matrix.FoldColumns((s, x) => s + 1.0, 0.0, Zeros.Include),
Is.EqualTo(Vector<double>.Build.Dense(matrix.ColumnCount, matrix.RowCount)), "forced - full coverage");
}
}
}

5
src/UnitTests/UnitTests.csproj

@ -303,9 +303,10 @@
<Compile Include="LinearAlgebraTests\Double\VectorTests.Arithmetic.cs" />
<Compile Include="LinearAlgebraTests\Double\VectorTests.cs" />
<Compile Include="LinearAlgebraTests\Double\VectorTests.Norm.cs" />
<Compile Include="GenericMath.cs" />
<Compile Include="LinearAlgebraTests\MatrixStructureTheory.Access.cs" />
<Compile Include="LinearAlgebraTests\MatrixStructureTheory.cs" />
<Compile Include="LinearAlgebraTests\MatrixStructureTheory.Map.cs" />
<Compile Include="LinearAlgebraTests\MatrixStructureTheory.Functional.cs" />
<Compile Include="LinearAlgebraTests\MatrixStructureTheory.Reform.cs" />
<Compile Include="LinearAlgebraTests\Single\DenseMatrixTests.cs" />
<Compile Include="LinearAlgebraTests\Single\DenseVectorArithmeticTheory.cs" />
@ -358,7 +359,7 @@
<Compile Include="LinearAlgebraTests\Single\VectorTests.Norm.cs" />
<Compile Include="LinearAlgebraTests\Double\VectorArithmeticTheory.cs" />
<Compile Include="LinearAlgebraTests\VectorArithmeticTheory.cs" />
<Compile Include="MatrixHelpers.cs" />
<Compile Include="LinearAlgebraTests\MatrixHelpers.cs" />
<Compile Include="EuclidTests\GcdRelatedTest.cs" />
<Compile Include="EuclidTests\GcdRelatedTestBigInteger.cs" />
<Compile Include="EuclidTests\IntegerTheoryTest.cs" />

Loading…
Cancel
Save