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

🔍 Tune search values (spsa) + define limits and steps in code #730

Merged
merged 10 commits into from
May 9, 2024
29 changes: 16 additions & 13 deletions src/Lynx.Cli/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,40 +14,43 @@
"UseOnlineTablebaseInSearch": false,
"OnlineTablebaseMaxSupportedPieces": 7,
"ShowWDL": false,
"SPSA_OB_R_end": 0.02,

"HardTimeBoundMultiplier": 0.52,
"SoftTimeBoundMultiplier": 1,
"DefaultMovesToGo": 45,
"SoftTimeBaseIncrementMultiplier": 0.8,

"LMR_MinDepth": 3,
"LMR_MinFullDepthSearchedMoves": 4,
"LMR_MinFullDepthSearchedMoves": 3,
"LMR_Base": 0.85,
"LMR_Divisor": 2.84,
"LMR_Divisor": 3.12,

"NMP_MinDepth": 3,
"NMP_BaseDepthReduction": 1,
"NMP_BaseDepthReduction": 2,
"NMP_DepthIncrement": 1,
"NMP_DepthDivisor": 5,

"AspirationWindow_Delta": 20,
"AspirationWindow_Delta": 12,
"AspirationWindow_MinDepth": 7,

"RFP_MaxDepth": 4,
"RFP_DepthScalingFactor": 87,
"RFP_MaxDepth": 6,
"RFP_DepthScalingFactor": 107,

"Razoring_MaxDepth": 3,
"Razoring_Depth1Bonus": 105,
"Razoring_NotDepth1Bonus": 161,
"Razoring_MaxDepth": 2,
"Razoring_Depth1Bonus": 84,
"Razoring_NotDepth1Bonus": 135,

"IIR_MinDepth": 2,
"IIR_MinDepth": 3,

"LMP_MaxDepth": 2,
"LMP_MaxDepth": 6,
"LMP_BaseMovesToTry": 0,
"LMP_MovesDepthMultiplier": 10,
"LMP_MovesDepthMultiplier": 4,

"History_MaxMoveValue": 8192,
"History_MaxMoveRawBonus": 1896,

"SEE_BadCaptureReduction": 1,
"SEE_BadCaptureReduction": 2,

// Evaluation
"DoubledPawnPenalty": {
Expand Down
54 changes: 41 additions & 13 deletions src/Lynx/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ public sealed class EngineSettings

public bool ShowWDL { get; set; } = false;

public double SPSA_OB_R_end { get; set; } = 0.02;

#region Time management

public double HardTimeBoundMultiplier { get; set; } = 0.52;
Expand All @@ -115,45 +117,68 @@ public sealed class EngineSettings

#endregion

[SPSAAttribute<int>(2, 10, 1)]
public int LMR_MinDepth { get; set; } = 3;

public int LMR_MinFullDepthSearchedMoves { get; set; } = 4;
[SPSAAttribute<int>(1, 10, 1)]
public int LMR_MinFullDepthSearchedMoves { get; set; } = 3;

/// <summary>
/// Value originally from Stormphrax, who apparently took it from Viridithas
/// </summary>
[SPSAAttribute<double>(0.1, 2, 0.1)]
public double LMR_Base { get; set; } = 0.85;

/// <summary>
/// Value originally from Akimbo
/// </summary>
public double LMR_Divisor { get; set; } = 2.84;
[SPSAAttribute<double>(1, 5, 0.1)]
public double LMR_Divisor { get; set; } = 3.12;

[SPSAAttribute<int>(1, 10, 1)]
public int NMP_MinDepth { get; set; } = 3;

public int NMP_BaseDepthReduction { get; set; } = 1;
[SPSAAttribute<int>(1, 5, 1)]
public int NMP_BaseDepthReduction { get; set; } = 2;

[SPSAAttribute<int>(0, 20, 1)]
public int NMP_DepthIncrement { get; set; } = 1;

[SPSAAttribute<int>(1, 20, 1)]
public int NMP_DepthDivisor { get; set; } = 5;

public int AspirationWindow_Delta { get; set; } = 20;
[SPSAAttribute<int>(1, 100, 5)]
public int AspirationWindow_Delta { get; set; } = 12;

[SPSAAttribute<int>(1, 20, 1)]
public int AspirationWindow_MinDepth { get; set; } = 7;

public int RFP_MaxDepth { get; set; } = 4;
[SPSAAttribute<int>(1, 20, 1)]
public int RFP_MaxDepth { get; set; } = 6;

public int RFP_DepthScalingFactor { get; set; } = 87;
[SPSAAttribute<int>(1, 300, 5)]
public int RFP_DepthScalingFactor { get; set; } = 107;

public int Razoring_MaxDepth { get; set; } = 3;
[SPSAAttribute<int>(1, 10, 1)]
public int Razoring_MaxDepth { get; set; } = 2;

public int Razoring_Depth1Bonus { get; set; } = 105;
[SPSAAttribute<int>(1, 300, 10)]
public int Razoring_Depth1Bonus { get; set; } = 84;

public int Razoring_NotDepth1Bonus { get; set; } = 161;
[SPSAAttribute<int>(1, 300, 10)]
public int Razoring_NotDepth1Bonus { get; set; } = 135;

public int IIR_MinDepth { get; set; } = 2;
[SPSAAttribute<int>(1, 10, 1)]
public int IIR_MinDepth { get; set; } = 3;

public int LMP_MaxDepth { get; set; } = 2;
[SPSAAttribute<int>(1, 10, 1)]
public int LMP_MaxDepth { get; set; } = 6;

[SPSAAttribute<int>(0, 10, 1)]
public int LMP_BaseMovesToTry { get; set; } = 0;

public int LMP_MovesDepthMultiplier { get; set; } = 10;
[SPSAAttribute<int>(0, 50, 1)]
public int LMP_MovesDepthMultiplier { get; set; } = 4;

public int History_MaxMoveValue { get; set; } = 8_192;

Expand All @@ -162,7 +187,8 @@ public sealed class EngineSettings
/// </summary>
public int History_MaxMoveRawBonus { get; set; } = 1_896;

public int SEE_BadCaptureReduction { get; set; } = 1;
[SPSAAttribute<int>(0, 6, 1)]
public int SEE_BadCaptureReduction { get; set; } = 2;

#region Evaluation

Expand Down Expand Up @@ -309,6 +335,8 @@ public override string ToString()
[JsonSerializable(typeof(EngineSettings))]
[JsonSerializable(typeof(TaperedEvaluationTerm))]
[JsonSerializable(typeof(TaperedEvaluationTermByRank))]
[JsonSerializable(typeof(SPSAAttribute<int>))]
[JsonSerializable(typeof(SPSAAttribute<double>))]
internal partial class EngineSettingsJsonSerializerContext : JsonSerializerContext
{
}
85 changes: 85 additions & 0 deletions src/Lynx/SPSAAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using System.Numerics;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Nodes;

namespace Lynx;

[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
internal class SPSAAttribute<T> : Attribute
where T : INumberBase<T>, IMultiplyOperators<T, T, T>, IConvertible, IParsable<T>, ISpanParsable<T>
{
private static readonly T _hundred;

public T MinValue { get; }
public T MaxValue { get; }
public T Step { get; }

#pragma warning disable S3963 // "static" fields should be initialized inline
static SPSAAttribute()
#pragma warning restore S3963 // "static" fields should be initialized inline
{
_hundred = T.Zero;
for (int i = 0; i < 100; i++)
{
_hundred += T.One;
}
}

public SPSAAttribute(T minValue, T maxValue, T step)
{
if (typeof(T) == typeof(double))
{
minValue *= _hundred;
maxValue *= _hundred;
step *= _hundred;
}

MinValue = minValue;
MaxValue = maxValue;
Step = step;
}

public string ToOBString(PropertyInfo property)
{
T val = GetPropertyValue(property);

return $"{property.Name}, int, {val}, {MinValue}, {MaxValue}, {Step}, {Configuration.EngineSettings.SPSA_OB_R_end}";
}

private static T GetPropertyValue(PropertyInfo property)
{
T val = (T)property.GetValue(Configuration.EngineSettings)!;

if (typeof(T) == typeof(double))
{
val *= _hundred;
}

return val;
}

public string ToOBPrettyString(PropertyInfo property)
{
T val = GetPropertyValue(property);

return $"{property.Name,-35} {"int",-5} {val,-5} {MinValue,-5} {MaxValue,-5} {Step,-5} {Configuration.EngineSettings.SPSA_OB_R_end,-5}";
}

public KeyValuePair<string, JsonNode?> ToWeatherFactoryString(PropertyInfo property)
{
T val = GetPropertyValue(property);

#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code
return KeyValuePair.Create(
property.Name,
JsonSerializer.SerializeToNode(new
{
value = val,
min_value = MinValue,
max_value = MaxValue,
step = Step,
}));
#pragma warning restore IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code
}
}
3 changes: 2 additions & 1 deletion src/Lynx/Search/NegaMax.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ private int NegaMax(int depth, int ply, int alpha, int beta, bool parentWasNullM
&& phase > 2 // Zugzwang risk reduction: pieces other than pawn presents
&& (ttElementType != NodeType.Alpha || ttEvaluation >= beta)) // TT suggests NMP will fail: entry must not be a fail-low entry with a score below beta - Stormphrax and Ethereal
{
var nmpReduction = Configuration.EngineSettings.NMP_BaseDepthReduction + ((depth + 1) / 3); // Clarity
var nmpReduction = Configuration.EngineSettings.NMP_BaseDepthReduction + ((depth + Configuration.EngineSettings.NMP_DepthIncrement) / Configuration.EngineSettings.NMP_DepthDivisor); // Clarity

// TODO more advanced adaptative reduction, similar to what Ethereal and Stormphrax are doing
//var nmpReduction = Math.Min(
Expand Down Expand Up @@ -306,6 +306,7 @@ private int NegaMax(int depth, int ply, int alpha, int beta, bool parentWasNullM
&& scores[moveIndex] >= EvaluationConstants.BadCaptureMoveBaseScoreValue)
{
reduction += Configuration.EngineSettings.SEE_BadCaptureReduction;
reduction = Math.Clamp(reduction, 0, depth - 1);
}

// Search with reduced depth
Expand Down
3 changes: 3 additions & 0 deletions src/Lynx/UCI/Commands/Engine/OptionCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ public sealed class OptionCommand : EngineBaseCommand
//$"option name {nameof(Configuration.EngineSettings.LMR_Divisor)} type spin default {100 * Configuration.EngineSettings.LMR_Divisor} min 0 max 1024",
//$"option name {nameof(Configuration.EngineSettings.NMP_MinDepth)} type spin default {Configuration.EngineSettings.NMP_MinDepth} min 0 max 1024",
//$"option name {nameof(Configuration.EngineSettings.NMP_BaseDepthReduction)} type spin default {Configuration.EngineSettings.NMP_BaseDepthReduction} min 0 max 1024",
//$"option name {nameof(Configuration.EngineSettings.NMP_DepthIncrement)} type spin default {Configuration.EngineSettings.NMP_DepthIncrement} min 0 max 1024",
//$"option name {nameof(Configuration.EngineSettings.NMP_DepthDivisor)} type spin default {Configuration.EngineSettings.NMP_DepthDivisor} min 0 max 1024",
//$"option name {nameof(Configuration.EngineSettings.AspirationWindow_Delta)} type spin default {Configuration.EngineSettings.AspirationWindow_Delta} min 0 max 1024",
//$"option name {nameof(Configuration.EngineSettings.AspirationWindow_MinDepth)} type spin default {Configuration.EngineSettings.AspirationWindow_MinDepth} min 0 max 1024",
//$"option name {nameof(Configuration.EngineSettings.RFP_MaxDepth)} type spin default {Configuration.EngineSettings.RFP_MaxDepth} min 0 max 1024",
Expand All @@ -151,6 +153,7 @@ public sealed class OptionCommand : EngineBaseCommand
//$"option name {nameof(Configuration.EngineSettings.LMP_MaxDepth)} type spin default {Configuration.EngineSettings.LMP_MaxDepth} min 0 max 1024",
//$"option name {nameof(Configuration.EngineSettings.LMP_BaseMovesToTry)} type spin default {Configuration.EngineSettings.LMP_BaseMovesToTry} min 0 max 1024",
//$"option name {nameof(Configuration.EngineSettings.LMP_MovesDepthMultiplier)} type spin default {Configuration.EngineSettings.LMP_MovesDepthMultiplier} min 0 max 1024",
//$"option name {nameof(Configuration.EngineSettings.SEE_BadCaptureReduction)} type spin default {Configuration.EngineSettings.SEE_BadCaptureReduction} min 0 max 1024",

#endregion
];
Expand Down
Loading
Loading