-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
855cc98
commit ada17ab
Showing
11 changed files
with
818 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
|
||
Microsoft Visual Studio Solution File, Format Version 12.00 | ||
# Visual Studio Version 16 | ||
VisualStudioVersion = 16.0.29519.181 | ||
MinimumVisualStudioVersion = 10.0.40219.1 | ||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CompensatedAccumulators", "CompensatedAccumulators\CompensatedAccumulators.csproj", "{B44E95B2-6403-4C76-873E-C338502A8CAD}" | ||
EndProject | ||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CompensatedAccumulators.Test", "CompensatedAccumulators.Test\CompensatedAccumulators.Test.csproj", "{E9E5D48D-A70E-4D08-9EA0-CC9EC712576F}" | ||
EndProject | ||
Global | ||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
Debug|Any CPU = Debug|Any CPU | ||
Release|Any CPU = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{B44E95B2-6403-4C76-873E-C338502A8CAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{B44E95B2-6403-4C76-873E-C338502A8CAD}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{B44E95B2-6403-4C76-873E-C338502A8CAD}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{B44E95B2-6403-4C76-873E-C338502A8CAD}.Release|Any CPU.Build.0 = Release|Any CPU | ||
{E9E5D48D-A70E-4D08-9EA0-CC9EC712576F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{E9E5D48D-A70E-4D08-9EA0-CC9EC712576F}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{E9E5D48D-A70E-4D08-9EA0-CC9EC712576F}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{E9E5D48D-A70E-4D08-9EA0-CC9EC712576F}.Release|Any CPU.Build.0 = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(SolutionProperties) = preSolution | ||
HideSolutionNode = FALSE | ||
EndGlobalSection | ||
GlobalSection(ExtensibilityGlobals) = postSolution | ||
SolutionGuid = {6B955F72-BAF7-4423-A591-D2AE25DF73A8} | ||
EndGlobalSection | ||
EndGlobal |
20 changes: 20 additions & 0 deletions
20
CompensatedAccumulators.Test/CompensatedAccumulators.Test.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>netcoreapp3.1</TargetFramework> | ||
|
||
<IsPackable>false</IsPackable> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" /> | ||
<PackageReference Include="xunit" Version="2.4.0" /> | ||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" /> | ||
<PackageReference Include="coverlet.collector" Version="1.0.1" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\CompensatedAccumulators\CompensatedAccumulators.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
//--------------------------------------------------------------------------- | ||
// PROJECT : Compensated Accumulators | ||
// COPYRIGHT : Andy Thomas (C) 2019 | ||
// WEB URL : https://kuiper.zone | ||
// LICENSE : GPLv3 | ||
//--------------------------------------------------------------------------- | ||
|
||
using System; | ||
using System.Diagnostics; | ||
using Xunit; | ||
using Xunit.Abstractions; | ||
|
||
namespace CompensatedAccumulators.Test | ||
{ | ||
public class CompensatedSumTest | ||
{ | ||
/// <summary> | ||
/// Implementation kind used in test theory data. | ||
/// </summary> | ||
public enum SumKind {Naive, Pairwise, Kahan, Neumaier, Klein}; | ||
|
||
private readonly ITestOutputHelper helper; | ||
|
||
/// <summary> | ||
/// Constructor with test helper. | ||
/// </summary> | ||
public CompensatedSumTest(ITestOutputHelper hlp) | ||
{ | ||
helper = hlp; | ||
} | ||
|
||
/// <summary> | ||
/// Test result of summation is not lost due to presence (and subtraction) of high values. | ||
/// Not all accumulators will pass this test. | ||
/// </summary> | ||
[Theory] | ||
[InlineData(SumKind.Neumaier)] | ||
[InlineData(SumKind.Klein)] | ||
public void Add_PetersTest_ExpectSumOf2(SumKind kind) | ||
{ | ||
// https://en.wikipedia.org/wiki/Kahan_summation_algorithm | ||
var sum = CreateSum(kind); | ||
sum.Add(1.0); | ||
sum.Add(1E100); | ||
sum.Add(1.0); | ||
sum.Add(-1E100); | ||
|
||
// Only Neumaier and Klein both give correct answer 2.0. | ||
// Others expected to give 0. | ||
helper.WriteLine(sum.Value.ToString()); | ||
|
||
Assert.Equal(2.0, sum.Value, 10); | ||
} | ||
|
||
/// <summary> | ||
/// PSRNG zero sum sequence. Expect better result than naive summation. | ||
/// </summary> | ||
[Theory] | ||
[InlineData(SumKind.Pairwise)] | ||
[InlineData(SumKind.Kahan)] | ||
[InlineData(SumKind.Neumaier)] | ||
[InlineData(SumKind.Klein)] | ||
[InlineData(SumKind.Naive)] | ||
public void Add_PsrngZeroSum_ExpectBetterAccuracyThanNaive(SumKind kind) | ||
{ | ||
const double Spread = 1E7; | ||
|
||
var sum = CreateSum(kind); | ||
var naive = CreateSum(SumKind.Naive); | ||
|
||
var rand = new Random(2002); | ||
|
||
for(int n = 0; n < 1000000; ++n) | ||
{ | ||
double x = rand.NextDouble() * Spread; | ||
sum.Add(x); | ||
naive.Add(x); | ||
} | ||
|
||
rand = new Random(2002); | ||
|
||
for(int n = 0; n < 1000000; ++n) | ||
{ | ||
double x = rand.NextDouble() * Spread; | ||
sum.Add(-x); | ||
naive.Add(-x); | ||
} | ||
|
||
helper.WriteLine("RESULT: " + Math.Abs(sum.Value)); | ||
helper.WriteLine("NAIVE: " + Math.Abs(naive.Value)); | ||
|
||
// Since we also test Naive as we want output, test | ||
// LTE instead of LT. | ||
Assert.True(Math.Abs(sum.Value) <= Math.Abs(naive.Value)); | ||
} | ||
|
||
/// <summary> | ||
/// Calling resets fully resets the accumulator. | ||
/// </summary> | ||
[Theory] | ||
[InlineData(SumKind.Pairwise)] | ||
[InlineData(SumKind.Kahan)] | ||
[InlineData(SumKind.Neumaier)] | ||
[InlineData(SumKind.Klein)] | ||
public void Reset_PerformsFullReset(SumKind kind) | ||
{ | ||
var sum = CreateSum(kind); | ||
|
||
sum.Add(1.5); | ||
sum.Clear(); | ||
Assert.Equal(0.0, sum.Value, 10); | ||
|
||
// Restarts | ||
sum.Add(1.5); | ||
Assert.Equal(1.5, sum.Value, 10); | ||
} | ||
|
||
/// <summary> | ||
/// Measure accumulator performance. Result written to helper. NaiveSum also test for comparison. | ||
/// </summary> | ||
[Theory] | ||
[InlineData(SumKind.Pairwise)] | ||
[InlineData(SumKind.Kahan)] | ||
[InlineData(SumKind.Neumaier)] | ||
[InlineData(SumKind.Klein)] | ||
[InlineData(SumKind.Naive)] | ||
public void Add_MeasurePerformance(SumKind kind) | ||
{ | ||
const long TicksPerSecond = 1000 * 1000 * 10; | ||
|
||
var sum = CreateSum(kind); | ||
|
||
// JIT | ||
sum.Add(1); | ||
sum.Clear(); | ||
|
||
double elap; | ||
long count = 0; | ||
var sw = Stopwatch.StartNew(); | ||
|
||
do | ||
{ | ||
for(int n = 0; n < 1000; ++n) | ||
{ | ||
sum.Add(++count); | ||
} | ||
|
||
elap = (double)sw.ElapsedTicks / TicksPerSecond; | ||
|
||
} while (elap < 1.0); | ||
|
||
helper.WriteLine("Count: " + count); | ||
helper.WriteLine("Elapsed: " + elap + " sec"); | ||
|
||
helper.WriteLine("RATE: " + count / elap + " per sec"); | ||
helper.WriteLine("TIME: " + 1E9 * elap / count + " ns"); | ||
} | ||
|
||
private static ICompensatedSum CreateSum(SumKind kind) | ||
{ | ||
switch(kind) | ||
{ | ||
case SumKind.Pairwise: return new PairwiseSum(); | ||
case SumKind.Kahan: return new KahanSum(); | ||
case SumKind.Neumaier: return new NeumaierSum(); | ||
case SumKind.Klein: return new KleinSum(); | ||
default: return new NaiveSum(); | ||
} | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
//--------------------------------------------------------------------------- | ||
// PROJECT : Compensated Accumulators | ||
// COPYRIGHT : Andy Thomas (C) 2019 | ||
// WEB URL : https://kuiper.zone | ||
// LICENSE : GPLv3 | ||
//--------------------------------------------------------------------------- | ||
|
||
namespace CompensatedAccumulators.Test | ||
{ | ||
/// <summary> | ||
/// Naive sum implementation for test comparison only. | ||
/// </summary> | ||
public class NaiveSum : ICompensatedSum | ||
{ | ||
/// <summary> | ||
/// Default constructor. | ||
/// </summary> | ||
public NaiveSum() | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Constructor with initial value. | ||
/// </summary> | ||
public NaiveSum(double x) | ||
{ | ||
Value = x; | ||
} | ||
|
||
/// <summary> | ||
/// Implements <see cref="ICompensatedSum.Value"/>. | ||
/// </summary> | ||
public double Value { get; set; } | ||
|
||
/// <summary> | ||
/// Implements <see cref="ICompensatedSum.Add(double)"/>. | ||
/// </summary> | ||
public void Add(double x) | ||
{ | ||
Value += x; | ||
} | ||
|
||
/// <summary> | ||
/// Implements <see cref="ICompensatedSum.Clear()"/>. | ||
/// </summary> | ||
public void Clear() | ||
{ | ||
Value = 0; | ||
} | ||
|
||
/// <summary> | ||
/// Implements <see cref="ICompensatedSum.Clone()"/>. | ||
/// </summary> | ||
public ICompensatedSum Clone() | ||
{ | ||
return new NaiveSum(Value); | ||
} | ||
|
||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>netstandard2.0</TargetFramework> | ||
</PropertyGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
//--------------------------------------------------------------------------- | ||
// PROJECT : Compensated Accumulators | ||
// COPYRIGHT : Andy Thomas (C) 2019 | ||
// WEB URL : https://kuiper.zone | ||
// LICENSE : GPLv3 | ||
//--------------------------------------------------------------------------- | ||
|
||
namespace CompensatedAccumulators | ||
{ | ||
/// <summary> | ||
/// An interface for a compensated sum algorithm. A "compensated sum" offers reduced numerical | ||
/// error when summing finite precision values in comparison to "naive" summation. | ||
/// </summary> | ||
public interface ICompensatedSum | ||
{ | ||
/// <summary> | ||
/// Get or sets the current summation value. | ||
/// </summary> | ||
double Value { get; set; } | ||
|
||
/// <summary> | ||
/// Adds 'x' to the <see cref="Value"/> sum, using a compensation technique designed to reduce numerical | ||
/// error. | ||
void Add(double x); | ||
|
||
/// <summary> | ||
/// Sets the sum value to zero and resets initial state. | ||
/// </summary> | ||
void Clear(); | ||
|
||
/// <summary> | ||
/// Creates a "deep" copy of the instance with the same value and internal state. Returns the new instance. | ||
/// </summary> | ||
ICompensatedSum Clone(); | ||
} | ||
} |
Oops, something went wrong.