Skip to content
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

New CPU-based Optimizer #1040

Merged
merged 18 commits into from
Mar 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
ef45840
Refactored IOptimizationFunction to support an additional abstract in…
m4rs-mt Jul 30, 2023
1c6b0b3
Added a new CPU-based optimization function interface to model object…
m4rs-mt Jul 30, 2023
1937dc4
Added new ICPUPositionModifier interface together with several implem…
m4rs-mt Aug 2, 2023
999e3a4
Added new CPU-optimized MetaOptimizer based on a modified version of …
m4rs-mt Aug 2, 2023
8fb3219
Added new Scalar component to MetaOptimizer to support scalar (non-ve…
m4rs-mt Aug 7, 2023
1bd68b8
Added new Vectorized component to MetaOptimizer to support vectorized…
m4rs-mt Aug 7, 2023
0c142a1
Added new InitializePlayers component to MetaOptimizer to initialize …
m4rs-mt Aug 7, 2023
f32dd16
Added new Evaluator component to MetaOptimizer to support parallel ev…
m4rs-mt Aug 7, 2023
e7c6702
Added new RawEvaluator component to MetaOptimizer to support highly c…
m4rs-mt Aug 7, 2023
dec7e88
Added new OGAndDG component to MetaOptimizer to compute OG and DG pos…
m4rs-mt Aug 7, 2023
5b373e5
Added new AdjustSOGPlayers component to MetaOptimizer to adjust all p…
m4rs-mt Aug 8, 2023
4956ffb
Added new UpdatePlayers component to MetaOptimizer to update all part…
m4rs-mt Aug 8, 2023
8a37f68
Added new Instance class to MetaOptimizer in order to hide implementa…
m4rs-mt Aug 8, 2023
1a92846
Extended basic OptimizationTests to include publicly available test f…
m4rs-mt Aug 8, 2023
84f13b9
Added CPUMetaOptimizerTests to test convergence of our optimization a…
m4rs-mt Aug 8, 2023
d001530
Renamed meta optimizer to SGO optimizer.
m4rs-mt Feb 1, 2024
fa63875
Adapted copyright information.
m4rs-mt Feb 1, 2024
02ec9f7
Adjusted CPU-based SGO optimizer tests.
m4rs-mt Feb 1, 2024
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
326 changes: 326 additions & 0 deletions Src/ILGPU.Algorithms.Tests.CPU/CPUSGOOptimizerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,326 @@
// ---------------------------------------------------------------------------------------
// ILGPU Algorithms
// Copyright (c) 2023-2024 ILGPU Project
// www.ilgpu.net
//
// File: CPUSGOOptimizerTests.cs
//
// This file is part of ILGPU and is distributed under the University of Illinois Open
// Source License. See LICENSE.txt for details.
// ---------------------------------------------------------------------------------------

using ILGPU.Algorithms.Optimization.CPU;
using ILGPU.Algorithms.Random;
using System;
using System.Threading.Tasks;
using Xunit;

#if NET7_0_OR_GREATER

#pragma warning disable CA1034 // Do not nest types
#pragma warning disable CA1819 // Properties should not return arrays

namespace ILGPU.Algorithms.Tests.CPU
{
/// <summary>
/// Contains tests to verify the functionality of the CPU-specialized
/// <see cref="SGOOptimizer{T,TEvalType}"/> class.
/// </summary>
public class CPUSGOOptimizerTests
{
#region CPU Functions

public interface IOptimizerTestFunction :
OptimizationTests.IPredefineTestFunction,
ICPUOptimizationFunction<float, float>
{ }

public readonly record struct TestBreakFunction(float Goal) :
ICPUOptimizationBreakFunction<float>
{
public bool Break(float evalType, int iteration) =>
Math.Abs(evalType - Goal) < 1e-3f || iteration > 1000;
}

/// <summary>
/// Represents the Himmelblau function:
/// https://en.wikipedia.org/wiki/Test_functions_for_optimization
/// </summary>
public readonly record struct HimmelblauFunction : IOptimizerTestFunction
{
public float Evaluate(ReadOnlySpan<float> position) =>
OptimizationTests.HimmelblauFunction.Evaluate(
position[0],
position[1]);

public bool CurrentIsBetter(float current, float proposed) =>
current < proposed;

public float Result =>
new OptimizationTests.HimmelblauFunction().Result;
public float[] LowerBounds =>
new OptimizationTests.HimmelblauFunction().LowerBounds;
public float[] UpperBounds =>
new OptimizationTests.HimmelblauFunction().UpperBounds;
}

/// <summary>
/// Represents the Easom function:
/// https://en.wikipedia.org/wiki/Test_functions_for_optimization
/// </summary>
public readonly record struct EasomFunction : IOptimizerTestFunction
{
public float Evaluate(ReadOnlySpan<float> position) =>
OptimizationTests.EasomFunction.Evaluate(
position[0],
position[1]);

public bool CurrentIsBetter(float current, float proposed) =>
current < proposed;

public float Result =>
new OptimizationTests.EasomFunction().Result;
public float[] LowerBounds =>
new OptimizationTests.EasomFunction().LowerBounds;
public float[] UpperBounds =>
new OptimizationTests.EasomFunction().UpperBounds;
}
/// <summary>
/// Represents the Shaffer function N4:
/// https://en.wikipedia.org/wiki/Test_functions_for_optimization
/// </summary>
public readonly record struct ShafferFunction4 : IOptimizerTestFunction
{
public float Evaluate(ReadOnlySpan<float> position) =>
OptimizationTests.ShafferFunction4.Evaluate(
position[0],
position[1]);

public bool CurrentIsBetter(float current, float proposed) =>
current < proposed;

public float Result =>
new OptimizationTests.ShafferFunction4().Result;
public float[] LowerBounds =>
new OptimizationTests.ShafferFunction4().LowerBounds;
public float[] UpperBounds =>
new OptimizationTests.ShafferFunction4().UpperBounds;
}

/// <summary>
/// Represents the Rosenbrock function constrained to a disk
/// https://en.wikipedia.org/wiki/Test_functions_for_optimization
/// </summary>
public readonly record struct RosenbrockDisk : IOptimizerTestFunction
{
public float Evaluate(ReadOnlySpan<float> position) =>
OptimizationTests.RosenbrockDisk.Evaluate(
position[0],
position[1]);

public bool CurrentIsBetter(float current, float proposed) =>
current < proposed;

public float Result =>
new OptimizationTests.RosenbrockDisk().Result;
public float[] LowerBounds =>
new OptimizationTests.RosenbrockDisk().LowerBounds;
public float[] UpperBounds =>
new OptimizationTests.RosenbrockDisk().UpperBounds;
}

/// <summary>
/// Represents the Gomez and Levy function:
/// https://en.wikipedia.org/wiki/Test_functions_for_optimization
/// </summary>
public readonly record struct GomezAndLevyFunction : IOptimizerTestFunction
{
public float Evaluate(ReadOnlySpan<float> position) =>
OptimizationTests.GomezAndLevyFunction.Evaluate(
position[0],
position[1]);

public bool CurrentIsBetter(float current, float proposed) =>
current < proposed;

public float Result =>
new OptimizationTests.GomezAndLevyFunction().Result;
public float[] LowerBounds =>
new OptimizationTests.GomezAndLevyFunction().LowerBounds;
public float[] UpperBounds =>
new OptimizationTests.GomezAndLevyFunction().UpperBounds;
}

#endregion

#region MemberData

public static TheoryData<object, int, float, float, float> TestData =>
new TheoryData<object, int, float, float, float>
{
{ new HimmelblauFunction(), 8192, 0.5f, 0.5f, 0.5f },
{ new EasomFunction(), 81920, 0.5f, 0.5f, 0.5f },
{ new ShafferFunction4(), 8192, 0.5f, 0.5f, 0.5f },
{ new RosenbrockDisk(), 8192, 0.5f, 0.5f, 0.5f },
{ new GomezAndLevyFunction(), 81920, 0.5f, 0.5f, 0.5f },
};

#endregion

[Theory]
[MemberData(nameof(TestData))]
public void MetaOptimizationScalar<TObjective>(
TObjective objective,
int numParticles,
float stepSizeDefensive,
float stepSizeOffensive,
float stepSizeOffensiveSOG)
where TObjective : struct, IOptimizerTestFunction
{
int numDimensions = objective.LowerBounds.Length;
var random = new System.Random(13377331);

using var optimizer = SGOOptimizer.CreateScalar<
float,
float,
RandomRanges.RandomRangeFloatProvider<XorShift64Star>>(
random,
numParticles,
numDimensions,
maxNumParallelThreads: 1);

optimizer.LowerBounds = objective.LowerBounds;
optimizer.UpperBounds = objective.UpperBounds;

optimizer.DefensiveStepSize = stepSizeDefensive;
optimizer.OffensiveStepSize = stepSizeOffensive;
optimizer.OffensiveSOGStepSize = stepSizeOffensiveSOG;

var breakFunction = new TestBreakFunction(objective.Result);
var result = optimizer.Optimize(
objective,
breakFunction,
float.MaxValue);

// The actually achievable result is 1e-6. However, as the RNG gives us
// non-deterministic results due to parallel processing, we limit ourselves
// to 1e-3 to make sure that the result lies roughly in the same ballpark
// what we were expecting
Assert.True(Math.Abs(result.Result - objective.Result) < 1e-3f);
}

[Theory]
[MemberData(nameof(TestData))]
public void MetaOptimizationVectorized<TObjective>(
TObjective objective,
int numParticles,
float stepSizeDefensive,
float stepSizeOffensive,
float stepSizeOffensiveSOG)
where TObjective : struct, IOptimizerTestFunction
{
int numDimensions = objective.LowerBounds.Length;
var random = new System.Random(13377331);

using var optimizer = SGOOptimizer.CreateVectorized<
float,
float,
RandomRanges.RandomRangeFloatProvider<XorShift64Star>>(
random,
numParticles,
numDimensions,
maxNumParallelThreads: 1);

optimizer.LowerBounds = objective.LowerBounds;
optimizer.UpperBounds = objective.UpperBounds;

optimizer.DefensiveStepSize = stepSizeDefensive;
optimizer.OffensiveStepSize = stepSizeOffensive;
optimizer.OffensiveSOGStepSize = stepSizeOffensiveSOG;

var breakFunction = new TestBreakFunction(objective.Result);
var result = optimizer.Optimize(
objective,
breakFunction,
float.MaxValue);

// The actually achievable result is 1e-6. However, as the RNG gives us
// non-deterministic results due to parallel processing, we limit ourselves
// to 1e-3 to make sure that the result lies roughly in the same ballpark
// what we were expecting
Assert.True(
Math.Abs(result.Result - objective.Result) < 1e-3f,
$"Expected {objective.Result}, but found {result.Result}");
}

[Theory]
[MemberData(nameof(TestData))]
public void MetaOptimizationScalarRaw<TObjective>(
TObjective objective,
int numParticles,
float stepSizeDefensive,
float stepSizeOffensive,
float stepSizeOffensiveSOG)
where TObjective : struct, IOptimizerTestFunction
{
int numDimensions = objective.LowerBounds.Length;
var random = new System.Random(13377331);

using var optimizer = SGOOptimizer.CreateScalar<
float,
float,
RandomRanges.RandomRangeFloatProvider<XorShift64Star>>(
random,
numParticles,
numDimensions,
maxNumParallelThreads: 1);

optimizer.LowerBounds = objective.LowerBounds;
optimizer.UpperBounds = objective.UpperBounds;

optimizer.DefensiveStepSize = stepSizeDefensive;
optimizer.OffensiveStepSize = stepSizeOffensive;
optimizer.OffensiveSOGStepSize = stepSizeOffensiveSOG;

void EvaluatePosition(
Memory<float> allPositions,
Memory<float> evaluations,
int _,
int numPaddedDimensions,
int __,
Stride2D.DenseY positionStride,
ParallelOptions options)
{
for (int i = 0; i < numParticles; ++i)
{
int offset = positionStride.ComputeElementIndex((i, 0));
int endOffset = positionStride.ComputeElementIndex(
(i, numPaddedDimensions));
var position = allPositions.Slice(offset, endOffset - offset);
var result = objective.Evaluate(position.Span);
evaluations.Span[i] = result;
}
}

var breakFunction = new TestBreakFunction(objective.Result);
var result = optimizer.OptimizeRaw(
EvaluatePosition,
breakFunction.Break,
objective.CurrentIsBetter,
float.MaxValue);

// The actually achievable result is 1e-6. However, as the RNG gives us
// non-deterministic results due to parallel processing, we limit ourselves
// to 1e-3 to make sure that the result lies roughly in the same ballpark
// what we were expecting
Assert.True(
Math.Abs(result.Result - objective.Result) < 1e-3f,
$"Expected {objective.Result}, but found {result.Result}");
}
}
}

#pragma warning restore CA1819
#pragma warning restore CA1034

#endif
Loading
Loading