141 lines
4.2 KiB
C#
141 lines
4.2 KiB
C#
using System.Collections.Concurrent;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
|
|
namespace StellaOps.ElkSharp;
|
|
|
|
internal static partial class ElkEdgeRouterIterative
|
|
{
|
|
private static bool ShouldRetryForEdgeCrossings(
|
|
RoutingRetryState retryState,
|
|
int attempt,
|
|
int maxAdaptationsPerStrategy)
|
|
{
|
|
if (retryState.RequiresPrimaryRetry || retryState.EdgeCrossings <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return attempt < Math.Max(0, maxAdaptationsPerStrategy - 1);
|
|
}
|
|
|
|
private static int DetermineAdaptiveAttemptBudget(
|
|
RoutingRetryState retryState,
|
|
int maxAdaptationsPerStrategy)
|
|
{
|
|
var boundedMaximum = Math.Clamp(maxAdaptationsPerStrategy, 1, 12);
|
|
if (retryState.RequiresBlockingRetry)
|
|
{
|
|
var complexBlocking =
|
|
retryState.UnderNodeViolations > 0
|
|
|| retryState.SharedLaneViolations > 0
|
|
|| retryState.TargetApproachJoinViolations > 0
|
|
|| retryState.GatewaySourceExitViolations > 0;
|
|
return Math.Min(boundedMaximum, complexBlocking ? 6 : 5);
|
|
}
|
|
|
|
if (retryState.RequiresLengthRetry)
|
|
{
|
|
return Math.Min(boundedMaximum, 4);
|
|
}
|
|
|
|
if (retryState.RequiresQualityRetry || retryState.EdgeCrossings > 0)
|
|
{
|
|
return Math.Min(boundedMaximum, 3);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
private static bool ShouldStopForStagnation(int stagnantAttempts, int attempt, int maxAdaptationsPerStrategy)
|
|
{
|
|
if (stagnantAttempts <= 0 || attempt < 2)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var stagnationBudget = Math.Min(Math.Max(4, maxAdaptationsPerStrategy / 12), 8);
|
|
return stagnantAttempts >= stagnationBudget;
|
|
}
|
|
|
|
private static bool ShouldRetryForPrimaryViolations(
|
|
RoutingRetryState retryState,
|
|
int attempt,
|
|
int maxAdaptationsPerStrategy)
|
|
{
|
|
if (!retryState.RequiresPrimaryRetry)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return attempt < Math.Max(0, maxAdaptationsPerStrategy - 1);
|
|
}
|
|
|
|
private static int DetermineTargetValidSolutionCount(
|
|
RoutingRetryState baselineRetryState,
|
|
IterativeRoutingConfig config)
|
|
{
|
|
if (!baselineRetryState.RequiresPrimaryRetry)
|
|
{
|
|
return config.RequiredValidSolutions;
|
|
}
|
|
|
|
if (baselineRetryState.RequiresBlockingRetry || baselineRetryState.RequiresLengthRetry)
|
|
{
|
|
return Math.Min(config.RequiredValidSolutions, 3);
|
|
}
|
|
|
|
return Math.Min(config.RequiredValidSolutions, 2);
|
|
}
|
|
|
|
private static int DetermineStrategySearchBudget(
|
|
RoutingRetryState baselineRetryState,
|
|
IterativeRoutingConfig config)
|
|
{
|
|
if (!baselineRetryState.RequiresPrimaryRetry)
|
|
{
|
|
return int.MaxValue;
|
|
}
|
|
|
|
var severity = baselineRetryState.PrimaryViolationCount + baselineRetryState.EdgeCrossings;
|
|
var minimumBudget = baselineRetryState.RequiresBlockingRetry || baselineRetryState.RequiresLengthRetry
|
|
? 8
|
|
: 6;
|
|
var severityBudget = severity >= 20
|
|
? 8
|
|
: severity >= 10
|
|
? 7
|
|
: 6;
|
|
return Math.Min(
|
|
OrderingNames.Length,
|
|
Math.Max(minimumBudget, severityBudget));
|
|
}
|
|
|
|
private static bool ShouldKeepBaselineSolution(
|
|
IReadOnlyCollection<ElkRoutedEdge> baselineEdges,
|
|
IReadOnlyCollection<ElkPositionedNode> nodes,
|
|
RoutingRetryState baselineRetryState)
|
|
{
|
|
if (baselineRetryState.RepeatCollectorCorridorViolations > 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var hasProtectedEdgeContract = baselineEdges.Any(edge =>
|
|
!string.IsNullOrWhiteSpace(edge.SourcePortId)
|
|
|| !string.IsNullOrWhiteSpace(edge.TargetPortId)
|
|
|| (!string.IsNullOrWhiteSpace(edge.Kind)
|
|
&& edge.Kind.StartsWith("backward|", StringComparison.OrdinalIgnoreCase)));
|
|
|
|
if (baselineEdges.Count <= 8
|
|
|| nodes.Count <= 8
|
|
|| hasProtectedEdgeContract)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return !baselineRetryState.RequiresPrimaryRetry && baselineEdges.Count <= 12;
|
|
}
|
|
|
|
}
|