diff --git a/Src/ILGPU.Algorithms.Tests.CPU/CPUMetaOptimizerTests.cs b/Src/ILGPU.Algorithms.Tests.CPU/CPUMetaOptimizerTests.cs
new file mode 100644
index 0000000000..b19765fc4a
--- /dev/null
+++ b/Src/ILGPU.Algorithms.Tests.CPU/CPUMetaOptimizerTests.cs
@@ -0,0 +1,330 @@
+// ---------------------------------------------------------------------------------------
+// ILGPU Algorithms
+// Copyright (c) 2023 ILGPU Project
+// www.ilgpu.net
+//
+// File: CPUMetaOptimizerTests.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
+{
+ ///
+ /// Contains tests to verify the functionality of the CPU-specialized
+ /// class.
+ ///
+ public class CPUMetaOptimizerTests
+ {
+ #region CPU Functions
+
+ public interface IOptimizerTestFunction :
+ OptimizationTests.IPredefineTestFunction,
+ ICPUOptimizationFunction
+ { }
+
+ public readonly record struct TestBreakFunction(float Goal) :
+ ICPUOptimizationBreakFunction
+ {
+ public bool Break(float evalType, int iteration) =>
+ Math.Abs(evalType - Goal) < 1e-4f || iteration > 500;
+ }
+
+ ///
+ /// Represents the Himmelblau function:
+ /// https://en.wikipedia.org/wiki/Test_functions_for_optimization
+ ///
+ public readonly record struct HimmelblauFunction : IOptimizerTestFunction
+ {
+ public float Evaluate(ReadOnlySpan 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;
+ }
+
+ ///
+ /// Represents the Easom function:
+ /// https://en.wikipedia.org/wiki/Test_functions_for_optimization
+ ///
+ public readonly record struct EasomFunction : IOptimizerTestFunction
+ {
+ public float Evaluate(ReadOnlySpan 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;
+ }
+ ///
+ /// Represents the Shaffer function N4:
+ /// https://en.wikipedia.org/wiki/Test_functions_for_optimization
+ ///
+ public readonly record struct ShafferFunction4 : IOptimizerTestFunction
+ {
+ public float Evaluate(ReadOnlySpan 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;
+ }
+
+ ///
+ /// Represents the Rosenbrock function constrained to a disk
+ /// https://en.wikipedia.org/wiki/Test_functions_for_optimization
+ ///
+ public readonly record struct RosenbrockDisk : IOptimizerTestFunction
+ {
+ public float Evaluate(ReadOnlySpan 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;
+ }
+
+ ///
+ /// Represents the Gomez and Levy function:
+ /// https://en.wikipedia.org/wiki/Test_functions_for_optimization
+ ///
+ public readonly record struct GomezAndLevyFunction : IOptimizerTestFunction
+ {
+ public float Evaluate(ReadOnlySpan 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,
+ object,
+ object,
+ object,
+ object> TestData =>
+ new TheoryData<
+ object,
+ object,
+ object,
+ object,
+ object>
+ {
+ { 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 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 = MetaOptimizer.CreateScalar<
+ float,
+ float,
+ RandomRanges.RandomRangeFloatProvider>(
+ random,
+ numParticles,
+ numDimensions);
+
+ 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-2 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-2f);
+ }
+
+ [Theory]
+ [MemberData(nameof(TestData))]
+ public void MetaOptimizationVectorized(
+ 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 = MetaOptimizer.CreateVectorized<
+ float,
+ float,
+ RandomRanges.RandomRangeFloatProvider>(
+ random,
+ numParticles,
+ numDimensions);
+
+ 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-2 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-2f);
+ }
+
+ [Theory]
+ [MemberData(nameof(TestData))]
+ public void MetaOptimizationScalarRaw(
+ 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 = MetaOptimizer.CreateScalar<
+ float,
+ float,
+ RandomRanges.RandomRangeFloatProvider>(
+ random,
+ numParticles,
+ numDimensions);
+
+ optimizer.LowerBounds = objective.LowerBounds;
+ optimizer.UpperBounds = objective.UpperBounds;
+
+ optimizer.DefensiveStepSize = stepSizeDefensive;
+ optimizer.OffensiveStepSize = stepSizeOffensive;
+ optimizer.OffensiveSOGStepSize = stepSizeOffensiveSOG;
+
+ void EvaluatePosition(
+ Memory allPositions,
+ Memory evaluations,
+ int _,
+ int numPaddedDimensions,
+ int __,
+ Stride2D.DenseY positionStride,
+ ParallelOptions options)
+ {
+ for (int i = 0; i < numParticles; ++i)
+ // Parallel.For(0, numParticles, options, 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-2 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-2f);
+ }
+ }
+}
+
+#pragma warning restore CA1819
+#pragma warning restore CA1034
+
+#endif
diff --git a/Src/ILGPU.Algorithms.Tests/OptimizationTests.cs b/Src/ILGPU.Algorithms.Tests/OptimizationTests.cs
index 7ab8acbbe3..1d70ad9292 100644
--- a/Src/ILGPU.Algorithms.Tests/OptimizationTests.cs
+++ b/Src/ILGPU.Algorithms.Tests/OptimizationTests.cs
@@ -20,6 +20,7 @@
using ILGPU.Tests;
using System.Linq;
using System.Numerics;
+using System.Runtime.CompilerServices;
using Xunit;
using Xunit.Abstractions;
@@ -34,11 +35,18 @@ public abstract partial class OptimizationTests : TestBase
protected OptimizationTests(ITestOutputHelper output, TestContext testContext)
: base(output, testContext)
{ }
-
+
#if NET7_0_OR_GREATER
-
+
#region Objectives
-
+
+ public interface IPredefineTestFunction
+ {
+ float Result { get; }
+ float[] LowerBounds { get; }
+ float[] UpperBounds { get; }
+ }
+
public readonly record struct DistanceF32x2(float Constant) :
IOptimizationFunction
{
@@ -60,7 +68,271 @@ public float Evaluate(
public bool CurrentIsBetter(float current, float proposed) =>
current <= proposed;
}
-
+
+ ///
+ /// Represents the Himmelblau function:
+ /// https://en.wikipedia.org/wiki/Test_functions_for_optimization
+ ///
+ public readonly record struct HimmelblauFunction :
+ IOptimizationFunction,
+ IPredefineTestFunction
+ {
+ private static readonly float[] GlobalLowerBounds = new float[]
+ {
+ -5.0f, -5.0f
+ };
+
+ private static readonly float[] GlobalUpperBounds = new float[]
+ {
+ 5.0f, 5.0f
+ };
+
+ ///
+ /// The optimal result.
+ ///
+ public const float GlobalResult = 0.0f;
+
+ ///
+ /// Evaluates the Himmelblau function.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Evaluate(float x, float y)
+ {
+ float first = (x * x + y - 11);
+ float second = (x + y * y - 7);
+ return first * first + second * second;
+ }
+
+ public float Result => GlobalResult;
+ public float[] LowerBounds => GlobalLowerBounds;
+ public float[] UpperBounds => GlobalUpperBounds;
+
+ public float Evaluate(
+ LongIndex1D index,
+ Index1D dimension,
+ SingleVectorView positionView)
+ {
+ var first = positionView[0];
+ return Evaluate(first.X, first.Y);
+ }
+
+ public bool CurrentIsBetter(float current, float proposed) =>
+ current < proposed;
+ }
+
+ ///
+ /// Represents the Easom function:
+ /// https://en.wikipedia.org/wiki/Test_functions_for_optimization
+ ///
+ public readonly record struct EasomFunction :
+ IOptimizationFunction,
+ IPredefineTestFunction
+ {
+ private static readonly float[] GlobalLowerBounds = new float[]
+ {
+ -100.0f, -100.0f
+ };
+
+ private static readonly float[] GlobalUpperBounds = new float[]
+ {
+ 100.0f, 100.0f
+ };
+
+ ///
+ /// The optimal result.
+ ///
+ public const float GlobalResult = -1.0f;
+
+ public float Result => GlobalResult;
+ public float[] LowerBounds => GlobalLowerBounds;
+ public float[] UpperBounds => GlobalUpperBounds;
+
+ ///
+ /// Evaluates the Easom function.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Evaluate(float x, float y)
+ {
+ float xPart = x - XMath.PI;
+ float yPart = y - XMath.PI;
+ return -XMath.Cos(x) * XMath.Cos(y) *
+ XMath.Exp(-(xPart * xPart + yPart * yPart));
+ }
+ public float Evaluate(
+ LongIndex1D index,
+ Index1D dimension,
+ SingleVectorView positionView)
+ {
+ var first = positionView[0];
+ return Evaluate(first.X, first.Y);
+ }
+
+ public bool CurrentIsBetter(float current, float proposed) =>
+ current < proposed;
+ }
+
+ ///
+ /// Represents the Shaffer function N4:
+ /// https://en.wikipedia.org/wiki/Test_functions_for_optimization
+ ///
+ public readonly record struct ShafferFunction4 :
+ IOptimizationFunction,
+ IPredefineTestFunction
+ {
+ private static readonly float[] GlobalLowerBounds = new float[]
+ {
+ -100.0f, -100.0f
+ };
+
+ private static readonly float[] GlobalUpperBounds = new float[]
+ {
+ 100.0f, 100.0f
+ };
+
+ ///
+ /// The optimal result.
+ ///
+ public const float GlobalResult = 0.292579f;
+
+ public float Result => GlobalResult;
+ public float[] LowerBounds => GlobalLowerBounds;
+ public float[] UpperBounds => GlobalUpperBounds;
+
+ ///
+ /// Evaluates the Shaffer function.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Evaluate(float x, float y)
+ {
+ float cos = XMath.Cos(XMath.Sin(XMath.Abs(x * x - y * y)));
+ float nominator = cos * cos - 0.5f;
+ float denominator = 1 + 0.001f * (x * x + y * y);
+ return 0.5f + nominator / (denominator * denominator);
+ }
+ public float Evaluate(
+ LongIndex1D index,
+ Index1D dimension,
+ SingleVectorView positionView)
+ {
+ var first = positionView[0];
+ return Evaluate(first.X, first.Y);
+ }
+
+ public bool CurrentIsBetter(float current, float proposed) =>
+ current < proposed;
+ }
+
+ ///
+ /// Represents the Rosenbrock function constrained to a disk
+ /// https://en.wikipedia.org/wiki/Test_functions_for_optimization
+ ///
+ public readonly record struct RosenbrockDisk :
+ IOptimizationFunction,
+ IPredefineTestFunction
+ {
+ private static readonly float[] GlobalLowerBounds = new float[]
+ {
+ -1.5f, -1.5f
+ };
+
+ private static readonly float[] GlobalUpperBounds = new float[]
+ {
+ 1.5f, 1.5f
+ };
+
+ ///
+ /// The optimal result.
+ ///
+ public const float GlobalResult = 0.0f;
+
+ public float Result => GlobalResult;
+ public float[] LowerBounds => GlobalLowerBounds;
+ public float[] UpperBounds => GlobalUpperBounds;
+
+ ///
+ /// Evaluates the constrained Rosenbrock function.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Evaluate(float x, float y)
+ {
+ float xMin = 1.0f - x;
+ float x2 = x * x;
+ float result = xMin * xMin + 100.0f * (y - x2) * (y - x2);
+ if (x * x + y * y <= 2.0f)
+ return result;
+ return float.MaxValue;
+ }
+
+ public float Evaluate(
+ LongIndex1D index,
+ Index1D dimension,
+ SingleVectorView positionView)
+ {
+ var first = positionView[0];
+ return Evaluate(first.X, first.Y);
+ }
+
+ public bool CurrentIsBetter(float current, float proposed) =>
+ current < proposed;
+ }
+
+ ///
+ /// Represents the Gomez and Levy function:
+ /// https://en.wikipedia.org/wiki/Test_functions_for_optimization
+ ///
+ public readonly record struct GomezAndLevyFunction :
+ IOptimizationFunction,
+ IPredefineTestFunction
+ {
+ private static readonly float[] GlobalLowerBounds = new float[]
+ {
+ -1.0f, -1.0f
+ };
+
+ private static readonly float[] GlobalUpperBounds = new float[]
+ {
+ 0.75f, 1.0f
+ };
+
+ ///
+ /// The optimal result.
+ ///
+ public const float GlobalResult = -1.031628453f;
+
+ public float Result => GlobalResult;
+ public float[] LowerBounds => GlobalLowerBounds;
+ public float[] UpperBounds => GlobalUpperBounds;
+
+ ///
+ /// Evaluates the constrained Gomez and Levy function.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Evaluate(float x, float y)
+ {
+ float x2 = x * x;
+ float x4 = x2 * x2;
+ float y2 = y * y;
+ float y4 = y2 * y2;
+ float result = 4.0f * x2 + 2.1f * x4 + 1.0f / 3.0f * x4 * x2 +
+ x * y - 4.0f * y2 + 4.0f * y4;
+ float sin = XMath.Sin(2.0f * XMath.PI * y);
+ float conditionValue = -XMath.Sin(4.0f * XMath.PI * x) + 2.0f * sin * sin;
+ return conditionValue < 1.5f ? result : float.MaxValue;
+ }
+
+ public float Evaluate(
+ LongIndex1D index,
+ Index1D dimension,
+ SingleVectorView positionView)
+ {
+ var first = positionView[0];
+ return Evaluate(first.X, first.Y);
+ }
+
+ public bool CurrentIsBetter(float current, float proposed) =>
+ current < proposed;
+ }
+
#endregion
#region MemberData
@@ -129,7 +401,7 @@ public static TheoryData<
#endregion
- [SkippableTheory()]
+ [SkippableTheory]
[MemberData(nameof(TestData))]
public void ParticleSwarmOptimization<
TFunc,
@@ -157,7 +429,7 @@ public void ParticleSwarmOptimization<
Skip.If(
Accelerator.AcceleratorType == AcceleratorType.CPU &&
optimizerConfig.NumIterations * optimizerConfig.NumParticles > 2048);
-
+
const int Seed = 24404699;
using var pso = new PSO<
TNumericType,
@@ -185,7 +457,7 @@ public void ParticleSwarmOptimization<
best,
optimizerConfig.NumIterations);
stream.Synchronize();
-
+
// Check result
Assert.True(
result.Result - delta <= expected,
diff --git a/Src/ILGPU.Algorithms/Optimization/CPU/MetaOptimizer.AdjustSOGPlayers.cs b/Src/ILGPU.Algorithms/Optimization/CPU/MetaOptimizer.AdjustSOGPlayers.cs
new file mode 100644
index 0000000000..80133ed814
--- /dev/null
+++ b/Src/ILGPU.Algorithms/Optimization/CPU/MetaOptimizer.AdjustSOGPlayers.cs
@@ -0,0 +1,162 @@
+// ---------------------------------------------------------------------------------------
+// ILGPU Algorithms
+// Copyright (c) 2023 ILGPU Project
+// www.ilgpu.net
+//
+// File: MetaOptimizer.AdjustSOGPlayers.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.Random;
+using ILGPU.Util;
+using System;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+
+#if NET7_0_OR_GREATER
+
+namespace ILGPU.Algorithms.Optimization.CPU
+{
+ partial class MetaOptimizer
+ {
+ ///
+ /// A parallel processing state to adjust SOG-based information for all winning
+ /// offensive players from the current solver iteration.
+ ///
+ /// The random range provider type.
+ private class AdjustSOGPlayersState : InitializePlayersState
+ where TRandom : struct, IRandomRangeProvider
+ {
+ ///
+ /// Creates a new SOG players state.
+ ///
+ /// The random to use.
+ public AdjustSOGPlayersState(TRandom random)
+ : base(random)
+ { }
+ }
+
+ ///
+ /// Updates all players according to defensive and offensive winners.
+ ///
+ /// The processor type being used.
+ /// The processing type.
+ /// The random range provider type.
+ private sealed class AdjustSOGPlayers :
+ ParallelProcessingCache<
+ AdjustSOGPlayersState,
+ AdjustSOGPlayers>,
+ IParallelProcessingBody>
+ where TProcessor : struct, IProcessor
+ where TType : unmanaged
+ where TRandom : struct, IRandomRangeProvider
+ {
+ private readonly MetaOptimizer parent;
+ private readonly Func, TRandom> getRandom;
+
+ ///
+ /// Creates a new player update instance.
+ ///
+ /// The parent optimizer instance.
+ /// A function creating a new RNG instance.
+ public AdjustSOGPlayers(
+ MetaOptimizer instance,
+ Func, TRandom> createRandom)
+ {
+ parent = instance;
+ getRandom = createRandom;
+ }
+
+ ///
+ /// Gets or sets the best known position vector.
+ ///
+ public ReadOnlyMemory BestPosition { get; set; }
+
+ ///
+ /// Returns the current instance.
+ ///
+ protected override AdjustSOGPlayers
+ CreateBody() => this;
+
+ ///
+ /// Creates an intermediate accumulation state.
+ ///
+ protected override AdjustSOGPlayersState
+ CreateIntermediate() => new(getRandom(parent));
+
+ ///
+ /// Does not perform any operation.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Initialize() { }
+
+ ///
+ /// Adjusts all SOG-player positions from the current iteration while taking
+ /// SDG and best positions into account.
+ ///
+ [MethodImpl(
+ MethodImplOptions.AggressiveInlining |
+ MethodImplOptions.AggressiveOptimization)]
+ public void Process(
+ int index,
+ ParallelLoopState? _,
+ AdjustSOGPlayersState state)
+ {
+ // Load sog index and associated position vector
+ var offensiveIndex = parent.sogList[index];
+ var offensive = parent
+ .GetNextPosition(offensiveIndex)
+ .CastUnsafe();
+
+ // Get two fresh random numbers
+ var r1 = state.Next();
+ var r2 = state.Next();
+
+ // Get lower and upper bounds
+ var lowerBounds = parent.lowerBounds.AsSpan().CastUnsafe();
+ var upperBounds = parent.upperBounds.AsSpan().CastUnsafe();
+
+ // Get best position and SDG
+ var bestPosition = BestPosition.Span.CastUnsafe();
+ var sdg = parent.sdg.AsSpan().CastUnsafe();
+
+ // Create new processor for this step
+ var processor = TProcessor.New();
+ for (int i = 0; i < offensive.Length; ++i)
+ {
+ // Get local offensive item ref
+ ref var offensiveVec = ref offensive.GetItemRef(i);
+
+ // Compute new position and set new vector of offensive SOG player
+ var xOffNew3 = processor.DetermineNewPosition(
+ offensiveVec,
+ bestPosition.GetItemRef(i),
+ sdg.GetItemRef(1),
+ r1,
+ r2,
+ parent.OffensiveSOGStepSize);
+
+ // Clamp new defensive position and store result
+ var clamped = processor.Clamp(
+ lowerBounds.GetItemRef(i),
+ upperBounds.GetItemRef(i),
+ xOffNew3);
+ offensiveVec = clamped;
+ }
+ }
+
+ ///
+ /// Does not perform any operation.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Finalize(
+ ReadOnlySpan> intermediateStates)
+ { }
+
+ }
+ }
+}
+
+#endif
diff --git a/Src/ILGPU.Algorithms/Optimization/CPU/MetaOptimizer.Evaluator.cs b/Src/ILGPU.Algorithms/Optimization/CPU/MetaOptimizer.Evaluator.cs
new file mode 100644
index 0000000000..18675bb69a
--- /dev/null
+++ b/Src/ILGPU.Algorithms/Optimization/CPU/MetaOptimizer.Evaluator.cs
@@ -0,0 +1,390 @@
+// ---------------------------------------------------------------------------------------
+// ILGPU Algorithms
+// Copyright (c) 2023 ILGPU Project
+// www.ilgpu.net
+//
+// File: MetaOptimizer.Evaluator.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.Util;
+using System;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+
+#if NET7_0_OR_GREATER
+
+namespace ILGPU.Algorithms.Optimization.CPU
+{
+ partial class MetaOptimizer
+ {
+ ///
+ /// A parallel evaluation state storing temporary best result and position
+ /// information per thread.
+ ///
+ ///
+ /// The optimization function type to use.
+ ///
+ ///
+ /// The intermediate state type for each optimization processing thread.
+ ///
+ private sealed class EvaluatorState : DisposeBase
+ where TFunction :
+ IBaseOptimizationFunction,
+ IParallelCache
+ where TIntermediate : class
+ {
+ private TFunction function;
+ private TEvalType bestKnownResult;
+ private readonly T[] bestPosition;
+
+ ///
+ /// Creates a new evaluation state.
+ ///
+ ///
+ /// The optimization function to use.
+ ///
+ ///
+ /// The number of padded dimensions taking vector lengths into account.
+ ///
+ public EvaluatorState(TFunction optimizationFunction, int numPaddedDimensions)
+ {
+ function = optimizationFunction;
+ bestPosition = new T[numPaddedDimensions];
+ Intermediate = function.CreateIntermediate();
+ }
+
+ ///
+ /// Returns the intermediate state of this instance.
+ ///
+ public TIntermediate Intermediate { get; }
+
+ ///
+ /// Resets the best known result to the given result value.
+ ///
+ /// The best result value to store.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Reset(TEvalType bestResult)
+ {
+ bestKnownResult = bestResult;
+ Array.Clear(bestPosition);
+ }
+
+ ///
+ /// Merges the given result with the internally stored one. If the passed
+ /// result value is considered better than the stored one, the passed position
+ /// vector will be copied to the internally stored best position.
+ ///
+ /// The result value to merge.
+ ///
+ /// The position that led to the given result value.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void MergeWith(TEvalType result, ReadOnlySpan position)
+ {
+ if (function.CurrentIsBetter(bestKnownResult, result))
+ return;
+
+ bestKnownResult = result;
+ position.CopyTo(bestPosition);
+ }
+
+ ///
+ /// Aggregates currently available information into the given result field.
+ /// If the objective function determines that the referenced result is worse
+ /// than the one stored internally, the referenced result value is updated
+ /// and the internally stored position is copied to the given result position
+ /// span.
+ ///
+ ///
+ /// A reference to the currently known best result.
+ ///
+ ///
+ /// A span pointing to the globally found best result position vector which
+ /// will be updated if the internally stored result value is considered
+ /// better than the referenced one.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void AggregateInto(ref TEvalType result, Span resultPosition)
+ {
+ if (function.CurrentIsBetter(result, bestKnownResult))
+ return;
+
+ result = bestKnownResult;
+ bestPosition.CopyTo(resultPosition);
+ }
+
+ ///
+ /// Disposes the intermediate state if required.
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ if (Intermediate is IDisposable disposable)
+ disposable.Dispose();
+
+ base.Dispose(disposing);
+ }
+ }
+
+ ///
+ /// Represents a result manager storing best result values.
+ ///
+ private struct ResultManager
+ {
+ private readonly T[] bestPosition;
+ private TEvalType bestResult;
+
+ ///
+ /// Creates a new result manager.
+ ///
+ /// The parent optimizer.
+ ///
+ /// The best known result provided by the user.
+ ///
+ ///
+ /// The best known position provided by the user.
+ ///
+ public ResultManager(
+ MetaOptimizer optimizer,
+ in TEvalType bestUserKnownResult,
+ ReadOnlyMemory? bestKnownPosition)
+ {
+ // Validate our best known position vector
+ if (bestKnownPosition.HasValue &&
+ bestKnownPosition.Value.Length != NumDimensions)
+ {
+ throw new ArgumentOutOfRangeException(nameof(bestKnownPosition));
+ }
+
+ bestPosition = new T[optimizer.NumPaddedDimensions];
+ bestResult = BestInitialResult = bestUserKnownResult;
+
+ NumDimensions = optimizer.NumDimensions;
+
+ // Check for a valid best known result
+ if (!bestKnownPosition.HasValue)
+ {
+ // Reset best known position
+ for (int i = 0; i < bestPosition.Length; ++i)
+ bestPosition[i] = T.Zero;
+ }
+ else
+ {
+ // Copy known position
+ bestKnownPosition.Value.CopyTo(bestPosition);
+
+ // Reset remaining parts
+ for (int i = NumDimensions; i < bestPosition.Length; ++i)
+ bestPosition[i] = T.Zero;
+ }
+ }
+
+ ///
+ /// Returns the number of dimensions.
+ ///
+ public int NumDimensions { get; }
+
+ ///
+ /// Returns the best found result.
+ ///
+ public readonly TEvalType BestResult => bestResult;
+
+ ///
+ /// Returns the best known initial result.
+ ///
+ public TEvalType BestInitialResult { get; }
+
+ ///
+ /// Returns the best found position (not padded).
+ ///
+ public readonly Memory BestPosition =>
+ new(bestPosition, 0, NumDimensions);
+
+ ///
+ /// Returns the best found internal position (padded).
+ ///
+ public readonly ReadOnlyMemory BestInternalPosition => bestPosition;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Aggregate(
+ EvaluatorState state)
+ where TFunction :
+ IBaseOptimizationFunction,
+ IParallelCache
+ where TIntermediate : class =>
+ state.AggregateInto(ref bestResult, bestPosition);
+ }
+
+ ///
+ /// Represents an abstract evaluator.
+ ///
+ private interface IEvaluator : IDisposable
+ {
+ ///
+ /// Returns the underlying result manager.
+ ///
+ ResultManager ResultManager { get; }
+
+ ///
+ /// Evaluates all players.
+ ///
+ /// The parallel processing options.
+ void EvaluatePlayers(ParallelOptions options);
+ }
+
+ ///
+ /// Represents an objective function evaluator that applies the user-defined
+ /// function to each player position in every step.
+ ///
+ /// The objective function type.
+ ///
+ /// The intermediate state type for each evaluator thread.
+ ///
+ /// The position modifier type.
+ private sealed class Evaluator :
+ ParallelProcessingCache<
+ EvaluatorState,
+ Evaluator>,
+ IParallelProcessingBody>,
+ IEvaluator
+ where TFunction : ICPUOptimizationFunction
+ where TIntermediate : class
+ where TModifier : ICPUPositionModifier
+ {
+ private readonly MetaOptimizer parent;
+ private TFunction function;
+ private TModifier modifier;
+
+ private readonly int numPaddedDimensions;
+ private ResultManager resultManager;
+
+ ///
+ /// Creates a new evaluator.
+ ///
+ /// The parent optimizer.
+ /// The optimization function.
+ /// The position modifier.
+ ///
+ /// The best known result provided by the user.
+ ///
+ ///
+ /// The best known position provided by the user.
+ ///
+ public Evaluator(
+ MetaOptimizer optimizer,
+ in TFunction optimizationFunction,
+ in TModifier positionModifier,
+ in TEvalType bestUserKnownResult,
+ ReadOnlyMemory? bestKnownPosition)
+ {
+ parent = optimizer;
+ function = optimizationFunction;
+ modifier = positionModifier;
+
+ numPaddedDimensions = optimizer.NumPaddedDimensions;
+ resultManager = new(optimizer, bestUserKnownResult, bestKnownPosition);
+ }
+
+ ///
+ /// Returns the result manager.
+ ///
+ public ResultManager ResultManager => resultManager;
+
+ ///
+ /// Returns the current instance.
+ ///
+ protected override Evaluator<
+ TFunction,
+ TIntermediate,
+ TModifier> CreateBody() => this;
+
+ ///
+ /// Creates an intermediate temporary state.
+ ///
+ protected override EvaluatorState
+ CreateIntermediate() =>
+ new(function, numPaddedDimensions);
+
+ ///
+ /// Resets the given intermediate state by using the best known result
+ /// provided by the user.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveOptimization)]
+ protected override void InitializeIntermediate(
+ EvaluatorState intermediateState)
+ {
+ intermediateState.Reset(resultManager.BestInitialResult);
+ function.InitializeIntermediate(intermediateState.Intermediate);
+ }
+
+ ///
+ /// Does not perform any operation.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Initialize() { }
+
+ ///
+ /// Evaluates all players and accumulates intermediate results.
+ ///
+ [MethodImpl(
+ MethodImplOptions.AggressiveInlining |
+ MethodImplOptions.AggressiveOptimization)]
+ public void Process(
+ int index,
+ ParallelLoopState? loopState,
+ EvaluatorState intermediateState)
+ {
+ // Get the source position and evaluate
+ var positionMemory = parent.GetPositionMemory(index);
+
+ // Adjust position
+ modifier.AdjustPosition(
+ index,
+ positionMemory,
+ resultManager.NumDimensions,
+ numPaddedDimensions);
+
+ // Convert into a span and evaluate
+ var position = positionMemory.Span;
+ var result = function.Evaluate(position, intermediateState.Intermediate);
+
+ // Store evaluation result
+ parent.evaluations[index] = result;
+
+ // Merge intermediate state
+ intermediateState.MergeWith(result, position);
+ }
+
+ ///
+ /// Aggregates all temporarily found best results into a globally shared
+ /// state to find the best solution taking all solutions into account.
+ ///
+ [MethodImpl(
+ MethodImplOptions.AggressiveInlining |
+ MethodImplOptions.AggressiveOptimization)]
+ public void Finalize(
+ ReadOnlySpan> intermediateStates)
+ {
+ // Iterate over all states and aggregate all information
+ foreach (var state in intermediateStates)
+ {
+ function.FinishProcessing(state.Intermediate);
+ resultManager.Aggregate(state);
+ }
+ }
+
+ ///
+ /// Evaluates all players in parallel using the underlying modifier, eval
+ /// function, and comparison functions.
+ ///
+ /// The parallel processing options.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void EvaluatePlayers(ParallelOptions options) =>
+ ParallelFor(0, parent.NumPlayers, options);
+ }
+ }
+}
+
+#endif
diff --git a/Src/ILGPU.Algorithms/Optimization/CPU/MetaOptimizer.InitializePlayers.cs b/Src/ILGPU.Algorithms/Optimization/CPU/MetaOptimizer.InitializePlayers.cs
new file mode 100644
index 0000000000..f83463a158
--- /dev/null
+++ b/Src/ILGPU.Algorithms/Optimization/CPU/MetaOptimizer.InitializePlayers.cs
@@ -0,0 +1,149 @@
+// ---------------------------------------------------------------------------------------
+// ILGPU Algorithms
+// Copyright (c) 2023 ILGPU Project
+// www.ilgpu.net
+//
+// File: MetaOptimizer.InitializePlayers.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.Random;
+using ILGPU.Util;
+using System;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+
+#if NET7_0_OR_GREATER
+
+namespace ILGPU.Algorithms.Optimization.CPU
+{
+ partial class MetaOptimizer
+ {
+ ///
+ /// A parallel processing state for player initialization based on random number
+ /// generators used during placement of players.
+ ///
+ /// A random provider type.
+ /// A processing type.
+ private class InitializePlayersState
+ where TType : unmanaged
+ where TRandom : struct, IRandomRangeProvider
+ {
+ private TRandom randomProvider;
+
+ ///
+ /// Creates a new initialization state.
+ ///
+ /// The random provider to use.
+ public InitializePlayersState(TRandom random)
+ {
+ randomProvider = random;
+ }
+
+ ///
+ /// Draws a random number using the given CPU-based RNG provider.
+ ///
+ /// The drawn random number.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public TType Next() => randomProvider.Next();
+ }
+
+ ///
+ /// A player position initializer.
+ ///
+ /// The processor type.
+ /// The processor element type.
+ /// The random provider type.
+ private sealed class InitializePlayers :
+ ParallelProcessingCache<
+ InitializePlayersState,
+ InitializePlayers>,
+ IParallelProcessingBody>
+ where TProcessor : struct, IProcessor
+ where TType : unmanaged
+ where TRandom : struct, IRandomRangeProvider
+ {
+ private readonly MetaOptimizer parent;
+ private readonly Func, TRandom> getRandom;
+
+ ///
+ /// Creates a new player initializer.
+ ///
+ /// The parent optimizer.
+ /// A function creating a new RNG instance.
+ public InitializePlayers(
+ MetaOptimizer optimizer,
+ Func, TRandom> createRandom)
+ {
+ parent = optimizer;
+ getRandom = createRandom;
+ }
+
+ ///
+ /// Returns the current instance.
+ ///
+ protected override InitializePlayers
+ CreateBody() => this;
+
+ ///
+ /// Creates an intermediate state which uses the parent RNG to create fresh
+ /// random numbers in parallel.
+ ///
+ protected override InitializePlayersState
+ CreateIntermediate() => new(getRandom(parent));
+
+ ///
+ /// Does not perform any operation.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Initialize() { }
+
+ ///
+ /// Accumulates offensive and defensive players into OG and DG vectors.
+ ///
+ [MethodImpl(
+ MethodImplOptions.AggressiveInlining |
+ MethodImplOptions.AggressiveOptimization)]
+ public void Process(
+ int index,
+ ParallelLoopState? loopState,
+ InitializePlayersState intermediateState)
+ {
+ // Get player and the local bounds
+ var player = parent.GetPosition(index).CastUnsafe();
+ var lower = parent.lowerBounds.AsSpan().CastUnsafe();
+ var upper = parent.upperBounds.AsSpan().CastUnsafe();
+
+ // Initialize a new processor
+ var processor = TProcessor.New();
+
+ // Initialize all player positions
+ for (int i = 0; i < parent.NumDimensionSlices; ++i)
+ {
+ // Draw a new random value
+ var randomValue = intermediateState.Next();
+
+ // Initialize local position
+ var initialPosition = processor.GetRandomPosition(
+ lower.GetItemRef(i),
+ upper.GetItemRef(i),
+ randomValue);
+ player.GetItemRef(i) = initialPosition;
+ }
+ }
+
+ ///
+ /// Does not perform any operation.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Finalize(
+ ReadOnlySpan>
+ intermediateStates)
+ { }
+ }
+ }
+}
+
+#endif
diff --git a/Src/ILGPU.Algorithms/Optimization/CPU/MetaOptimizer.Instance.cs b/Src/ILGPU.Algorithms/Optimization/CPU/MetaOptimizer.Instance.cs
new file mode 100644
index 0000000000..5bc677e417
--- /dev/null
+++ b/Src/ILGPU.Algorithms/Optimization/CPU/MetaOptimizer.Instance.cs
@@ -0,0 +1,568 @@
+// ---------------------------------------------------------------------------------------
+// ILGPU Algorithms
+// Copyright (c) 2023 ILGPU Project
+// www.ilgpu.net
+//
+// File: MetaOptimizer.Instance.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.Random;
+using ILGPU.Util;
+using System;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+
+#if NET7_0_OR_GREATER
+
+#pragma warning disable CA1000 // No static members on generic types
+
+namespace ILGPU.Algorithms.Optimization.CPU
+{
+ partial class MetaOptimizer
+ {
+ ///
+ /// Holds intermediate and run-specific optimizer instances that depend on
+ /// objective function and random instances.
+ ///
+ /// The internal evaluator type.
+ /// The objective function type.
+ ///
+ /// The type of all intermediate states during processing.
+ ///
+ /// The processor type being used.
+ /// The processor element type.
+ /// The random range generator type.
+ sealed class RuntimeInstance<
+ TEvaluator,
+ TFunction,
+ TIntermediate,
+ TProcessor,
+ TType,
+ TRandom> : DisposeBase
+ where TEvaluator : class, IEvaluator
+ where TFunction : IBaseOptimizationFunction
+ where TIntermediate : class
+ where TProcessor : struct, IProcessor
+ where TType : unmanaged
+ where TRandom : struct, IRandomRangeProvider
+ {
+ private readonly MetaOptimizer optimizer;
+ private readonly TEvaluator evaluator;
+ private readonly UpdatePlayers<
+ TFunction,
+ TProcessor,
+ TType,
+ TRandom> updatePlayers;
+
+ ///
+ /// Creates a new runtime instance.
+ ///
+ /// The parent optimizer.
+ ///
+ /// A specialized random provider generator.
+ ///
+ /// The objective function.
+ /// The evaluator instance.
+ public RuntimeInstance(
+ MetaOptimizer parent,
+ Func, TRandom> createRandom,
+ in TFunction function,
+ TEvaluator evaluatorInstance)
+ {
+ optimizer = parent;
+ evaluator = evaluatorInstance;
+ updatePlayers = new(parent, createRandom, function)
+ {
+ BestPosition = evaluator.ResultManager.BestInternalPosition
+ };
+ }
+
+ ///
+ /// Returns the best result manager.
+ ///
+ public ResultManager ResultManager => evaluator.ResultManager;
+
+ ///
+ /// Evaluates all player positions.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void EvaluatePlayers(ParallelOptions options) =>
+ evaluator.EvaluatePlayers(options);
+
+ ///
+ /// Updates all player positions.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void UpdatePlayers(ParallelOptions options)
+ {
+ updatePlayers.ParallelFor(0, optimizer.M, options);
+
+ // Update SOG and SDG information
+ updatePlayers.HasCurrentSOGAndSDG = true;
+ }
+
+ ///
+ /// Disposes the current evaluator and the specialized update players
+ /// instance.
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ evaluator.Dispose();
+ updatePlayers.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+ }
+
+ ///
+ /// An instance implementing
+ ///
+ /// The processor type being used.
+ /// The processor element type.
+ ///
+ /// The random range generator type for scalar types.
+ ///
+ ///
+ /// The random range generator type for specialized processing types.
+ ///
+ sealed class Instance<
+ TProcessor,
+ TType,
+ TRandom,
+ TTypeRandom> : MetaOptimizer
+ where TProcessor : struct, IProcessor
+ where TType : unmanaged
+ where TRandom : struct, IRandomRangeProvider
+ where TTypeRandom : struct, IRandomRangeProvider
+ {
+ private readonly ParallelOptions parallelOptions;
+ private readonly OGAndDG ogAndDG;
+ private readonly AdjustSOGPlayers<
+ TProcessor,
+ TType,
+ TRandom> adjustSOGPlayers;
+ private readonly InitializePlayers<
+ TProcessor,
+ TType,
+ TTypeRandom> initializePlayers;
+
+ private readonly Func, TRandom> getRandom;
+
+ ///
+ /// Creates a new meta optimizer instance.
+ ///
+ /// The input random number generator.
+ /// The number of players to use.
+ /// The dimensionality of the problem.
+ ///
+ /// The maximum number of parallel processing threads (if any).
+ ///
+ ///
+ /// A function callback to create random range generators for type T.
+ ///
+ ///
+ /// A function callback to create random range generators for type TType.
+ ///
+ public Instance(
+ System.Random inputRandom,
+ int numPlayers,
+ int numDimensions,
+ int? maxNumParallelThreads,
+ Func, TRandom> createRandom,
+ Func, TTypeRandom> createTTypeRandom)
+ : base(
+ inputRandom,
+ numPlayers,
+ numDimensions,
+ maxNumParallelThreads,
+ TProcessor.Length)
+ {
+ ogAndDG = new(this);
+ adjustSOGPlayers = new(this, createRandom);
+ initializePlayers = new(this, createTTypeRandom);
+
+ getRandom = createRandom;
+
+ // Create new parallel options limiting the max degree of parallelism
+ parallelOptions = new ParallelOptions()
+ {
+ MaxDegreeOfParallelism = MaxNumWorkers,
+ };
+ }
+
+ ///
+ /// Optimizes the given optimization function while using a specified
+ /// break function and initial values for the best result.
+ ///
+ /// The optimization function type.
+ ///
+ /// The intermediate optimization state type.
+ ///
+ /// The break function type.
+ /// The position modifier type.
+ ///
+ /// The optimization function to use.
+ ///
+ /// The break function to use.
+ ///
+ /// The position modifier to apply to all position updates during
+ /// optimization.
+ ///
+ /// Te best known result.
+ /// The best known position.
+ ///
+ /// A tuple consisting of the best result and position found.
+ ///
+ public override (TEvalType Result, Memory Position) Optimize<
+ TFunction,
+ TIntermediate,
+ TBreakFunction,
+ TModifier>(
+ in TFunction optimizationFunction,
+ in TBreakFunction breakFunction,
+ in TModifier positionModifier,
+ TEvalType bestResult,
+ ReadOnlyMemory? bestKnownPosition = default)
+ {
+ // Create new evaluator based on the given optimization function
+ var evaluator = new Evaluator(
+ this,
+ optimizationFunction,
+ positionModifier,
+ bestResult,
+ bestKnownPosition);
+
+ // Create a new runtime instance to track all instances for this run
+ using var runtimeInstance = new RuntimeInstance<
+ Evaluator,
+ TFunction,
+ TIntermediate,
+ TProcessor,
+ TType,
+ TRandom>(
+ this,
+ getRandom,
+ optimizationFunction,
+ evaluator);
+
+ // Perform optimization
+ OptimizeInternal(breakFunction, runtimeInstance);
+
+ // Load best result information
+ var resultManager = runtimeInstance.ResultManager;
+ return (resultManager.BestResult, resultManager.BestPosition);
+ }
+
+ public override (TEvalType Result, Memory Position) OptimizeRaw(
+ RawCPUOptimizationFunction optimizationFunction,
+ CPUOptimizationBreakFunction breakFunction,
+ CPUEvaluationComparison evaluationComparison,
+ TEvalType bestResult,
+ ReadOnlyMemory? bestKnownPosition = default)
+ {
+ // Create new evaluator based on the given optimization function
+ var evaluator = new RawEvaluator(
+ this,
+ optimizationFunction,
+ evaluationComparison,
+ bestResult,
+ bestKnownPosition);
+
+ // Create our raw function wrapper
+ var wrapper = new RawComparisonWrapper(evaluationComparison);
+
+ // Create a new runtime instance to track all instances for this run
+ using var runtimeInstance = new RuntimeInstance<
+ RawEvaluator,
+ RawComparisonWrapper,
+ object,
+ TProcessor,
+ TType,
+ TRandom>(
+ this,
+ getRandom,
+ wrapper,
+ evaluator);
+
+ // Perform optimization
+ var breakFunctionWrapper = new BreakFunctionWrapper(breakFunction);
+ OptimizeInternal(breakFunctionWrapper, runtimeInstance);
+
+ // Load best result information
+ var resultManager = runtimeInstance.ResultManager;
+ return (resultManager.BestResult, resultManager.BestPosition);
+ }
+
+ ///
+ /// The internal optimizer loop which used the SGO algorithm to adjust
+ /// player/particle positions according to the objective functions and the
+ /// update parameters defined.
+ ///
+ /// The break function to use.
+ ///
+ /// The current runtime instance holding all temporary instances.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveOptimization)]
+ private void OptimizeInternal<
+ TFunction,
+ TIntermediate,
+ TBreakFunction,
+ TEvaluator>(
+ in TBreakFunction breakFunction,
+ RuntimeInstance<
+ TEvaluator,
+ TFunction,
+ TIntermediate,
+ TProcessor,
+ TType,
+ TRandom> runtimeInstance)
+ where TEvaluator : class, IEvaluator
+ where TFunction : IBaseOptimizationFunction
+ where TIntermediate : class
+ where TBreakFunction : ICPUOptimizationBreakFunction
+ {
+ // Update internal references
+ adjustSOGPlayers.BestPosition =
+ runtimeInstance.ResultManager.BestInternalPosition;
+
+ // Initialize all players
+ initializePlayers.ParallelFor(0, NumPlayers, parallelOptions);
+
+ // Evaluate all players first
+ runtimeInstance.EvaluatePlayers(parallelOptions);
+
+ // Enter actual optimizer loop
+ for (int iteration = 0; ; ++iteration)
+ {
+ // Permute all indices in the beginning
+ Permute();
+
+ // Copy positions to new versions
+ CopyPositions();
+
+ // Initialize all SOG information
+ InitSOGList();
+
+ // Compute OG and DG information
+ ogAndDG.ParallelFor(0, M, parallelOptions);
+
+ // Update all players
+ runtimeInstance.UpdatePlayers(parallelOptions);
+
+ // Update SOG adjustments
+ if (iteration > 0)
+ adjustSOGPlayers.ParallelFor(0, sogListCounter, parallelOptions);
+
+ // Finally, swap all buffers
+ SwapBuffers();
+
+ // Evaluate all players
+ runtimeInstance.EvaluatePlayers(parallelOptions);
+
+ // Check for user-defined break predicates
+ if (breakFunction.Break(
+ runtimeInstance.ResultManager.BestResult,
+ iteration))
+ {
+ break;
+ }
+ }
+ }
+
+ #region IDisposable
+
+ ///
+ /// Disposes internal parallel cache instances.
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ ogAndDG.Dispose();
+ adjustSOGPlayers.Dispose();
+ initializePlayers.Dispose();
+ }
+
+ base.Dispose(disposing);
+ }
+
+ #endregion
+ }
+
+ ///
+ /// Creates a new meta optimizer using non-vectorized scalar operations.
+ ///
+ /// The random range provider type to use.
+ /// The input random number generator.
+ ///
+ /// The number of players to use (must be at least two and an even number).
+ ///
+ ///
+ /// The number of dimensions (must be greater than one).
+ ///
+ ///
+ /// The maximum number of parallel threads (if any). Not providing a specific
+ /// number of threads means using as many threads as possible.
+ ///
+ /// The created meta optimizer instance.
+ [CLSCompliant(false)]
+ public static MetaOptimizer CreateScalar(
+ System.Random inputRandom,
+ int numPlayers,
+ int numDimensions,
+ int? maxNumParallelThreads = null)
+ where TRandom : struct, IRandomRangeProvider
+ {
+ // Creates new random range generators using the scalar type T
+ TRandom CreateRandom(MetaOptimizer parent) =>
+ TRandom.Create(parent.random, T.Zero, T.One);
+
+ return new Instance(
+ inputRandom,
+ numPlayers,
+ numDimensions,
+ maxNumParallelThreads,
+ CreateRandom,
+ CreateRandom);
+ }
+
+ ///
+ /// Creates a new meta optimizer using vectorized operations.
+ ///
+ /// The random range provider type to use.
+ /// The input random number generator.
+ ///
+ /// The number of players to use (must be at least two and an even number).
+ ///
+ ///
+ /// The number of dimensions (must be greater than one).
+ ///
+ ///
+ /// The maximum number of parallel threads (if any). Not providing a specific
+ /// number of threads means using as many threads as possible.
+ ///
+ /// The created meta optimizer instance.
+ [CLSCompliant(false)]
+ public static MetaOptimizer CreateVectorized(
+ System.Random inputRandom,
+ int numPlayers,
+ int numDimensions,
+ int? maxNumParallelThreads = null)
+ where TRandom : struct, IRandomRangeProvider
+ {
+ // Creates new random range generators using the scalar type T
+ TRandom CreateRandom(MetaOptimizer parent) =>
+ TRandom.Create(parent.random, T.Zero, T.One);
+
+ // Creates new random range generators using the vectorized type TType
+ RandomRangeVectorProvider CreateVectorizedRandom(
+ MetaOptimizer parent) =>
+ CreateRandom(parent).CreateVectorProvider();
+
+ return new Instance<
+ VectorizedProcessor,
+ Vector,
+ TRandom,
+ RandomRangeVectorProvider>(
+ inputRandom,
+ numPlayers,
+ numDimensions,
+ maxNumParallelThreads,
+ CreateRandom,
+ CreateVectorizedRandom);
+ }
+ }
+
+ ///
+ /// A static helper class for instances.
+ ///
+ public static class MetaOptimizer
+ {
+ #region Static
+
+ ///
+ /// Creates a new meta optimizer using non-vectorized scalar operations.
+ ///
+ ///
+ /// The main element type for all position vectors.
+ ///
+ /// The evaluation data type.
+ /// The random range provider type to use.
+ /// The input random number generator.
+ ///
+ /// The number of players to use (must be at least two and an even number).
+ ///
+ ///
+ /// The number of dimensions (must be greater than one).
+ ///
+ ///
+ /// The maximum number of parallel threads (if any). Not providing a specific
+ /// number of threads means using as many threads as possible.
+ ///
+ /// The created meta optimizer instance.
+ [CLSCompliant(false)]
+ public static MetaOptimizer CreateScalar(
+ System.Random inputRandom,
+ int numPlayers,
+ int numDimensions,
+ int? maxNumParallelThreads = null)
+ where T : unmanaged, INumber
+ where TEvalType : struct, IEquatable
+ where TRandom : struct, IRandomRangeProvider =>
+ MetaOptimizer.CreateScalar(
+ inputRandom,
+ numPlayers,
+ numDimensions,
+ maxNumParallelThreads);
+
+ ///
+ /// Creates a new meta optimizer using vectorized operations.
+ ///
+ ///
+ /// The main element type for all position vectors.
+ ///
+ /// The evaluation data type.
+ /// The random range provider type to use.
+ /// The input random number generator.
+ ///
+ /// The number of players to use (must be at least two and an even number).
+ ///
+ ///
+ /// The number of dimensions (must be greater than one).
+ ///
+ ///
+ /// The maximum number of parallel threads (if any). Not providing a specific
+ /// number of threads means using as many threads as possible.
+ ///
+ /// The created meta optimizer instance.
+ [CLSCompliant(false)]
+ public static MetaOptimizer CreateVectorized<
+ T,
+ TEvalType,
+ TRandom>(
+ System.Random inputRandom,
+ int numPlayers,
+ int numDimensions,
+ int? maxNumParallelThreads = null)
+ where T : unmanaged, INumber
+ where TEvalType : struct, IEquatable
+ where TRandom : struct, IRandomRangeProvider =>
+ MetaOptimizer.CreateVectorized(
+ inputRandom,
+ numPlayers,
+ numDimensions,
+ maxNumParallelThreads);
+
+ #endregion
+ }
+}
+
+#pragma warning restore CA1000
+
+#endif
diff --git a/Src/ILGPU.Algorithms/Optimization/CPU/MetaOptimizer.OGAndDG.cs b/Src/ILGPU.Algorithms/Optimization/CPU/MetaOptimizer.OGAndDG.cs
new file mode 100644
index 0000000000..013a1b32ad
--- /dev/null
+++ b/Src/ILGPU.Algorithms/Optimization/CPU/MetaOptimizer.OGAndDG.cs
@@ -0,0 +1,192 @@
+// ---------------------------------------------------------------------------------------
+// ILGPU Algorithms
+// Copyright (c) 2023 ILGPU Project
+// www.ilgpu.net
+//
+// File: MetaOptimizer.OGAndDG.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.Util;
+using System;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+
+#if NET7_0_OR_GREATER
+
+namespace ILGPU.Algorithms.Optimization.CPU
+{
+ partial class MetaOptimizer
+ {
+ ///
+ /// Represents an intermediate parallel processing state for OG and DG state.
+ ///
+ private sealed class OGAndDGState
+ {
+ private readonly T[] nextOG;
+ private readonly T[] nextDG;
+
+ ///
+ /// Creates a new intermediate state.
+ ///
+ /// The number of dimensions.
+ public OGAndDGState(int numDimensions)
+ {
+ nextOG = new T[numDimensions];
+ nextDG = new T[numDimensions];
+ }
+
+ ///
+ /// Returns a span of the given processing type pointing to the next OG.
+ ///
+ /// The processing type.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Span GetNextOG() where TType : struct =>
+ nextOG.AsSpan().CastUnsafe();
+
+ ///
+ /// Returns a span of the given processing type pointing to the next DG.
+ ///
+ /// The processing type.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Span GetNextDG() where TType : struct =>
+ nextDG.AsSpan().CastUnsafe();
+ }
+
+ ///
+ /// Computes OG and DG information.
+ ///
+ /// The processor type.
+ /// The processor element type.
+ private sealed class OGAndDG :
+ ParallelProcessingCache>,
+ IParallelProcessingBody
+ where TProcessor : struct, IProcessor
+ where TType : unmanaged
+ {
+ private readonly MetaOptimizer parent;
+ private readonly T convertedM;
+
+ ///
+ /// Creates a new OG and DG computer.
+ ///
+ /// The parent optimizer.
+ public OGAndDG(MetaOptimizer optimizer)
+ {
+ parent = optimizer;
+ convertedM = T.CreateTruncating(optimizer.M);
+ }
+
+ ///
+ /// Returns the current instance.
+ ///
+ protected override OGAndDG CreateBody() => this;
+
+ ///
+ /// Creates an intermediate temporary accumulation array of two times the
+ /// dimension size.
+ ///
+ protected override OGAndDGState CreateIntermediate() =>
+ new(parent.NumPaddedDimensions);
+
+ ///
+ /// Resets the given intermediate state by resetting all values to T.Zero.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveOptimization)]
+ protected override void InitializeIntermediate(
+ OGAndDGState intermediateState)
+ {
+ var nextOG = intermediateState.GetNextOG();
+ var nextDG = intermediateState.GetNextDG();
+
+ parent.Reset(nextOG, nextDG);
+ }
+
+ ///
+ /// Resets parent OG and DG vectors for accumulation purposes.
+ ///
+ [MethodImpl(
+ MethodImplOptions.AggressiveInlining |
+ MethodImplOptions.AggressiveOptimization)]
+ public void Initialize()
+ {
+ // Reset OG and DG vectors
+ var og = parent.og.AsSpan().CastUnsafe();
+ var dg = parent.dg.AsSpan().CastUnsafe();
+
+ parent.Reset(og, dg);
+ }
+
+ ///
+ /// Accumulates offensive and defensive players into OG and DG vectors.
+ ///
+ [MethodImpl(
+ MethodImplOptions.AggressiveInlining |
+ MethodImplOptions.AggressiveOptimization)]
+ public void Process(
+ int index,
+ ParallelLoopState? loopState,
+ OGAndDGState intermediateState)
+ {
+ // Get offsets and spans for offensive and defensive players
+ var indices = parent.indices.AsSpan();
+ int offensiveIndex = indices.GetItemRef(index);
+ int defensiveIndex = indices.GetItemRef(index + parent.M);
+
+ // Get the actual source views
+ var offensive = parent
+ .GetPosition(offensiveIndex)
+ .CastUnsafe();
+ var defensive = parent
+ .GetPosition(defensiveIndex)
+ .CastUnsafe();
+
+ // Get the actual target views
+ var og = intermediateState.GetNextOG();
+ var dg = intermediateState.GetNextDG();
+
+ // Accumulate all intermediates
+ parent.Accumulate(
+ og,
+ dg,
+ offensive,
+ defensive);
+ }
+
+ ///
+ /// Accumulates all intermediate OG and DG states while averaging the result.
+ ///
+ [MethodImpl(
+ MethodImplOptions.AggressiveInlining |
+ MethodImplOptions.AggressiveOptimization)]
+ public void Finalize(ReadOnlySpan intermediateStates)
+ {
+ var og = parent.og.AsSpan().CastUnsafe();
+ var dg = parent.dg.AsSpan().CastUnsafe();
+
+ // Iterate over all dimensions and states accumulate results
+ foreach (var state in intermediateStates)
+ {
+ var sourceOG = state.GetNextOG();
+ var sourceDG = state.GetNextDG();
+
+ parent.Accumulate(
+ og,
+ dg,
+ sourceOG,
+ sourceDG);
+ }
+
+ // Compute averages over all dimension slices
+ parent.ComputeAverage(
+ og,
+ dg,
+ convertedM);
+ }
+ }
+ }
+}
+
+#endif
diff --git a/Src/ILGPU.Algorithms/Optimization/CPU/MetaOptimizer.RawEvaluator.cs b/Src/ILGPU.Algorithms/Optimization/CPU/MetaOptimizer.RawEvaluator.cs
new file mode 100644
index 0000000000..57f08e2545
--- /dev/null
+++ b/Src/ILGPU.Algorithms/Optimization/CPU/MetaOptimizer.RawEvaluator.cs
@@ -0,0 +1,205 @@
+// ---------------------------------------------------------------------------------------
+// ILGPU Algorithms
+// Copyright (c) 2023 ILGPU Project
+// www.ilgpu.net
+//
+// File: MetaOptimizer.RawEvaluator.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.Util;
+using System;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+
+#if NET7_0_OR_GREATER
+
+namespace ILGPU.Algorithms.Optimization.CPU
+{
+ partial class MetaOptimizer
+ {
+ ///
+ /// Represents a comparison interface wrapper around a delegate comparison
+ /// function used to compare evaluation results.
+ ///
+ /// The evaluation delegate.
+ private readonly record struct RawComparisonWrapper(
+ CPUEvaluationComparison EvaluationComparison) :
+ IBaseOptimizationFunction,
+ IParallelCache