public PathFinderJob( PathFinderJobSpec spec, IEnumerable <byte[]> paths = null, IDictionary <byte[], int> nodeCounts = null) { _name = spec.Name; _tableau = spec.Tableau; _startPoint = spec.StartPoint; _endPoints = new HashSet <TerminalNode>(spec.EndPoints); _threadCount = spec.ThreadCount; _monitorInterval = spec.MonitorInterval; _saveFilePath = Configuration.Filename(_tableau.Shape, _name); _equivalentPathsLookup = _endPoints.ToDictionary( endPoint => endPoint, endPoint => _tableau.Shape.GetEquivalentPathsDelegate(new NodePair(_startPoint, endPoint))); _paths = paths is not null ? new ConcurrentHashSet <byte[]>(paths, new ByteArrayEqualityComparer()) : new ConcurrentHashSet <byte[]>(new ByteArrayEqualityComparer()); _nodeCounts = nodeCounts is not null ? new ConcurrentDictionary <byte[], int>(nodeCounts, new ByteSequenceEqualEqualityComparer()) : new ConcurrentDictionary <byte[], int>(new ByteSequenceEqualEqualityComparer()); }
public static async Task Main(string[] args) { Trace.Listeners.Add(new TextWriterTraceListener(Console.Out)); if (!Directory.Exists(Configuration.FolderName)) { Directory.CreateDirectory(Configuration.FolderName); } var shape = GetShape(args); var tableau = shape.CreateTableau(); var concurrency = GetConcurrency(args); var persistInterval = GetPersistInterval(args); var terminalNodePairs = tableau.TerminalNodeUniqueCombinations() .GroupBy(nodePair => (TerminalNode)nodePair.Node1) .Select(grp => ( startNode: grp.Key, endNodes: grp.Select(node => (TerminalNode)node.Node2).ToList())) .ToList(); foreach (var(startNode, endNodes) in terminalNodePairs) { Trace.WriteLine($"{startNode.Index}: {string.Join(", ", endNodes.Select(node => node.Index))}"); } var jobs = new List <(PathFinderJob, PathFinderState)>(); foreach (var(startNode, endNodes) in terminalNodePairs) { var jobAdded = false; var jobSpec = new PathFinderJobSpec { Name = startNode.Index.ToString(), Tableau = tableau, StartPoint = startNode, EndPoints = endNodes, ThreadCount = concurrency, MonitorInterval = persistInterval }; if (persistInterval > TimeSpan.Zero) { var saveFilePath = Configuration.Filename(shape, jobSpec.Name); Trace.WriteLine($"Save file path: {saveFilePath}"); if (File.Exists(saveFilePath)) { var savedState = RouteStateReaderWriter.ReadFromFile(saveFilePath); if (savedState.Shape != shape.Name) { throw new Exception($"The shape {savedState.Shape} specfied in file '{saveFilePath}' does not match the expected value {shape.Name}."); } if (savedState.Size != shape.Size) { throw new Exception($"The size {savedState.Size} specfied in file '{saveFilePath}' does not match the expected value {shape.Size}."); } var endPoints = savedState.TerminalNodes .Select(index => tableau.TerminalNodes[index]) .ToList(); var steps = new List <Step>(); var queue = new Queue <(Step step, int id)>(); var items = savedState.Steps.Where(x => x.PreviousId == 0).ToList(); foreach (var item in items) { var step = new Step( node: tableau.Nodes[item.Position], direction: new Direction(item.Direction)); queue.Enqueue((step, item.Id)); } while (queue.Any()) { var(previousStep, id) = queue.Dequeue(); items = savedState.Steps.Where(x => x.PreviousId == id).ToList(); if (items.Count == 0) { steps.Add(previousStep); continue; } foreach (var item in items) { var direction = new Direction(item.Direction); var twist = direction - previousStep.Direction; var step = new Step( node: tableau.Nodes[item.Position], direction: direction, twist: twist, previous: previousStep); queue.Enqueue((step, item.Id)); } } var pathFinderJob = new PathFinderJob(jobSpec); //TODO: paths var pathFinderState = new PathFinderState { Name = jobSpec.Name, Steps = steps, Progress = savedState.Progress }; jobs.Add((pathFinderJob, pathFinderState)); jobAdded = true; } } if (!jobAdded) { var pathFinderJob = new PathFinderJob(jobSpec); var pathFinderState = new PathFinderState { Name = jobSpec.Name, Steps = startNode.Links.Select(link => new Step(link.Value, link.Key)).ToList(), Progress = new Progress() }; jobs.Add((pathFinderJob, pathFinderState)); } } var tokenSource = new CancellationTokenSource(); var tasks = new List <Task <(IDictionary <byte[], int>, PathFinderState)> >(); foreach (var(pathFinderJob, pathFinderState) in jobs) { tasks.Add(pathFinderJob.ExploreAsync(pathFinderState, tokenSource.Token)); } await Task.WhenAll(tasks); var combinedElapsedTime = TimeSpan.Zero; var combinedRouteCount = 0L; var combinedNodeCounts = new Dictionary <byte[], int>(new ByteSequenceEqualEqualityComparer()); foreach (var task in tasks) { var(nodeCounts, pathFinderState) = task.Result; foreach (var nodeCount in nodeCounts) { if (!combinedNodeCounts.ContainsKey(nodeCount.Key)) { combinedNodeCounts.Add(nodeCount.Key, 0); } combinedNodeCounts[nodeCount.Key] += nodeCount.Value; } var uniqueSolutionCount = nodeCounts.Count(item => item.Value == 1); combinedRouteCount += pathFinderState.Progress.RouteCount; if (pathFinderState.Progress.ElapsedTime > combinedElapsedTime) { combinedElapsedTime = pathFinderState.Progress.ElapsedTime; } Trace.WriteLine($"Result for job {pathFinderState.Name}: routes = {pathFinderState.Progress.RouteCount}, distinct solutions = {nodeCounts.Count}, unique solutions = {uniqueSolutionCount}, elapsed time = {pathFinderState.Progress.ElapsedTime}"); } var combinedUniqueSolutionCount = combinedNodeCounts.Count(item => item.Value == 1); Trace.WriteLine($"Overall result: routes = {combinedRouteCount}, distinct solutions = {combinedNodeCounts.Count}, unique solutions = {combinedUniqueSolutionCount}, elapsed time = {combinedElapsedTime}"); }