using System; using System.Collections.Generic; using System.Linq; using System.Text; using NUnit.Framework; namespace SharpGLTF.Transforms { [Category("Core.Transforms")] public class SparseWeight8Tests { [TestCase(0)] [TestCase(1)] [TestCase(0,0.0001f)] [TestCase(2, -2, 2, -2)] [TestCase(0.2f, 0.15f, 0.25f, 0.10f, 0.30f)] [TestCase(0, 0, 1, 0, 2, 0, 3, 4, 5, 0, 6, 0, 7, 0, 6, 0, 9, 0, 11)] [TestCase(9, -9, 8, -8, 7, -7, 6, -6, 5, -5, 4, -4, 3, -3, 2, -2, 1, -1)] [TestCase(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)] [TestCase(0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)] public void TestSparseCreation(params float[] array1) { var array2 = CreateSparseCompatibleArray(array1); var array3 = array1 .Select((val, idx) => (idx, val)) .Where(item => item.val != 0) .Reverse() .ToArray(); // creation mode 1 var sparse = SparseWeight8.Create(array1); Assert.That(sparse.WeightSum, Is.EqualTo(array2.Sum())); Assert.That(sparse.Expand(array2.Length), Is.EqualTo(array2)); // creation mode 2 var indexedSparse = SparseWeight8.Create(array3); Assert.That(indexedSparse.WeightSum, Is.EqualTo(array2.Sum()).Within(0.000001f)); Assert.That(indexedSparse.Expand(array2.Length), Is.EqualTo(array2)); Assert.That(SparseWeight8.AreEqual(sparse, indexedSparse)); // sort by weights var sByWeights = SparseWeight8.OrderedByWeight(sparse); Assert.That(sByWeights.WeightSum, Is.EqualTo(array2.Sum())); Assert.That(sByWeights.Expand(array2.Length), Is.EqualTo(array2)); CheckWeightOrdered(sByWeights); // sort by indices var sByIndices = SparseWeight8.OrderedByIndex(sByWeights); CheckIndexOrdered(sByIndices); Assert.That(sByIndices.WeightSum, Is.EqualTo(array2.Sum())); Assert.That(sByIndices.Expand(array2.Length), Is.EqualTo(array2)); // equality Assert.That(SparseWeight8.AreEqual(sByIndices, sByWeights), Is.True); Assert.That(sByWeights.GetHashCode(), Is.EqualTo(sByIndices.GetHashCode())); // sum var sum = SparseWeight8.Add(sByIndices, sByWeights); Assert.That(sum.WeightSum, Is.EqualTo(array2.Sum() * 2)); // complement normalization if (!array2.Any(item => item<0)) { Assert.That(sparse.GetNormalizedWithComplement(int.MaxValue).WeightSum, Is.GreaterThanOrEqualTo(1)); } } [Test] public void TestSparseCreation() { var sparse = SparseWeight8.Create ( (9, 9), (8, 2), (5, 1), // we set these weights separately (5, 1), // to check that 5 will pass 8 (5, 1), // in the sorted set. (7, 1) ); Assert.That(sparse[5], Is.EqualTo(3)); Assert.That(sparse[7], Is.EqualTo(1)); Assert.That(sparse[8], Is.EqualTo(2)); Assert.That(sparse[9], Is.EqualTo(9)); } [Test] public void TestCreateSparseFromVectors() { Assert.That ( SparseWeight8.Create(new System.Numerics.Vector4(0, 1, 2, 3), new System.Numerics.Vector4(1, 1, 1, 1)).Expand(4), Is.EqualTo(SparseWeight8.Create(1, 1, 1, 1).Expand(4)) ); Assert.That ( SparseWeight8.Create(new System.Numerics.Vector4(0, 1, 2, 3), new System.Numerics.Vector4(1, 2, 3, 4)).Expand(4), Is.EqualTo(SparseWeight8.Create(1, 2, 3, 4).Expand(4)) ); Assert.That ( SparseWeight8.Create(new System.Numerics.Vector4(0, 1, 2, 3), new System.Numerics.Vector4(4, 3, 2, 1)).Expand(4), Is.EqualTo(SparseWeight8.Create(4, 3, 2, 1).Expand(4)) ); Assert.That ( SparseWeight8.Create(new System.Numerics.Vector4(0, 2, 2, 3), new System.Numerics.Vector4(4, 3, 2, 1)).Expand(4), Is.EqualTo(SparseWeight8.Create(4, 0, 5, 1).Expand(4)) ); Assert.That ( SparseWeight8.Create(new System.Numerics.Vector4(1, 1, 1, 1), new System.Numerics.Vector4(1, 1, 1, 1)).Expand(4), Is.EqualTo(SparseWeight8.Create(0, 4, 0, 0).Expand(4)) ); } /// /// Creates a new array with only the 8 most relevant weights. /// /// /// static float[] CreateSparseCompatibleArray(params float[] array) { const int MAXWEIGHTS = 8; if (array == null) return null; var threshold =array .Select(item => Math.Abs(item)) .OrderByDescending(item => item) .Take(MAXWEIGHTS) .Min(); var array2 = new float[array.Length]; var c = 0; for(int i=0; i < array2.Length; ++i) { var v = array[i]; if (v == 0) continue; if (Math.Abs(v) >= threshold) { array2[i] = v; ++c; if (c >= MAXWEIGHTS) return array2; } } return array2; } static void CheckWeightOrdered(SparseWeight8 sparse) { Assert.Multiple(() => { Assert.That(Math.Abs(sparse.Weight0), Is.GreaterThanOrEqualTo(Math.Abs(sparse.Weight1))); Assert.That(Math.Abs(sparse.Weight1), Is.GreaterThanOrEqualTo(Math.Abs(sparse.Weight2))); Assert.That(Math.Abs(sparse.Weight2), Is.GreaterThanOrEqualTo(Math.Abs(sparse.Weight3))); Assert.That(Math.Abs(sparse.Weight3), Is.GreaterThanOrEqualTo(Math.Abs(sparse.Weight4))); Assert.That(Math.Abs(sparse.Weight4), Is.GreaterThanOrEqualTo(Math.Abs(sparse.Weight5))); Assert.That(Math.Abs(sparse.Weight5), Is.GreaterThanOrEqualTo(Math.Abs(sparse.Weight6))); Assert.That(Math.Abs(sparse.Weight6), Is.GreaterThanOrEqualTo(Math.Abs(sparse.Weight7))); }); } static void CheckIndexOrdered(SparseWeight8 sparse) { var pairs = sparse.GetIndexedWeights(); bool zeroFound = false; long lastIndex = long.MinValue; foreach(var (index,weight) in pairs) { if (weight == 0) zeroFound = true; if (zeroFound) { Assert.That(index, Is.EqualTo(0)); Assert.That(weight, Is.EqualTo(0)); continue; } Assert.That(index, Is.GreaterThan(lastIndex)); lastIndex = index; } } [Test] public void TestSparseNormalization() { var sparse1 = SparseWeight8 .Create(0, 0, 0, 0, 0, 0.1f, 0.7f, 0, 0, 0, 0.1f) .GetNormalizedWithComplement(int.MaxValue); Assert.That(sparse1[5], Is.EqualTo(0.1f)); Assert.That(sparse1[6], Is.EqualTo(0.7f)); Assert.That(sparse1[10], Is.EqualTo(0.1f)); Assert.That(sparse1[int.MaxValue], Is.EqualTo(0.1f).Within(0.0000001f)); Assert.That(sparse1.WeightSum, Is.EqualTo(1)); } [Test] public void TestSparseEquality() { Assert.That(SparseWeight8.AreEqual(SparseWeight8.Create(0, 1), SparseWeight8.Create(0, 1)), Is.True); Assert.That(SparseWeight8.AreEqual(SparseWeight8.Create(0, 1), SparseWeight8.Create(0, 1, 0.25f)), Is.False); Assert.That(SparseWeight8.AreEqual(SparseWeight8.Create(0, 1), SparseWeight8.Create(1, 0)), Is.False); // check if two "half weights" are equal to one "full weight" //Assert.IsTrue(SparseWeight8.AreWeightsEqual(SparseWeight8.Create((3, 5), (3, 5)), SparseWeight8.Create((3, 10)))); } [Test] public void TestSparseWeightsLinearInterpolation1() { var x = SparseWeight8.Create(0,0,1,2); Assert.That(x.Expand(4), Is.EqualTo(new[] { 0f, 0f, 1f, 2f })); var y = SparseWeight8.Create(1,2,0,0); Assert.That(y.Expand(4), Is.EqualTo(new[] { 1f, 2f, 0f, 0f })); var z = SparseWeight8.InterpolateLinear(x, y, 0.5f); Assert.That(z[0], Is.EqualTo(0.5f)); Assert.That(z[1], Is.EqualTo(1)); Assert.That(z[2], Is.EqualTo(0.5f)); Assert.That(z[3], Is.EqualTo(1)); } [Test] public void TestSparseWeightsLinearInterpolation2() { var ax = new float[] { 0, 0, 0, 0, 0, 0.1f, 0.7f, 0, 0, 0, 0.1f }; var ay = new float[] { 0, 0, 0.2f, 0, 0.1f, 0, 0, 0, 0, 0, 0, 0, 0.2f }; var cc = Math.Min(ax.Length, ay.Length); var x = SparseWeight8.Create(ax); Assert.That(x.Expand(ax.Length), Is.EqualTo(ax)); var y = SparseWeight8.Create(ay); Assert.That(y.Expand(ay.Length), Is.EqualTo(ay)); var z = SparseWeight8.InterpolateLinear(x, y, 0.5f); for (int i=0; i < cc; ++i) { var w = (ax[i] + ay[i]) / 2; Assert.That(z[i], Is.EqualTo(w)); } } [Test] public void TestSparseWeightsCubicInterpolation() { var a = SparseWeight8.Create(0, 0, 0.2f, 0, 0, 0, 1); var b = SparseWeight8.Create(1, 1, 0.4f, 0, 0, 1, 0); var t = SparseWeight8.Subtract(b, a); Assert.That(t[0], Is.EqualTo(1)); Assert.That(t[1], Is.EqualTo(1)); Assert.That(t[2], Is.EqualTo(0.2f)); Assert.That(t[3], Is.EqualTo(0)); Assert.That(t[4], Is.EqualTo(0)); Assert.That(t[5], Is.EqualTo(1)); Assert.That(t[6], Is.EqualTo(-1)); var lr = SparseWeight8.InterpolateLinear(a, b, 0.4f); var cr = SparseWeight8.InterpolateCubic(a, t, b, t, 0.4f); Assert.That(cr[0], Is.EqualTo(lr[0]).Within(0.000001f)); Assert.That(cr[1], Is.EqualTo(lr[1]).Within(0.000001f)); Assert.That(cr[2], Is.EqualTo(lr[2]).Within(0.000001f)); Assert.That(cr[3], Is.EqualTo(lr[3]).Within(0.000001f)); Assert.That(cr[4], Is.EqualTo(lr[4]).Within(0.000001f)); Assert.That(cr[5], Is.EqualTo(lr[5]).Within(0.000001f)); Assert.That(cr[6], Is.EqualTo(lr[6]).Within(0.000001f)); Assert.That(cr[7], Is.EqualTo(lr[7]).Within(0.000001f)); } [Test] public void TestSparseWeightReduction() { var a = SparseWeight8.Create(5, 3, 2, 4, 0, 4, 2, 6, 3, 6, 1); var b = a.GetTrimmed(4); Assert.That(b.GetNonZeroWeights().Count(), Is.EqualTo(4)); Assert.That(b[0], Is.EqualTo(a[0])); Assert.That(b[3], Is.EqualTo(a[3])); Assert.That(b[7], Is.EqualTo(a[7])); Assert.That(b[9], Is.EqualTo(a[9])); Assert.That(b.Weight4, Is.EqualTo(0)); Assert.That(b.Weight5, Is.EqualTo(0)); Assert.That(b.Weight6, Is.EqualTo(0)); Assert.That(b.Weight7, Is.EqualTo(0)); } } }