diff --git a/FlowEdge.cs b/FlowEdge.cs index cd3786bb857427bb5221c62795137fc2a6e31e28..c37eaf561425a1647643de09b1b375727b715c06 100644 --- a/FlowEdge.cs +++ b/FlowEdge.cs @@ -5,17 +5,19 @@ using QuikGraph; public class FlowEdge : Edge<FlowNode> { public double MaxFlow { get; set; } - public double CurrentFlow { get; set; } + public double Residual { get; set; } + public bool IsBackwards { get; set; } public FlowEdge(FlowNode source, FlowNode target, double maxFlow) : base(source, target) { + IsBackwards = false; MaxFlow = maxFlow; - CurrentFlow = 0; // Initially, the current flow is set to zero. + Residual = 0; } public override string ToString() { - return $"{Source.Id} -> {Target.Id} [CurrentFlow: {CurrentFlow}, MaxFlow: {MaxFlow}]"; + return $"{Source.Id} -> {Target.Id} [ResidualFlow: {Residual}, MaxFlow: {MaxFlow}]"; } } diff --git a/FordFulkersonAlgorithm.cs b/FordFulkersonAlgorithm.cs index 14145e292ad96909b86607eb72a8b67afbd9bbe9..9a3a6a82ed3a00e2e49aa06d8c2a9b9cec636cb5 100644 --- a/FordFulkersonAlgorithm.cs +++ b/FordFulkersonAlgorithm.cs @@ -5,7 +5,7 @@ namespace FlowForge; public class FordFulkersonAlgorithm { - private FlowGraph _flowGraph; + private readonly FlowGraph _flowGraph; public FordFulkersonAlgorithm(FlowGraph flowGraph) { @@ -14,22 +14,111 @@ public class FordFulkersonAlgorithm public double Run(string sourceId, string targetId) { + // get source and target nodes FlowNode? source = _flowGraph.GetVertexById(sourceId); FlowNode? target = _flowGraph.GetVertexById(targetId); if (source == null || target == null) - throw new ArgumentException("Invalid source or target node."); + throw new ArgumentException("Invalid source or target node!"); double maxFlow = 0.0; - // Implement the Ford-Fulkerson algorithm here. - // Update the CurrentFlow in each FlowEdge as necessary. + var pathFlow = new Dictionary<FlowEdge, double>(); + + + var allEdges = _flowGraph.GetEdges(); + foreach (var edge in allEdges) + { + edge.Residual = edge.MaxFlow; + } + + // execute as long as there is an augmenting path + while (FindAugmentingPath(source, target, pathFlow)) + { + // calculate bottleneck in augmenting path + double pathMinFlow = double.MaxValue; + foreach (var edge in pathFlow.Keys) + { + pathMinFlow = Math.Min(pathMinFlow, pathFlow[edge]); + } + + // add flow along the augmenting path + foreach (var edge in pathFlow.Keys) + { + edge.Residual -= pathMinFlow; + + // check if backwards edge exists + FlowEdge? reverseEdge; + if (!_flowGraph.Graph.TryGetEdge(edge.Target, edge.Source, out reverseEdge)) + { + // create a backwards edge if it doesn't exist + reverseEdge = new FlowEdge(edge.Target, edge.Source, edge.MaxFlow); + + reverseEdge.Residual = 0; + reverseEdge.IsBackwards = true; + + _flowGraph.Graph.AddEdge(reverseEdge); + } + + if (reverseEdge != null) reverseEdge.Residual += pathMinFlow; + } + + // add to total flow + maxFlow += pathMinFlow; + } + + // return maximum flow after no augmenting paths were found anymore return maxFlow; } - private bool FindAugmentingPath(FlowNode source, FlowNode target, Dictionary<FlowEdge, double> pathFlow) + + public bool FindAugmentingPath(FlowNode source, FlowNode target, Dictionary<FlowEdge, double> pathFlow) { - // Implement BFS/DFS to find an augmenting path and return true if found. - // This will be used to determine if we can push more flow through the graph. + // parent map to walk back path + var parentMap = new Dictionary<FlowNode, FlowEdge>(); + + // queue for breath first search + var queue = new Queue<FlowNode>(); + queue.Enqueue(source); + + // map to store visited nodes + var visited = new HashSet<FlowNode> { source }; + + while (queue.Count > 0) + { + FlowNode current = queue.Dequeue(); + + var outEdges = _flowGraph.Graph.OutEdges(current); + + // go through all outgoing edges + foreach (FlowEdge currentEdge in outEdges) + { + if (currentEdge.Residual <= 0 || visited.Contains(currentEdge.Target)) continue; + + visited.Add(currentEdge.Target); + parentMap.Add(currentEdge.Target, currentEdge); + + // if we reached the target node + if (currentEdge.Target.Equals(target)) + { + FlowNode currentNode = target; + pathFlow.Clear(); + + while (currentNode != source) + { + FlowEdge pathEdge = parentMap[currentNode]; + pathFlow.Add(pathEdge, pathEdge.Residual); + currentNode = pathEdge.Source; + } + + // return the augmenting path found + return true; + } + + // search further + queue.Enqueue(currentEdge.Target); + } + } + return false; } } diff --git a/GraphVisualizer.cs b/GraphVisualizer.cs index 59702b18e6589675d40c2b0807e14fdf62eb6ec9..e248c8cf6661aeb1db4a5832b12c1fe82fb97d27 100644 --- a/GraphVisualizer.cs +++ b/GraphVisualizer.cs @@ -2,9 +2,9 @@ namespace FlowForge; -public class GraphVisualizer +public static class GraphVisualizer { - public static Graph ConvertToMsaglGraph(FlowGraph flowGraph) + public static Graph ConvertToMsaglGraph(FlowGraph? flowGraph) { Graph msaglGraph = new Graph(); @@ -17,7 +17,15 @@ public class GraphVisualizer foreach (var edge in flowGraph.Graph.Edges) { var msaglEdge = msaglGraph.AddEdge(edge.Source.Id, edge.Target.Id); - msaglEdge.LabelText = $"{edge.CurrentFlow}/{edge.MaxFlow}"; + + if (edge.IsBackwards) + { + msaglEdge.LabelText = $"{0}/{edge.Residual}"; + } + else + { + msaglEdge.LabelText = $"{edge.MaxFlow-edge.Residual}/{edge.MaxFlow}"; + } } return msaglGraph; diff --git a/MainWindow.xaml b/MainWindow.xaml index 5d5691436ec0f9039bb7d97ed345cc1070e8afd7..fd8a744d84473c4205352400c86e3d4494a9a93c 100644 --- a/MainWindow.xaml +++ b/MainWindow.xaml @@ -3,6 +3,36 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="FlowForge" Height="600" Width="800"> <Grid x:Name="MainGrid"> - <!-- Der WindowsFormsHost wird im Code-Behind hinzugefügt --> + <!-- Define two rows: one fixed height for the button and one for the graph viewer --> + <Grid.RowDefinitions> + <RowDefinition Height="70"/> <!-- Fixed height row for button --> + <RowDefinition Height="*"/> <!-- Remaining space for the graph viewer --> + </Grid.RowDefinitions> + + <!-- Button in the first row --> + <Button x:Name="InitializeGraphButton" + Content="Initialize Graph" + Width="150" + Height="40" + VerticalAlignment="Center" + HorizontalAlignment="Left" + Margin="10" + Grid.Row="0" + Panel.ZIndex="1" + Click="InitializeGraphButton_Click"/> + + <!-- Second Button in the first row --> + <Button x:Name="RunAlgorithmButton" + Content="Run Ford-Fulkerson" + Width="150" + Height="40" + VerticalAlignment="Center" + HorizontalAlignment="Left" + Margin="180,10,0,10" + Grid.Row="0" + Click="RunAlgorithmButton_Click"/> + + <!-- Placeholder for graph viewer --> + <Grid x:Name="GraphViewerGrid" Grid.Row="1"/> </Grid> </Window> \ No newline at end of file diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs index 226abc5b4cf7b9ccbbc775ef4895865f0551267c..edbe1677ced621f6b7860331c29f0712de9d40a2 100644 --- a/MainWindow.xaml.cs +++ b/MainWindow.xaml.cs @@ -20,71 +20,164 @@ using Microsoft.Msagl.GraphViewerGdi; namespace FlowForge { - /// <summary> - /// Interaction logic for MainWindow.xaml - /// </summary> public partial class MainWindow : Window { private GViewer? _gViewer; + private FlowGraph? _flowGraph; + private FordFulkersonAlgorithm? _fordFulkerson; public MainWindow() { InitializeComponent(); + InitializeGraph(); SetupGraphViewer(); - DisplayGraph(); } private void SetupGraphViewer() { - // GViewer initialisieren + // GViewer initialization _gViewer = new GViewer(); - // WindowsFormsHost erstellen - var host = new WindowsFormsHost(); - - // GViewer dem Host hinzufügen - host.Child = _gViewer; + // WindowsFormsHost creation + var host = new WindowsFormsHost + { + Child = _gViewer // Attach GViewer to host + }; - // Host dem Grid hinzufügen - MainGrid.Children.Add(host); + // Add the host to the specific Grid designed for graph viewer + GraphViewerGrid.Children.Add(host); } - private void DisplayGraph() + // Event handler for "Initialize and Display Graph" button + private void InitializeGraphButton_Click(object sender, RoutedEventArgs e) { - // Graph erstellen - MsaglDrawing.Graph graph = new MsaglDrawing.Graph(); - - // Kanten hinzufügen und Beschriftungen setzen - var edge1 = graph.AddEdge("1", "2"); - edge1.LabelText = "10/10"; - - var edge2 = graph.AddEdge("2", "3"); - edge2.LabelText = "5/10"; + DisplayGraph(); + } - var edge3 = graph.AddEdge("3", "4"); - edge3.LabelText = "5/20"; + private void InitializeGraph() + { + // Initialize a FlowGraph + _flowGraph = new FlowGraph(); + + // Adding vertices to the graph + _flowGraph.AddVertex("1", "1"); + _flowGraph.AddVertex("2", "2"); + _flowGraph.AddVertex("3", "3"); + _flowGraph.AddVertex("4", "4"); + _flowGraph.AddVertex("5", "5"); + + // Adding edges with flow capacities + _flowGraph.AddEdge("1", "2", 10); + _flowGraph.AddEdge("2", "3", 5); + _flowGraph.AddEdge("3", "4", 15); + _flowGraph.AddEdge("2", "4", 5); + _flowGraph.AddEdge("2", "5", 100); + _flowGraph.AddEdge("5", "4", 2); + _flowGraph.AddEdge("1", "5", 200); + + // Initialize Ford-Fulkerson algorithm with the flow graph + _fordFulkerson = new FordFulkersonAlgorithm(_flowGraph); + } - var edge4 = graph.AddEdge("2", "4"); - edge4.LabelText = "5/20"; + private void DisplayGraph() + { + // Convert QuickGraph to MSAGL Graph + var msaglGraph = GraphVisualizer.ConvertToMsaglGraph(_flowGraph); - // Knoten Attribute setzen - MsaglDrawing.Node node1 = graph.FindNode("1"); + // Farbe für Knoten 1 und 4 festlegen + var node1 = msaglGraph.FindNode("1"); if (node1 != null) { - node1.Attr.FillColor = MsaglDrawing.Color.Green; // Node 1 grün färben + node1.Attr.FillColor = MsaglDrawing.Color.Green; // Knoten 1 wird grün } - MsaglDrawing.Node node4 = graph.FindNode("4"); + var node4 = msaglGraph.FindNode("4"); if (node4 != null) { - node4.Attr.FillColor = MsaglDrawing.Color.Red; // Node 4 rot färben + node4.Attr.FillColor = MsaglDrawing.Color.Red; // Knoten 4 wird rot } - // Graph dem GViewer zuweisen + foreach (var node in msaglGraph.Nodes) + { + node.Attr.Shape = MsaglDrawing.Shape.Circle; // Knoten als Kreis + } + + foreach (var edge in msaglGraph.Edges) + { + if (edge.Label != null) + { + edge.Label.FontSize = 4; // Setze die Schriftgröße des Kantenlabels auf 8 + } + } + + // Assign MSAGL graph to viewer for display if (_gViewer != null) { - _gViewer.Graph = graph; + _gViewer.Graph = msaglGraph; } } + + + // Event handler for "Run Ford-Fulkerson" button + private void RunAlgorithmButton_Click(object sender, RoutedEventArgs e) + { + if (_fordFulkerson == null) + { + MessageBox.Show("Please initialize the graph first by clicking the 'Initialize Graph' button!", "Error", MessageBoxButton.OK, MessageBoxImage.Error); + return; + } + + double maxFlow = _fordFulkerson.Run("1", "4"); + + Console.WriteLine($"Maximaler Fluss von Quelle zu Senke: {maxFlow}"); + + /* + // Dictionary to store the flow along the augmenting path + var pathFlow = new Dictionary<FlowEdge, double>(); + + // Try to find an augmenting path from source (1) to target (4) + bool pathFound = _fordFulkerson.FindAugmentingPath(_flowGraph.GetVertexById("1"), _flowGraph.GetVertexById("4"), pathFlow); + + if (pathFound) + { + // Find the start and end nodes + var source = _flowGraph.GetVertexById("1"); + var target = _flowGraph.GetVertexById("4"); + + Console.WriteLine("Augmenting path found:"); + + // To print in correct order, traverse the path from target to source + var pathInOrder = new List<FlowEdge>(); + var current = target; + + // We trace back from the target to the source using the edges stored in pathFlow + while (current != source) + { + var edge = pathFlow.Keys.FirstOrDefault(e => e.Target == current); + if (edge == null) continue; + + pathInOrder.Insert(0, edge); // Insert the edge at the beginning to reverse the order + current = edge.Source; // Move to the previous node + } + + // Now we print the path from source to target in order + foreach (var edge in pathInOrder) + { + Console.WriteLine($"{edge.Source.Id} -> {edge.Target.Id}, Remaining Flow: {pathFlow[edge]}"); + } + } + else + { + Console.WriteLine("No augmenting path found."); + } +*/ + + // Run the Ford-Fulkerson algorithm + //double maxFlow = _fordFulkerson.Run("1", "4"); + //MessageBox.Show($"Calculated Maximum Flow from Source to Sink: {maxFlow}"); + + // Update the visualization (optional) + DisplayGraph(); + } } -} +} \ No newline at end of file