From 4de67b014c626e8a5f22dde8ffa9f8989b6f79f1 Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Mon, 30 Sep 2024 20:35:42 +0530 Subject: [PATCH 1/9] Improve comments, add doctests to coloring.py --- backtracking/coloring.py | 103 ++++++++++++++++++++++++++++++--------- 1 file changed, 80 insertions(+), 23 deletions(-) diff --git a/backtracking/coloring.py b/backtracking/coloring.py index f10cdbcf9..e0d3c9e52 100644 --- a/backtracking/coloring.py +++ b/backtracking/coloring.py @@ -1,7 +1,7 @@ """ -Graph Coloring also called "m coloring problem" -consists of coloring a given graph with at most m colors -such that no adjacent vertices are assigned the same color +Graph Coloring (also called the "m coloring problem") is the problem of +assigning at most 'm' colors to the vertices of a graph such that +no two adjacent vertices share the same color. Wikipedia: https://en.wikipedia.org/wiki/Graph_coloring """ @@ -11,13 +11,27 @@ def valid_coloring( neighbours: list[int], colored_vertices: list[int], color: int ) -> bool: """ + This function checks if a given vertex can be assigned the specified color + without violating the graph coloring constraints (i.e., no two adjacent vertices + have the same color). + + Procedure: For each neighbour check if the coloring constraint is satisfied If any of the neighbours fail the constraint return False If all neighbours validate the constraint return True - >>> neighbours = [0,1,0,1,0] - >>> colored_vertices = [0, 2, 1, 2, 0] + Parameters: + neighbours (list[int]): The list representing which vertices are adjacent to the current vertex. + 1 indicates an edge between the current vertex and the neighbour. + colored_vertices (list[int]): List of current color assignments for all vertices (-1 means uncolored). + color (int): The color we are trying to assign to the current vertex. + Returns: + bool: True if the vertex can be safely colored with the given color, otherwise False. + + Examples: + >>> neighbours = [0, 1, 0, 1, 0] + >>> colored_vertices = [0, 2, 1, 2, 0] >>> color = 1 >>> valid_coloring(neighbours, colored_vertices, color) True @@ -25,8 +39,14 @@ def valid_coloring( >>> color = 2 >>> valid_coloring(neighbours, colored_vertices, color) False + + >>> neighbors = [1, 0, 1, 0] + >>> colored_vertices = [-1, -1, -1, -1] + >>> color = 0 + >>> valid_coloring(neighbors, colored_vertices, color) + True """ - # Does any neighbour not satisfy the constraints + # Check if any adjacent vertex has already been colored with the same color return not any( neighbour == 1 and colored_vertices[i] == color for i, neighbour in enumerate(neighbours) @@ -37,7 +57,7 @@ def util_color( graph: list[list[int]], max_colors: int, colored_vertices: list[int], index: int ) -> bool: """ - Pseudo-Code + Recursive function to try and color the graph using backtracking. Base Case: 1. Check if coloring is complete @@ -51,6 +71,18 @@ def util_color( 2.4. if current coloring leads to a solution return 2.5. Uncolor given vertex + Parameters: + graph (list of list of int): Adjacency matrix representing the graph. + graph[i][j] is 1 if there is an edge between vertex i and j. + max_colors (int): Maximum number of colors allowed (m in the m-coloring problem). + colored_vertices (list of int): Current color assignments for each vertex. + -1 indicates that the vertex has not been colored yet. + index (int): The current vertex index being processed. + + Returns: + bool: True if the graph can be colored using at most max_colors, otherwise False. + + Examples: >>> graph = [[0, 1, 0, 0, 0], ... [1, 0, 1, 0, 1], ... [0, 1, 0, 1, 0], @@ -67,36 +99,45 @@ def util_color( >>> util_color(graph, max_colors, colored_vertices, index) False """ - - # Base Case + # Base Case: If all vertices have been assigned a color, we have a valid solution if index == len(graph): return True - # Recursive Step - for i in range(max_colors): - if valid_coloring(graph[index], colored_vertices, i): - # Color current vertex - colored_vertices[index] = i - # Validate coloring + # Try each color for the current vertex + for color in range(max_colors): + # Check if it's valid to color the current vertex with 'color' + if valid_coloring(graph[index], colored_vertices, color): + colored_vertices[index] = color # Assign color + # Recur to color the rest of the vertices if util_color(graph, max_colors, colored_vertices, index + 1): return True - # Backtrack + # Backtrack if no solution found with the current assignment colored_vertices[index] = -1 - return False + + return False # Return False if no valid coloring is possible def color(graph: list[list[int]], max_colors: int) -> list[int]: """ - Wrapper function to call subroutine called util_color - which will either return True or False. - If True is returned colored_vertices list is filled with correct colorings + Attempts to color the graph with at most max_colors colors such that no two adjacent + vertices have the same color. If it is possible, returns the list of color assignments; + otherwise, returns an empty list. + Parameters: + graph (list of list of int): Adjacency matrix representing the graph. + max_colors (int): Maximum number of colors allowed. + + Returns: + list of int: List of color assignments if the graph can be colored using max_colors. + Each index in the list represents the color assigned to the corresponding vertex. + If coloring is not possible, returns an empty list. + + Examples: >>> graph = [[0, 1, 0, 0, 0], ... [1, 0, 1, 0, 1], ... [0, 1, 0, 1, 0], ... [0, 1, 1, 0, 0], ... [0, 1, 0, 0, 0]] - >>> max_colors = 3 >>> color(graph, max_colors) [0, 1, 0, 2, 0] @@ -104,10 +145,26 @@ def color(graph: list[list[int]], max_colors: int) -> list[int]: >>> max_colors = 2 >>> color(graph, max_colors) [] + + >>> graph = [[0, 1], [1, 0]] # Simple 2-node graph + >>> max_colors = 2 + >>> color(graph, max_colors) + [0, 1] + + >>> graph = [[0, 1, 1], [1, 0, 1], [1, 1, 0]] # Complete graph of 3 vertices + >>> max_colors = 2 + >>> color(graph, max_colors) + [] + + >>> max_colors = 3 + >>> color(graph, max_colors) + [0, 1, 2] """ + # Initialize all vertices as uncolored (-1) colored_vertices = [-1] * len(graph) + # Use the utility function to try and color the graph starting from vertex 0 if util_color(graph, max_colors, colored_vertices, 0): - return colored_vertices + return colored_vertices # Return the successful color assignment - return [] + return [] # Return an empty list if no valid coloring is possible From 92449bfb32a6c7ea59ec2028114d23b98b5cbf64 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 15:08:59 +0000 Subject: [PATCH 2/9] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- backtracking/coloring.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backtracking/coloring.py b/backtracking/coloring.py index e0d3c9e52..8412ee3be 100644 --- a/backtracking/coloring.py +++ b/backtracking/coloring.py @@ -1,6 +1,6 @@ """ -Graph Coloring (also called the "m coloring problem") is the problem of -assigning at most 'm' colors to the vertices of a graph such that +Graph Coloring (also called the "m coloring problem") is the problem of +assigning at most 'm' colors to the vertices of a graph such that no two adjacent vertices share the same color. Wikipedia: https://en.wikipedia.org/wiki/Graph_coloring @@ -119,7 +119,7 @@ def util_color( def color(graph: list[list[int]], max_colors: int) -> list[int]: """ - Attempts to color the graph with at most max_colors colors such that no two adjacent + Attempts to color the graph with at most max_colors colors such that no two adjacent vertices have the same color. If it is possible, returns the list of color assignments; otherwise, returns an empty list. From d38b93510629bcac7bc0ab80b655aaa537bd54fa Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Mon, 30 Sep 2024 20:41:53 +0530 Subject: [PATCH 3/9] Fix ruff errors --- backtracking/coloring.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/backtracking/coloring.py b/backtracking/coloring.py index 8412ee3be..cef143a04 100644 --- a/backtracking/coloring.py +++ b/backtracking/coloring.py @@ -21,13 +21,17 @@ def valid_coloring( If all neighbours validate the constraint return True Parameters: - neighbours (list[int]): The list representing which vertices are adjacent to the current vertex. - 1 indicates an edge between the current vertex and the neighbour. - colored_vertices (list[int]): List of current color assignments for all vertices (-1 means uncolored). + neighbours (list[int]): The list representing which vertices + are adjacent to the current vertex. + 1 indicates an edge between the current vertex + and the neighbour. + colored_vertices (list[int]): List of current color assignments for all vertices + (-1 means uncolored). color (int): The color we are trying to assign to the current vertex. Returns: - bool: True if the vertex can be safely colored with the given color, otherwise False. + bool: True if the vertex can be safely colored with the given color, + otherwise False. Examples: >>> neighbours = [0, 1, 0, 1, 0] @@ -73,10 +77,12 @@ def util_color( Parameters: graph (list of list of int): Adjacency matrix representing the graph. - graph[i][j] is 1 if there is an edge between vertex i and j. + graph[i][j] is 1 if there is an edge + between vertex i and j. max_colors (int): Maximum number of colors allowed (m in the m-coloring problem). colored_vertices (list of int): Current color assignments for each vertex. - -1 indicates that the vertex has not been colored yet. + -1 indicates that the vertex has not been colored + yet. index (int): The current vertex index being processed. Returns: @@ -120,7 +126,8 @@ def util_color( def color(graph: list[list[int]], max_colors: int) -> list[int]: """ Attempts to color the graph with at most max_colors colors such that no two adjacent - vertices have the same color. If it is possible, returns the list of color assignments; + vertices have the same color. + If it is possible, returns the list of color assignments; otherwise, returns an empty list. Parameters: @@ -129,7 +136,8 @@ def color(graph: list[list[int]], max_colors: int) -> list[int]: Returns: list of int: List of color assignments if the graph can be colored using max_colors. - Each index in the list represents the color assigned to the corresponding vertex. + Each index in the list represents the color assigned + to the corresponding vertex. If coloring is not possible, returns an empty list. Examples: From 8fae9d55a80d642823259bb0b201b80f6b428745 Mon Sep 17 00:00:00 2001 From: Hardik Pawar <97388607+Hardvan@users.noreply.github.com> Date: Tue, 1 Oct 2024 08:21:05 +0530 Subject: [PATCH 4/9] Update backtracking/coloring.py Co-authored-by: Christian Clauss --- backtracking/coloring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backtracking/coloring.py b/backtracking/coloring.py index cef143a04..497b4cac8 100644 --- a/backtracking/coloring.py +++ b/backtracking/coloring.py @@ -11,7 +11,7 @@ def valid_coloring( neighbours: list[int], colored_vertices: list[int], color: int ) -> bool: """ - This function checks if a given vertex can be assigned the specified color + Check if a given vertex can be assigned the specified color without violating the graph coloring constraints (i.e., no two adjacent vertices have the same color). From 9da28b0ccc25efff2f3ad774a90511491c9943ff Mon Sep 17 00:00:00 2001 From: Hardik Pawar <97388607+Hardvan@users.noreply.github.com> Date: Tue, 1 Oct 2024 08:21:52 +0530 Subject: [PATCH 5/9] Update backtracking/coloring.py Co-authored-by: Christian Clauss --- backtracking/coloring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backtracking/coloring.py b/backtracking/coloring.py index 497b4cac8..d7282a9e9 100644 --- a/backtracking/coloring.py +++ b/backtracking/coloring.py @@ -21,7 +21,7 @@ def valid_coloring( If all neighbours validate the constraint return True Parameters: - neighbours (list[int]): The list representing which vertices + neighbours: The list representing which vertices are adjacent to the current vertex. 1 indicates an edge between the current vertex and the neighbour. From 42c32e7c4e3971e94b46ded2fb8e6bec410a02ef Mon Sep 17 00:00:00 2001 From: Hardik Pawar <97388607+Hardvan@users.noreply.github.com> Date: Tue, 1 Oct 2024 08:22:15 +0530 Subject: [PATCH 6/9] Update backtracking/coloring.py Co-authored-by: Christian Clauss --- backtracking/coloring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backtracking/coloring.py b/backtracking/coloring.py index d7282a9e9..ae489f9e3 100644 --- a/backtracking/coloring.py +++ b/backtracking/coloring.py @@ -125,7 +125,7 @@ def util_color( def color(graph: list[list[int]], max_colors: int) -> list[int]: """ - Attempts to color the graph with at most max_colors colors such that no two adjacent + Attempt to color the graph with at most max_colors colors such that no two adjacent vertices have the same color. If it is possible, returns the list of color assignments; otherwise, returns an empty list. From 7fafb02c4a4c5f9da9031a0f07d6a41fd6e12e56 Mon Sep 17 00:00:00 2001 From: Hardik Pawar <97388607+Hardvan@users.noreply.github.com> Date: Tue, 1 Oct 2024 08:22:29 +0530 Subject: [PATCH 7/9] Update backtracking/coloring.py Co-authored-by: Christian Clauss --- backtracking/coloring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backtracking/coloring.py b/backtracking/coloring.py index ae489f9e3..0a9a1139f 100644 --- a/backtracking/coloring.py +++ b/backtracking/coloring.py @@ -173,6 +173,6 @@ def color(graph: list[list[int]], max_colors: int) -> list[int]: # Use the utility function to try and color the graph starting from vertex 0 if util_color(graph, max_colors, colored_vertices, 0): - return colored_vertices # Return the successful color assignment + return colored_vertices # The successful color assignment return [] # Return an empty list if no valid coloring is possible From 26321da52fa2ca9048d6b313ef978c988e4e1656 Mon Sep 17 00:00:00 2001 From: Hardik Pawar <97388607+Hardvan@users.noreply.github.com> Date: Tue, 1 Oct 2024 08:22:36 +0530 Subject: [PATCH 8/9] Update backtracking/coloring.py Co-authored-by: Christian Clauss --- backtracking/coloring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backtracking/coloring.py b/backtracking/coloring.py index 0a9a1139f..2fa30ec6b 100644 --- a/backtracking/coloring.py +++ b/backtracking/coloring.py @@ -175,4 +175,4 @@ def color(graph: list[list[int]], max_colors: int) -> list[int]: if util_color(graph, max_colors, colored_vertices, 0): return colored_vertices # The successful color assignment - return [] # Return an empty list if no valid coloring is possible + return [] # No valid coloring is possible From 156f8fe5fd4aacd8fed499cbf85c7a424988f4fe Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Tue, 1 Oct 2024 15:26:34 +0530 Subject: [PATCH 9/9] Remove type hints for function docstrings --- backtracking/coloring.py | 46 ++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/backtracking/coloring.py b/backtracking/coloring.py index 2fa30ec6b..79fafed19 100644 --- a/backtracking/coloring.py +++ b/backtracking/coloring.py @@ -22,16 +22,16 @@ def valid_coloring( Parameters: neighbours: The list representing which vertices - are adjacent to the current vertex. - 1 indicates an edge between the current vertex - and the neighbour. - colored_vertices (list[int]): List of current color assignments for all vertices - (-1 means uncolored). - color (int): The color we are trying to assign to the current vertex. + are adjacent to the current vertex. + 1 indicates an edge between the current vertex + and the neighbour. + colored_vertices: List of current color assignments for all vertices + (-1 means uncolored). + color: The color we are trying to assign to the current vertex. Returns: - bool: True if the vertex can be safely colored with the given color, - otherwise False. + True if the vertex can be safely colored with the given color, + otherwise False. Examples: >>> neighbours = [0, 1, 0, 1, 0] @@ -76,17 +76,17 @@ def util_color( 2.5. Uncolor given vertex Parameters: - graph (list of list of int): Adjacency matrix representing the graph. - graph[i][j] is 1 if there is an edge - between vertex i and j. - max_colors (int): Maximum number of colors allowed (m in the m-coloring problem). - colored_vertices (list of int): Current color assignments for each vertex. - -1 indicates that the vertex has not been colored - yet. - index (int): The current vertex index being processed. + graph: Adjacency matrix representing the graph. + graph[i][j] is 1 if there is an edge + between vertex i and j. + max_colors: Maximum number of colors allowed (m in the m-coloring problem). + colored_vertices: Current color assignments for each vertex. + -1 indicates that the vertex has not been colored + yet. + index: The current vertex index being processed. Returns: - bool: True if the graph can be colored using at most max_colors, otherwise False. + True if the graph can be colored using at most max_colors, otherwise False. Examples: >>> graph = [[0, 1, 0, 0, 0], @@ -131,14 +131,14 @@ def color(graph: list[list[int]], max_colors: int) -> list[int]: otherwise, returns an empty list. Parameters: - graph (list of list of int): Adjacency matrix representing the graph. - max_colors (int): Maximum number of colors allowed. + graph: Adjacency matrix representing the graph. + max_colors: Maximum number of colors allowed. Returns: - list of int: List of color assignments if the graph can be colored using max_colors. - Each index in the list represents the color assigned - to the corresponding vertex. - If coloring is not possible, returns an empty list. + List of color assignments if the graph can be colored using max_colors. + Each index in the list represents the color assigned + to the corresponding vertex. + If coloring is not possible, returns an empty list. Examples: >>> graph = [[0, 1, 0, 0, 0],