Skip to content

0.31.6.0: Adds implicit and explicit conversions from double[][] to F64Matrix #124

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Oct 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<Project>
<PropertyGroup>
<Version>0.31.5.0</Version>
<AssemblyVersion>0.31.5.0</AssemblyVersion>
<FileVersion>0.31.5.0</FileVersion>
<Version>0.31.6.0</Version>
<AssemblyVersion>0.31.6.0</AssemblyVersion>
<FileVersion>0.31.6.0</FileVersion>
<NeutralLanguage>en</NeutralLanguage>
<Authors>Mads Dabros</Authors>
<Copyright>Copyright © Mads Dabros 2014</Copyright>
Expand Down
16 changes: 15 additions & 1 deletion src/SharpLearning.Containers.Test/Matrices/F64MatrixTest.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SharpLearning.Containers.Matrices;
using System;

namespace SharpLearning.Containers.Test.Matrices
{
Expand Down Expand Up @@ -101,7 +102,7 @@ public void F64Matrix_Rows_Predefined()
{
var sut = CreateFeatures();
var actual = new F64Matrix(2, 3);
sut.Rows(new int [] { 0, 2}, actual);
sut.Rows(new int[] { 0, 2 }, actual);
var expected = GetExpectedRowSubMatrix();

Assert.IsTrue(expected.Equals(actual));
Expand All @@ -128,6 +129,19 @@ public void F64Matrix_Columns_predefined()
Assert.IsTrue(expected.Equals(actual));
}

[TestMethod]
public void F64Matrix_Implicit_Conversion()
{
Func<F64Matrix, F64Matrix> converter = m => m;

var actual = converter(new double[][] { new double[] { 0, 1 }, new double[] { 2, 3 } });

Assert.AreEqual(0, actual.At(0,0));
Assert.AreEqual(1, actual.At(0,1));
Assert.AreEqual(2, actual.At(1,0));
Assert.AreEqual(3, actual.At(1,1));
}

double[] GetExpectedColumn()
{
return new double[3] { 2, 20, 200 };
Expand Down
52 changes: 31 additions & 21 deletions src/SharpLearning.Containers/Extensions/ArrayExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,10 @@ public static void SortWith<TKey, TValues>(this TKey[] keys, Interval1D interval
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="interval"></param>
/// <param name="distination"></param>
public static void CopyTo<T>(this T[] source, Interval1D interval, T[] distination)
/// <param name="destination"></param>
public static void CopyTo<T>(this T[] source, Interval1D interval, T[] destination)
{
Array.Copy(source, interval.FromInclusive, distination, interval.FromInclusive, interval.Length);
Array.Copy(source, interval.FromInclusive, destination, interval.FromInclusive, interval.Length);
}

/// <summary>
Expand All @@ -225,7 +225,7 @@ public static void IndexedCopy<T>(this int[] indices, T[] source, Interval1D int
/// <param name="source"></param>
/// <param name="interval"></param>
/// <param name="destination"></param>
public static void IndexedCopy(this int[] indices, F64MatrixColumnView source,
public static void IndexedCopy(this int[] indices, F64MatrixColumnView source,
Interval1D interval, double[] destination)
{
for (int i = interval.FromInclusive; i < interval.ToExclusive; i++)
Expand Down Expand Up @@ -354,22 +354,32 @@ public static double ScoreAtPercentile(this double[] values, double percentile)
var index = percentile * (values.Length - 1.0);
var i = (int)index;
var diff = index - i;
if(diff != 0.0)

if (diff != 0.0)
{
var j = i + 1;
var v1 = array[i];
var w1 = j - index;

var v2 = array[j];
var w2 = index - i;

return (v1 * w1 + v2 * w2) / (w1 + w2);
}

return array[i];
}

/// <summary>
/// Converts an array of arrays to an F64Matrix
/// </summary>
/// <param name="m"></param>
/// <returns></returns>
public static F64Matrix ToF64Matrix(this double[][] m)
{
return ToF64Matrix(m.ToList());
}

/// <summary>
/// Converts a list of arrays to an F64Matrix
/// </summary>
Expand Down Expand Up @@ -456,7 +466,7 @@ public static int[] StratifiedIndexSampling<T>(this T[] data, int sampleSize, Ra
{
if (data.Length < sampleSize)
{
throw new ArgumentException("SampleSize " + sampleSize +
throw new ArgumentException("SampleSize " + sampleSize +
" is larger than data size " + data.Length);
}

Expand All @@ -467,7 +477,7 @@ public static int[] StratifiedIndexSampling<T>(this T[] data, int sampleSize, Ra
{
if (kvp.Value == 0)
{
throw new ArgumentException("Sample size is too small for value: " +
throw new ArgumentException("Sample size is too small for value: " +
kvp.Key + " to be included.");
}
}
Expand All @@ -477,12 +487,12 @@ public static int[] StratifiedIndexSampling<T>(this T[] data, int sampleSize, Ra
indices.Shuffle(random);

var currentSampleCount = requiredSamples.ToDictionary(k => k.Key, k => 0);

// might be slightly different than the specified depending on data distribution
var actualSampleSize = requiredSamples.Select(s => s.Value).Sum();

// if actual sample size is different from specified add/subtract duff from largest class
if(actualSampleSize != sampleSize)
if (actualSampleSize != sampleSize)
{
var diff = sampleSize - actualSampleSize;
var largestClassKey = requiredSamples.OrderByDescending(s => s.Value).First().Key;
Expand All @@ -491,12 +501,12 @@ public static int[] StratifiedIndexSampling<T>(this T[] data, int sampleSize, Ra

var sampleIndices = new int[sampleSize];
var sampleIndex = 0;

for (int i = 0; i < data.Length; i++)
{
var index = indices[i];
var value = data[index];
if(currentSampleCount[value] != requiredSamples[value])
if (currentSampleCount[value] != requiredSamples[value])
{
sampleIndices[sampleIndex++] = index;
currentSampleCount[value]++;
Expand Down Expand Up @@ -531,15 +541,15 @@ public static int[] StratifiedIndexSampling<T>(this T[] data, int sampleSize, Ra
/// <returns></returns>
public static int[] StratifiedIndexSampling<T>(this T[] data, int sampleSize, int[] dataIndices, Random random)
{
if (dataIndices.Length < sampleSize)
if (dataIndices.Length < sampleSize)
{
throw new ArgumentException("SampleSize " + sampleSize +
throw new ArgumentException("SampleSize " + sampleSize +
" is larger than dataIndices size " + dataIndices.Length);
}

if (data.Length < dataIndices.Length)
if (data.Length < dataIndices.Length)
{
throw new ArgumentException("dataIndices " + dataIndices.Length +
throw new ArgumentException("dataIndices " + dataIndices.Length +
" is larger than data size " + data.Length);
}

Expand All @@ -550,15 +560,15 @@ public static int[] StratifiedIndexSampling<T>(this T[] data, int sampleSize, in
{
if (kvp.Value == 0)
{
throw new ArgumentException("Sample size is too small for value: " +
throw new ArgumentException("Sample size is too small for value: " +
kvp.Key + " to be included.");
}
}

var currentSampleCount = requiredSamples.ToDictionary(k => k.Key, k => 0);
// might be slightly different than the specified depending on data distribution
var actualSampleSize = requiredSamples.Select(s => s.Value).Sum();

// if actual sample size is different from specified add/subtract difference from largest class
if (actualSampleSize != sampleSize)
{
Expand All @@ -584,7 +594,7 @@ public static int[] StratifiedIndexSampling<T>(this T[] data, int sampleSize, in
currentSampleCount[value]++;
}

if(sampleIndex == sampleSize)
if (sampleIndex == sampleSize)
{
break;
}
Expand Down
4 changes: 4 additions & 0 deletions src/SharpLearning.Containers/Matrices/F64Matrix.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
using System;
using System.Linq;
using SharpLearning.Containers.Views;
using SharpLearning.Containers.Extensions;

namespace SharpLearning.Containers.Matrices
{
/// <summary>
/// Matrix of doubles
/// </summary>
/// <remarks>Can be implicitly converted from double[][]</remarks>
public sealed unsafe class F64Matrix : IMatrix<double>, IEquatable<F64Matrix>
{
double[] m_featureArray;
Expand Down Expand Up @@ -301,5 +303,7 @@ public override int GetHashCode()
return hash;
}
}

public static implicit operator F64Matrix(double[][] b) => b.ToF64Matrix();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,34 @@ public void ClassificationNeuralNetLearner_Learn()
var numberOfClasses = 5;

var random = new Random(32);
var (observations, targets) = CreateData(numberOfObservations,
var (observations, targets) = CreateData(numberOfObservations,
numberOfFeatures, numberOfClasses, random);

var net = new NeuralNet();
net.Add(new InputLayer(numberOfFeatures));
net.Add(new DenseLayer(10));
net.Add(new SvmLayer(numberOfClasses));

var sut = new ClassificationNeuralNetLearner(net, new AccuracyLoss());
var model = sut.Learn(observations, targets);

var predictions = model.Predict(observations);

var evaluator = new TotalErrorClassificationMetric<double>();
var actual = evaluator.Error(targets, predictions);

Assert.AreEqual(0.762, actual);
}

[TestMethod]
public void ClassificationNeuralNetLearner_Learn_Array()
{
var numberOfObservations = 500;
var numberOfFeatures = 5;
var numberOfClasses = 5;

var random = new Random(32);
var (observations, targets) = CreateArrayData(numberOfObservations,
numberOfFeatures, numberOfClasses, random);

var net = new NeuralNet();
Expand Down Expand Up @@ -48,10 +75,10 @@ public void ClassificationNeuralNetLearner_Learn_Early_Stopping()

var random = new Random(32);

var (observations, targets) = CreateData(numberOfObservations,
var (observations, targets) = CreateData(numberOfObservations,
numberOfFeatures, numberOfClasses, random);

var (validationObservations, validationTargets) = CreateData(numberOfObservations,
var (validationObservations, validationTargets) = CreateData(numberOfObservations,
numberOfFeatures, numberOfClasses, random);

var net = new NeuralNet();
Expand Down Expand Up @@ -92,5 +119,16 @@ public void ClassificationNeuralNetLearner_Constructor_Throw_On_Wrong_OutputLaye

return (observations, targets);
}

(double[][] observations, double[] targets) CreateArrayData(
int numberOfObservations, int numberOfFeatures, int numberOfClasses, Random random)
{
var observations = Enumerable.Range(0, numberOfObservations).Select(i => Enumerable.Range(0, numberOfFeatures)
.Select(ii => random.NextDouble()).ToArray()).ToArray();
var targets = Enumerable.Range(0, numberOfObservations)
.Select(i => (double)random.Next(0, numberOfClasses)).ToArray();

return (observations, targets);
}
}
}