diff --git a/BreadthFirstSearchStrategy.cs b/BreadthFirstSearchStrategy.cs index 6637dc4e455777f73ed97bcebbb320cad1fb82d8..9a7d624c1839e79cfa600d3617144c73778465a6 100644 --- a/BreadthFirstSearchStrategy.cs +++ b/BreadthFirstSearchStrategy.cs @@ -6,7 +6,7 @@ namespace FlowForge; public class BreadthFirstSearchStrategy : ISearchStrategy { - public bool FindAugmentingPath(FlowGraph flowGraph, FlowNode source, FlowNode target, Dictionary<FlowEdge, double> pathFlow, bool forceWorstCase = false) + public bool FindAugmentingPath(FlowGraph flowGraph, FlowNode source, FlowNode target, Dictionary<FlowEdge, double> pathFlow, FlowEdge? specialEdge = null) { Console.WriteLine("Breadth Depth First Search..."); // parent map to walk back path @@ -25,7 +25,7 @@ public class BreadthFirstSearchStrategy : ISearchStrategy Console.WriteLine("Current Node: " + current); var outEdges = flowGraph.Graph.OutEdges(current).ToList(); - List<FlowEdge> sortedOutEdges = forceWorstCase ? outEdges.OrderBy(edge => edge.Residual).ToList() : outEdges.OrderBy(edge => int.Parse(edge.Target.Id)).ToList(); + List<FlowEdge> sortedOutEdges = outEdges.OrderBy(edge => int.Parse(edge.Target.Id)).ToList(); Console.WriteLine("Sorted out edges for node:"); foreach (FlowEdge edge in sortedOutEdges) diff --git a/DepthFirstSearchStrategy.cs b/DepthFirstSearchStrategy.cs index c55105d458d063a4c46648364f9db56a7a63a5a2..4235b3c9d82a6992c2ba224fe677c493c136af4c 100644 --- a/DepthFirstSearchStrategy.cs +++ b/DepthFirstSearchStrategy.cs @@ -6,31 +6,15 @@ namespace FlowForge; public class DepthFirstSearchStrategy : ISearchStrategy { - public bool FindAugmentingPath(FlowGraph flowGraph, FlowNode source, FlowNode target, Dictionary<FlowEdge, double> pathFlow, bool forceWorstCase = false) + public bool FindAugmentingPath(FlowGraph flowGraph, FlowNode source, FlowNode target, Dictionary<FlowEdge, double> pathFlow, FlowEdge? specialEdge = null) { try { - if (forceWorstCase) + if (specialEdge != null) { - // Attempt to retrieve the nodes by ID - FlowNode? node2 = flowGraph.GetVertexById("2"); - FlowNode? node3 = flowGraph.GetVertexById("3"); - - // Validate that the nodes were found - if (node2 == null || node3 == null) - { - throw new InvalidOperationException("One or more required vertices (2 or 3) could not be found in the graph."); - } - - // Attempt to find the edge between node2 and node3 - if (!flowGraph.Graph.TryGetEdge(node2, node3, out var edgeFrom2To3) || edgeFrom2To3 == null) - { - throw new InvalidOperationException("The edge between node2 and node3 does not exist."); - } - // Use the worst-case search to find the augmenting path WorstCaseSearch worstCaseSearch = new WorstCaseSearch(); - return worstCaseSearch.FindAugmentingPathWithEdge(flowGraph, source, target, edgeFrom2To3, pathFlow); + return WorstCaseSearch.FindAugmentingPathWithEdge(flowGraph, source, target, specialEdge, pathFlow); } } catch (Exception e) diff --git a/FordFulkersonAlgorithm.cs b/FordFulkersonAlgorithm.cs index 4c9589c9b32b657c986dc4235d5694c4560f2076..a2a8b1b3b56e3f7859735cbcfea4423d3f4dbd48 100644 --- a/FordFulkersonAlgorithm.cs +++ b/FordFulkersonAlgorithm.cs @@ -26,7 +26,7 @@ public class FordFulkersonAlgorithm this.TargetId = targetId; } - public double Run(ISearchStrategy strategy, bool forceWorstCase = false) + public double Run(ISearchStrategy strategy, FlowEdge? specialEdge = null) { // get source and target nodes FlowNode? source = _flowGraph.GetVertexById(SourceId); @@ -37,9 +37,10 @@ public class FordFulkersonAlgorithm throw new ArgumentException("Invalid source or target node!"); } Console.WriteLine("Start of Ford-Fulkerson Algorithm..."); - if (forceWorstCase) + + if (specialEdge != null) { - Console.WriteLine("Using worst case scenario for edge selection."); + Console.WriteLine("Using worst case scenario for edge selection with special edge " + specialEdge); } double maxFlow = 0.0; @@ -51,7 +52,7 @@ public class FordFulkersonAlgorithm SaveGraphState(); // execute as long as there is an augmenting path - while (strategy.FindAugmentingPath(_flowGraph, source, target, pathFlow, forceWorstCase)) + while (strategy.FindAugmentingPath(_flowGraph, source, target, pathFlow, specialEdge)) { PrintOrderedPath(pathFlow); @@ -140,12 +141,18 @@ public class FordFulkersonAlgorithm { Console.WriteLine("Augmenting Path found:"); + if (!pathFlow.Any()) + { + Console.WriteLine("pathFlow is empty."); + return; + } + // Step 1: Find the starting edge FlowEdge startEdge = pathFlow.Keys - .First(edge => !pathFlow.Keys.Any(e => e.Target.Id == edge.Source.Id)); + .First(edge => pathFlow.Keys.All(e => e.Target.Id != edge.Source.Id)); // Step 2: Follow the path from start to end - List<FlowEdge> orderedPath = new List<FlowEdge> { startEdge }; + var orderedPath = new List<FlowEdge> { startEdge }; while (true) { var currentEdge = orderedPath.Last(); diff --git a/ISearchStrategy.cs b/ISearchStrategy.cs index 2ed45734d3d464199734128995fcecb29e142514..8bd1001d659318a9695deb5c427a00930eaf2308 100644 --- a/ISearchStrategy.cs +++ b/ISearchStrategy.cs @@ -4,7 +4,7 @@ namespace FlowForge; public interface ISearchStrategy { - bool FindAugmentingPath(FlowGraph flowGraph, FlowNode source, FlowNode target, Dictionary<FlowEdge, double> pathFlow, bool forceWorstCase = false); + bool FindAugmentingPath(FlowGraph flowGraph, FlowNode source, FlowNode target, Dictionary<FlowEdge, double> pathFlow, FlowEdge? specialEdge = null); public static void BuildAugmentingPath(FlowNode source, FlowNode target, Dictionary<FlowNode, FlowEdge> parentMap, Dictionary<FlowEdge, double> pathFlow) { diff --git a/MainWindow.xaml b/MainWindow.xaml index 6626861622aa6488902dad9412ef097377d9223b..276b2e36d215b0c58f7d784e720fd483a5cbb255 100644 --- a/MainWindow.xaml +++ b/MainWindow.xaml @@ -1,7 +1,7 @@ <Window x:Class="FlowForge.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - Title="Flow-Forge" Height="600" Width="1080"> + Title="Flow-Forge" Height="600" Width="1150"> <Grid x:Name="MainGrid"> <!-- Define two rows: one fixed height for the button and one for the graph viewer --> <Grid.RowDefinitions> @@ -84,9 +84,27 @@ Content="Force worst case" VerticalAlignment="Center" HorizontalAlignment="Left" - Margin="880,-15,0,10" + Margin="885,-15,0,10" Grid.Row="0" - IsChecked="True"/> + IsChecked="False" + Checked="WorstCaseCheckbox_Checked" + Unchecked="WorstCaseCheckbox_Unchecked"/> + + <StackPanel x:Name="EdgeDefinitionStackPanel" + Orientation="Horizontal" + VerticalAlignment="Top" + HorizontalAlignment="Left" + Margin="880,30,0,0" + Grid.Row="0" + Visibility="Collapsed"> <!-- Initially hidden --> + <TextBlock Text="Source:" VerticalAlignment="Center" Margin="5"/> + <TextBox x:Name="SourceNodeInput" Width="50" Margin="5"/> + + <TextBlock Text="Target:" VerticalAlignment="Center" Margin="5"/> + <TextBox x:Name="TargetNodeInput" Width="50" Margin="5"/> + </StackPanel> + + <!-- Placeholder for graph viewer --> <Grid x:Name="GraphViewerGrid" Grid.Row="1"/> diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs index 062db1162ff5cfcbf9e8ea532334d5e8bbb30d00..5e691bae59a55e2f4732b3c00115f01170d76332 100644 --- a/MainWindow.xaml.cs +++ b/MainWindow.xaml.cs @@ -28,6 +28,8 @@ namespace FlowForge private MsaglDrawing.Graph _msaglGraph = new MsaglDrawing.Graph(); private bool _isInitialized = false; + private FlowEdge? _specialEdge = null; + public MainWindow() { @@ -76,6 +78,8 @@ namespace FlowForge // Display the graph after initialization DisplayGraph(); + + SnapToGridButton_Click(sender, e); // Snap nodes to grid } private void InitializeGraphFromFile(string filePath) @@ -154,9 +158,9 @@ namespace FlowForge } //SaveNodePositions(_gViewer.Graph); // Save positions before running the algorithm - - bool forceWorstCase = WorstCaseCheckbox.IsChecked ?? false; - double maxFlow = _fordFulkerson.Run(new DepthFirstSearchStrategy(), forceWorstCase); + + AssignSpecialEdge(); + double maxFlow = _fordFulkerson.Run(new DepthFirstSearchStrategy(), _specialEdge); _isInitialized = true; DisplayGraph(); @@ -176,7 +180,7 @@ namespace FlowForge SaveNodePositions(_gViewer?.Graph); // Save positions before running the algorithm bool forceWorstCase = WorstCaseCheckbox.IsChecked ?? false; - double maxFlow = _fordFulkerson.Run(new BreadthFirstSearchStrategy(), forceWorstCase); + double maxFlow = _fordFulkerson.Run(new BreadthFirstSearchStrategy()); _isInitialized = true; DisplayGraph(); @@ -386,5 +390,55 @@ namespace FlowForge _gViewer.Graph = _msaglGraph; } } + + private void WorstCaseCheckbox_Checked(object sender, RoutedEventArgs e) + { + Console.WriteLine("Force worst case enabled."); + EdgeDefinitionStackPanel.Visibility = Visibility.Visible; // Show the StackPanel + } + + private void WorstCaseCheckbox_Unchecked(object sender, RoutedEventArgs e) + { + Console.WriteLine("Force worst case disabled."); + EdgeDefinitionStackPanel.Visibility = Visibility.Collapsed; // Hide the StackPanel + } + + private void AssignSpecialEdge() + { + // Get the source and target from the text boxes + string sourceNodeId = SourceNodeInput.Text.Trim(); + string targetNodeId = TargetNodeInput.Text.Trim(); + + // Validate inputs + if (string.IsNullOrEmpty(sourceNodeId) || string.IsNullOrEmpty(targetNodeId)) + { + Console.WriteLine("Source or target node ID is empty."); + _specialEdge = null; + return; + } + + // Retrieve the source and target nodes from the graph + FlowNode? sourceNode = _flowGraph.GetVertexById(sourceNodeId); + FlowNode? targetNode = _flowGraph.GetVertexById(targetNodeId); + + if (sourceNode == null || targetNode == null) + { + Console.WriteLine($"Could not find one or both nodes: Source ({sourceNodeId}), Target ({targetNodeId})."); + _specialEdge = null; + return; + } + + // Find the edge between source and target + if (!_flowGraph.Graph.TryGetEdge(sourceNode, targetNode, out var edge) || edge == null) + { + Console.WriteLine($"The edge between {sourceNodeId} and {targetNodeId} does not exist."); + _specialEdge = null; + return; + } + + // Assign the edge to the specialEdge field + _specialEdge = edge; + Console.WriteLine($"Special edge assigned: {_specialEdge}"); + } } } \ No newline at end of file diff --git a/WorstCaseSearch.cs b/WorstCaseSearch.cs index 8064ecb7ea4e915dc1f05372557ad56a5dda2b6a..1380eec8e988af61e0474a49e19aea9db695725a 100644 --- a/WorstCaseSearch.cs +++ b/WorstCaseSearch.cs @@ -6,11 +6,10 @@ namespace FlowForge; public class WorstCaseSearch { - public bool FindAugmentingPathWithEdge(FlowGraph flowGraph, FlowNode source, FlowNode target, FlowEdge requiredEdge, Dictionary<FlowEdge, double> pathFlow) + public static bool FindAugmentingPathWithEdge(FlowGraph flowGraph, FlowNode source, FlowNode target, FlowEdge requiredEdge, Dictionary<FlowEdge, double> pathFlow) { // Determine if the required edge is valid in either direction FlowEdge? validEdge; - bool isReversed = false; if (requiredEdge.Residual > 0) { @@ -21,7 +20,6 @@ public class WorstCaseSearch { if (validEdge.Residual > 0) { - isReversed = true; Console.WriteLine($"Searching for augmenting path that includes edge {requiredEdge.Target.Id} -> {requiredEdge.Source.Id}"); } else @@ -42,7 +40,7 @@ public class WorstCaseSearch // Phase 1: Find a path from source to the starting node of the valid edge var phase1Path = new Dictionary<FlowEdge, double>(); - var phase1Success = FindAugmentingPath(flowGraph, source, validEdge.Source, phase1Path); + bool phase1Success = FindAugmentingPath(flowGraph, source, validEdge.Source, phase1Path); if (!phase1Success) { @@ -63,7 +61,7 @@ public class WorstCaseSearch // Phase 2: Find a path from the ending node of the valid edge to the target var phase2Path = new Dictionary<FlowEdge, double>(); - var phase2Success = FindAugmentingPath(flowGraph, validEdge.Target, target, phase2Path); + var phase2Success = FindAugmentingPath(updatedGraph, validEdge.Target, target, phase2Path); if (!phase2Success) { @@ -71,6 +69,21 @@ public class WorstCaseSearch return FindAugmentingPath(flowGraph, source, target, pathFlow); } + Console.WriteLine("Phase 1 Path:"); + foreach (var kvp in phase1Path) + { + Console.WriteLine($"Edge: {kvp.Key.Source.Id} -> {kvp.Key.Target.Id}, Capacity: {kvp.Value}"); + } + + Console.WriteLine($"Valid Edge: {validEdge.Source.Id} -> {validEdge.Target.Id}, Residual: {validEdge.Residual}"); + + Console.WriteLine("Phase 2 Path:"); + foreach (var kvp in phase2Path) + { + Console.WriteLine($"Edge: {kvp.Key.Source.Id} -> {kvp.Key.Target.Id}, Capacity: {kvp.Value}"); + } + + // Combine the results pathFlow.Clear(); foreach (var kvp in phase1Path) @@ -101,9 +114,14 @@ public class WorstCaseSearch } } - public bool FindAugmentingPath(FlowGraph flowGraph, FlowNode source, FlowNode target, Dictionary<FlowEdge, double> pathFlow, bool forceWorstCase = false) + private static bool FindAugmentingPath(FlowGraph flowGraph, FlowNode source, FlowNode target, Dictionary<FlowEdge, double> pathFlow) { Console.WriteLine($"Searching for sub-augmenting path from {source.Id} -> {target.Id}"); + + if (source.Equals(target)) + { + return true; + } // parent map to walk back path var parentMap = new Dictionary<FlowNode, FlowEdge>(); @@ -120,7 +138,7 @@ public class WorstCaseSearch Console.WriteLine("Current Node: " + current); var outEdges = flowGraph.Graph.OutEdges(current).ToList(); - List<FlowEdge> sortedOutEdges = forceWorstCase ? outEdges.OrderBy(edge => edge.Residual).ToList() : outEdges.OrderBy(edge => int.Parse(edge.Target.Id)).ToList(); + List<FlowEdge> sortedOutEdges = outEdges.OrderBy(edge => int.Parse(edge.Target.Id)).ToList(); Console.WriteLine("Sorted out edges for node:"); foreach (FlowEdge edge in sortedOutEdges)