From 3ceccfb59cec84f5871ad5efd31a3e43d42867b7 Mon Sep 17 00:00:00 2001 From: VarshiniShreeV Date: Sun, 27 Oct 2024 00:21:12 +0530 Subject: [PATCH 01/13] Fixed --- sorts/topological_sort.py | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/sorts/topological_sort.py b/sorts/topological_sort.py index efce8165fcac..7613d07b2d6c 100644 --- a/sorts/topological_sort.py +++ b/sorts/topological_sort.py @@ -1,10 +1,3 @@ -"""Topological Sort.""" - -# a -# / \ -# b c -# / \ -# d e edges: dict[str, list[str]] = { "a": ["c", "b"], "b": ["d", "e"], @@ -14,28 +7,16 @@ } vertices: list[str] = ["a", "b", "c", "d", "e"] - def topological_sort(start: str, visited: list[str], sort: list[str]) -> list[str]: - """Perform topological sort on a directed acyclic graph.""" + visited.append(start) current = start - # add current to visited - visited.append(current) - neighbors = edges[current] - for neighbor in neighbors: - # if neighbor not in visited, visit + for neighbor in edges[start]: if neighbor not in visited: - sort = topological_sort(neighbor, visited, sort) - # if all neighbors visited add current to sort + topological_sort(neighbor, visited, sort) sort.append(current) - # if all vertices haven't been visited select a new one to visit - if len(visited) != len(vertices): - for vertice in vertices: - if vertice not in visited: - sort = topological_sort(vertice, visited, sort) - # return sort return sort - if __name__ == "__main__": sort = topological_sort("a", [], []) + sort.reverse() #Top down approach print(sort) From e321b1e444c55c6059689dcfe6b17127b916c4ff Mon Sep 17 00:00:00 2001 From: VarshiniShreeV Date: Sun, 27 Oct 2024 12:46:59 +0530 Subject: [PATCH 02/13] Added TSP --- travelling_salesman_problem.py | 226 +++++++++++++++++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 travelling_salesman_problem.py diff --git a/travelling_salesman_problem.py b/travelling_salesman_problem.py new file mode 100644 index 000000000000..70f6cf637d70 --- /dev/null +++ b/travelling_salesman_problem.py @@ -0,0 +1,226 @@ +""" Travelling Salesman Problem (TSP) """ + +import itertools +import math + +class InvalidGraphError(ValueError): + """Custom error for invalid graph inputs.""" + +def euclidean_distance(point1: list[float], point2: list[float]) -> float: + """ + Calculate the Euclidean distance between two points in 2D space. + + :param point1: Coordinates of the first point [x, y] + :param point2: Coordinates of the second point [x, y] + :return: The Euclidean distance between the two points + + >>> euclidean_distance([0, 0], [3, 4]) + 5.0 + >>> euclidean_distance([1, 1], [1, 1]) + 0.0 + >>> euclidean_distance([1, 1], ['a', 1]) + Traceback (most recent call last): + ... + ValueError: Invalid input: Points must be numerical coordinates + """ + try: + return math.sqrt((point2[0] - point1[0]) ** 2 + (point2[1] - point1[1]) ** 2) + except TypeError: + raise ValueError("Invalid input: Points must be numerical coordinates") + +def validate_graph(graph_points: dict[str, list[float]]) -> None: + """ + Validate the input graph to ensure it has valid nodes and coordinates. + + :param graph_points: A dictionary where the keys are node names, + and values are 2D coordinates as [x, y] + :raises InvalidGraphError: If the graph points are not valid + + >>> validate_graph({"A": [10, 20], "B": [30, 21], "C": [15, 35]}) # Valid graph + >>> validate_graph({"A": [10, 20], "B": [30, "invalid"], "C": [15, 35]}) + Traceback (most recent call last): + ... + InvalidGraphError: Each node must have a valid 2D coordinate [x, y] + + >>> validate_graph([10, 20]) # Invalid input type + Traceback (most recent call last): + ... + InvalidGraphError: Graph must be a dictionary with node names and coordinates + + >>> validate_graph({"A": [10, 20], "B": [30, 21], "C": [15]}) # Missing coordinate + Traceback (most recent call last): + ... + InvalidGraphError: Each node must have a valid 2D coordinate [x, y] + """ + if not isinstance(graph_points, dict): + raise InvalidGraphError( + "Graph must be a dictionary with node names and coordinates" + ) + + for node, coordinates in graph_points.items(): + if ( + not isinstance(node, str) + or not isinstance(coordinates, list) + or len(coordinates) != 2 + or not all(isinstance(c, (int, float)) for c in coordinates) + ): + raise InvalidGraphError("Each node must have a valid 2D coordinate [x, y]") + +# TSP in Brute Force Approach +def travelling_salesman_brute_force( + graph_points: dict[str, list[float]], +) -> tuple[list[str], float]: + """ + Solve the Travelling Salesman Problem using brute force. + + :param graph_points: A dictionary of nodes and their coordinates {node: [x, y]} + :return: The shortest path and its total distance + + >>> graph = {"A": [10, 20], "B": [30, 21], "C": [15, 35]} + >>> travelling_salesman_brute_force(graph) + (['A', 'C', 'B', 'A'], 56.35465722402587) + """ + validate_graph(graph_points) + + nodes = list(graph_points.keys()) # Extracting the node names (keys) + + # There shoukd be atleast 2 nodes for a valid TSP + if len(nodes) < 2: + raise InvalidGraphError("Graph must have at least two nodes") + + min_path = [] # List that stores shortest path + min_distance = float("inf") # Initialize minimum distance to infinity + + start_node = nodes[0] + other_nodes = nodes[1:] + + # Iterating over all permutations of the other nodes + for perm in itertools.permutations(other_nodes): + path = [start_node, *perm, start_node] + + # Calculating the total distance + total_distance = sum( + euclidean_distance(graph_points[path[i]], graph_points[path[i + 1]]) + for i in range(len(path) - 1) + ) + + # Update minimum distance if shorter path found + if total_distance < min_distance: + min_distance = total_distance + min_path = path + + return min_path, min_distance + +# TSP in Dynamic Programming approach +def travelling_salesman_dynamic_programming( + graph_points: dict[str, list[float]], +) -> tuple[list[str], float]: + """ + Solve the Travelling Salesman Problem using dynamic programming. + + :param graph_points: A dictionary of nodes and their coordinates {node: [x, y]} + :return: The shortest path and its total distance + + >>> graph = {"A": [10, 20], "B": [30, 21], "C": [15, 35]} + >>> travelling_salesman_dynamic_programming(graph) + (['A', 'C', 'B', 'A'], 56.35465722402587) + """ + validate_graph(graph_points) + + n = len(graph_points) # Extracting the node names (keys) + + # There shoukd be atleast 2 nodes for a valid TSP + if n < 2: + raise InvalidGraphError("Graph must have at least two nodes") + + nodes = list(graph_points.keys()) # Extracting the node names (keys) + + # Initialize distance matrix with float values + dist = [[euclidean_distance(graph_points[nodes[i]], graph_points[nodes[j]]) for j in range(n)] for i in range(n)] + + # Initialize a dynamic programming table with infinity + dp = [[float("inf")] * n for _ in range(1 << n)] + dp[1][0] = 0 # Only visited node is the starting point at node 0 + + # Iterate through all masks of visited nodes + for mask in range(1 << n): + for u in range(n): + # If current node 'u' is visited + if mask & (1 << u): + # Traverse nodes 'v' such that u->v + for v in range(n): + if mask & (1 << v) == 0: # If v is not visited + next_mask = mask | (1 << v) # Upodate mask to include 'v' + # Update dynamic programming table with minimum distance + dp[next_mask][v] = min(dp[next_mask][v], dp[mask][u] + dist[u][v]) + + final_mask = (1 << n) - 1 + min_cost = float("inf") + end_node = -1 # Track the last node in the optimal path + + for u in range(1, n): + if min_cost > dp[final_mask][u] + dist[u][0]: + min_cost = dp[final_mask][u] + dist[u][0] + end_node = u + + path = [] + mask = final_mask + while end_node != 0: + path.append(nodes[end_node]) + for u in range(n): + # If current state corresponds to optimal state before visiting end node + if ( + mask & (1 << u) + and dp[mask][end_node] + == dp[mask ^ (1 << end_node)][u] + dist[u][end_node] + ): + mask ^= 1 << end_node # Update mask to remove end node + end_node = u # Set the previous node as end node + break + + path.append(nodes[0]) # Bottom-up Order + path.reverse() # Top-Down Order + path.append(nodes[0]) + + return path, min_cost + + +# Demo Graph +# C (15, 35) +# | +# | +# | +# F (5, 15) --- A (10, 20) +# | | +# | | +# | | +# | | +# E (25, 5) --- B (30, 21) +# | +# | +# | +# D (40, 10) +# | +# | +# | +# G (50, 25) + + +if __name__ == "__main__": + demo_graph = { + "A": [10.0, 20.0], + "B": [30.0, 21.0], + "C": [15.0, 35.0], + "D": [40.0, 10.0], + "E": [25.0, 5.0], + "F": [5.0, 15.0], + "G": [50.0, 25.0], + } + + # Brute force + brute_force_result = travelling_salesman_brute_force(demo_graph) + print(f"Brute force result: {brute_force_result}") + + # Dynamic programming + dp_result = travelling_salesman_dynamic_programming(demo_graph) + print(f"Dynamic programming result: {dp_result}") From 76db9e005b6bdb4652425fce9cc737cc13ba6d75 Mon Sep 17 00:00:00 2001 From: VarshiniShreeV Date: Sun, 27 Oct 2024 12:48:38 +0530 Subject: [PATCH 03/13] Fixes 12192 --- sorts/topological_sort.py | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/sorts/topological_sort.py b/sorts/topological_sort.py index 7613d07b2d6c..351f185294a3 100644 --- a/sorts/topological_sort.py +++ b/sorts/topological_sort.py @@ -1,3 +1,11 @@ +"""Topological Sort on Directed Acyclic Graph(DAG)""" + +# a +# / \ +# b c +# / \ +# d e + edges: dict[str, list[str]] = { "a": ["c", "b"], "b": ["d", "e"], @@ -5,18 +13,39 @@ "d": [], "e": [], } + vertices: list[str] = ["a", "b", "c", "d", "e"] +# Perform topological sort on a DAG starting from the specified node def topological_sort(start: str, visited: list[str], sort: list[str]) -> list[str]: - visited.append(start) current = start - for neighbor in edges[start]: + # Mark the current node as visited + visited.append(current) + # List of all neighbors of current node + neighbors = edges[current] + + # Traverse all neighbors of the current node + for neighbor in neighbors: + # Recursively visit each unvisited neighbor if neighbor not in visited: - topological_sort(neighbor, visited, sort) + sort = topological_sort(neighbor, visited, sort) + + # After visiting all neighbors, add the current node to the sorted list sort.append(current) + + # If there are some nodes that were not visited (disconnected components) + if len(visited) != len(vertices): + for vertex in vertices: + if vertex not in visited: + sort = topological_sort(vertex, visited, sort) + + # Return sorted list return sort if __name__ == "__main__": + # Topological Sorting from node "a" (Returns the order in bottom up approach) sort = topological_sort("a", [], []) - sort.reverse() #Top down approach + + # Reversing the list to get the correct topological order (Top down approach) + sort.reverse() print(sort) From 613f482360dfb51322d8dae2c8bed5bf7ac8ca6b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 27 Oct 2024 07:23:56 +0000 Subject: [PATCH 04/13] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- sorts/topological_sort.py | 4 +++- travelling_salesman_problem.py | 43 ++++++++++++++++++++++------------ 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/sorts/topological_sort.py b/sorts/topological_sort.py index 351f185294a3..90035b460225 100644 --- a/sorts/topological_sort.py +++ b/sorts/topological_sort.py @@ -16,6 +16,7 @@ vertices: list[str] = ["a", "b", "c", "d", "e"] + # Perform topological sort on a DAG starting from the specified node def topological_sort(start: str, visited: list[str], sort: list[str]) -> list[str]: current = start @@ -42,10 +43,11 @@ def topological_sort(start: str, visited: list[str], sort: list[str]) -> list[st # Return sorted list return sort + if __name__ == "__main__": # Topological Sorting from node "a" (Returns the order in bottom up approach) sort = topological_sort("a", [], []) # Reversing the list to get the correct topological order (Top down approach) - sort.reverse() + sort.reverse() print(sort) diff --git a/travelling_salesman_problem.py b/travelling_salesman_problem.py index 70f6cf637d70..5c69420a0723 100644 --- a/travelling_salesman_problem.py +++ b/travelling_salesman_problem.py @@ -1,11 +1,13 @@ -""" Travelling Salesman Problem (TSP) """ +"""Travelling Salesman Problem (TSP)""" import itertools import math + class InvalidGraphError(ValueError): """Custom error for invalid graph inputs.""" + def euclidean_distance(point1: list[float], point2: list[float]) -> float: """ Calculate the Euclidean distance between two points in 2D space. @@ -28,6 +30,7 @@ def euclidean_distance(point1: list[float], point2: list[float]) -> float: except TypeError: raise ValueError("Invalid input: Points must be numerical coordinates") + def validate_graph(graph_points: dict[str, list[float]]) -> None: """ Validate the input graph to ensure it has valid nodes and coordinates. @@ -41,12 +44,12 @@ def validate_graph(graph_points: dict[str, list[float]]) -> None: Traceback (most recent call last): ... InvalidGraphError: Each node must have a valid 2D coordinate [x, y] - + >>> validate_graph([10, 20]) # Invalid input type Traceback (most recent call last): ... InvalidGraphError: Graph must be a dictionary with node names and coordinates - + >>> validate_graph({"A": [10, 20], "B": [30, 21], "C": [15]}) # Missing coordinate Traceback (most recent call last): ... @@ -66,6 +69,7 @@ def validate_graph(graph_points: dict[str, list[float]]) -> None: ): raise InvalidGraphError("Each node must have a valid 2D coordinate [x, y]") + # TSP in Brute Force Approach def travelling_salesman_brute_force( graph_points: dict[str, list[float]], @@ -89,7 +93,7 @@ def travelling_salesman_brute_force( raise InvalidGraphError("Graph must have at least two nodes") min_path = [] # List that stores shortest path - min_distance = float("inf") # Initialize minimum distance to infinity + min_distance = float("inf") # Initialize minimum distance to infinity start_node = nodes[0] other_nodes = nodes[1:] @@ -111,6 +115,7 @@ def travelling_salesman_brute_force( return min_path, min_distance + # TSP in Dynamic Programming approach def travelling_salesman_dynamic_programming( graph_points: dict[str, list[float]], @@ -127,20 +132,26 @@ def travelling_salesman_dynamic_programming( """ validate_graph(graph_points) - n = len(graph_points) # Extracting the node names (keys) + n = len(graph_points) # Extracting the node names (keys) # There shoukd be atleast 2 nodes for a valid TSP if n < 2: raise InvalidGraphError("Graph must have at least two nodes") - nodes = list(graph_points.keys()) # Extracting the node names (keys) + nodes = list(graph_points.keys()) # Extracting the node names (keys) # Initialize distance matrix with float values - dist = [[euclidean_distance(graph_points[nodes[i]], graph_points[nodes[j]]) for j in range(n)] for i in range(n)] - - # Initialize a dynamic programming table with infinity + dist = [ + [ + euclidean_distance(graph_points[nodes[i]], graph_points[nodes[j]]) + for j in range(n) + ] + for i in range(n) + ] + + # Initialize a dynamic programming table with infinity dp = [[float("inf")] * n for _ in range(1 << n)] - dp[1][0] = 0 # Only visited node is the starting point at node 0 + dp[1][0] = 0 # Only visited node is the starting point at node 0 # Iterate through all masks of visited nodes for mask in range(1 << n): @@ -149,14 +160,16 @@ def travelling_salesman_dynamic_programming( if mask & (1 << u): # Traverse nodes 'v' such that u->v for v in range(n): - if mask & (1 << v) == 0: # If v is not visited - next_mask = mask | (1 << v) # Upodate mask to include 'v' + if mask & (1 << v) == 0: # If v is not visited + next_mask = mask | (1 << v) # Upodate mask to include 'v' # Update dynamic programming table with minimum distance - dp[next_mask][v] = min(dp[next_mask][v], dp[mask][u] + dist[u][v]) + dp[next_mask][v] = min( + dp[next_mask][v], dp[mask][u] + dist[u][v] + ) final_mask = (1 << n) - 1 min_cost = float("inf") - end_node = -1 # Track the last node in the optimal path + end_node = -1 # Track the last node in the optimal path for u in range(1, n): if min_cost > dp[final_mask][u] + dist[u][0]: @@ -175,7 +188,7 @@ def travelling_salesman_dynamic_programming( == dp[mask ^ (1 << end_node)][u] + dist[u][end_node] ): mask ^= 1 << end_node # Update mask to remove end node - end_node = u # Set the previous node as end node + end_node = u # Set the previous node as end node break path.append(nodes[0]) # Bottom-up Order From 08732c14827d6797d1a19cc296029cb82d8bb086 Mon Sep 17 00:00:00 2001 From: cclauss Date: Sat, 12 Sep 2026 21:54:57 +0000 Subject: [PATCH 05/13] updating DIRECTORY.md --- DIRECTORY.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index d264265479eb..4c689fd6c02e 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -187,6 +187,7 @@ * [Intensity Based Segmentation](computer_vision/intensity_based_segmentation.py) * [Mean Threshold](computer_vision/mean_threshold.py) * [Mosaic Augmentation](computer_vision/mosaic_augmentation.py) + * [Otsu Threshold](computer_vision/otsu_threshold.py) * [Pooling Functions](computer_vision/pooling_functions.py) * [Vision Transformer](computer_vision/vision_transformer.py) @@ -454,6 +455,7 @@ * [Minimum Steps To One](dynamic_programming/minimum_steps_to_one.py) * [Minimum Tickets Cost](dynamic_programming/minimum_tickets_cost.py) * [Narcissistic Number](dynamic_programming/narcissistic_number.py) + * [Needleman Wunsch](dynamic_programming/needleman_wunsch.py) * [Optimal Binary Search Tree](dynamic_programming/optimal_binary_search_tree.py) * [Palindrome Partitioning](dynamic_programming/palindrome_partitioning.py) * [Range Sum Query](dynamic_programming/range_sum_query.py) @@ -1493,6 +1495,8 @@ ## [Tests](tests) * [Test Sorts](tests/test_sorts.py) +## [Travelling Salesman Problem](/travelling_salesman_problem.py) + ## [Web Programming](web_programming) * [Co2 Emission](web_programming/co2_emission.py) * [Covid Stats Via Xpath](web_programming/covid_stats_via_xpath.py) From ed8008ba3ca1d5b1cc636779322a67d0b2c1e0cd Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sat, 12 Sep 2026 23:57:15 +0200 Subject: [PATCH 06/13] Rename travelling_salesman_problem.py to graphs/travelling_salesman_problem.py --- .../travelling_salesman_problem.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename travelling_salesman_problem.py => graphs/travelling_salesman_problem.py (100%) diff --git a/travelling_salesman_problem.py b/graphs/travelling_salesman_problem.py similarity index 100% rename from travelling_salesman_problem.py rename to graphs/travelling_salesman_problem.py From 795be1d33e487c3469b1fc236b1a942571b4f2d6 Mon Sep 17 00:00:00 2001 From: cclauss Date: Sat, 12 Sep 2026 21:57:33 +0000 Subject: [PATCH 07/13] updating DIRECTORY.md --- DIRECTORY.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 4c689fd6c02e..56ac6b94994c 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -615,6 +615,7 @@ * [Test Johnson](graphs/tests/test_johnson.py) * [Test Min Spanning Tree Kruskal](graphs/tests/test_min_spanning_tree_kruskal.py) * [Test Min Spanning Tree Prim](graphs/tests/test_min_spanning_tree_prim.py) + * [Travelling Salesman Problem](graphs/travelling_salesman_problem.py) ## [Greedy Methods](greedy_methods) * [Best Time To Buy And Sell Stock](greedy_methods/best_time_to_buy_and_sell_stock.py) @@ -1495,8 +1496,6 @@ ## [Tests](tests) * [Test Sorts](tests/test_sorts.py) -## [Travelling Salesman Problem](/travelling_salesman_problem.py) - ## [Web Programming](web_programming) * [Co2 Emission](web_programming/co2_emission.py) * [Covid Stats Via Xpath](web_programming/covid_stats_via_xpath.py) From 1c243fc30844da9696b075beca38804b3e37eadd Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 13 Sep 2026 00:00:06 +0200 Subject: [PATCH 08/13] Fix typo in TSP validation comments --- graphs/travelling_salesman_problem.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graphs/travelling_salesman_problem.py b/graphs/travelling_salesman_problem.py index 5c69420a0723..4c03e92dfda9 100644 --- a/graphs/travelling_salesman_problem.py +++ b/graphs/travelling_salesman_problem.py @@ -88,7 +88,7 @@ def travelling_salesman_brute_force( nodes = list(graph_points.keys()) # Extracting the node names (keys) - # There shoukd be atleast 2 nodes for a valid TSP + # There should be at least 2 nodes for a valid TSP if len(nodes) < 2: raise InvalidGraphError("Graph must have at least two nodes") @@ -134,7 +134,7 @@ def travelling_salesman_dynamic_programming( n = len(graph_points) # Extracting the node names (keys) - # There shoukd be atleast 2 nodes for a valid TSP + # There should be at least 2 nodes for a valid TSP if n < 2: raise InvalidGraphError("Graph must have at least two nodes") From 3ca9a00a0ca967ba7817e994f41c1828aece8af2 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 13 Sep 2026 00:03:37 +0200 Subject: [PATCH 09/13] Fix expected output for TSP function test case Updated expected output for travelling_salesman_brute_force function. --- graphs/travelling_salesman_problem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphs/travelling_salesman_problem.py b/graphs/travelling_salesman_problem.py index 4c03e92dfda9..063c2d0a7c5d 100644 --- a/graphs/travelling_salesman_problem.py +++ b/graphs/travelling_salesman_problem.py @@ -82,7 +82,7 @@ def travelling_salesman_brute_force( >>> graph = {"A": [10, 20], "B": [30, 21], "C": [15, 35]} >>> travelling_salesman_brute_force(graph) - (['A', 'C', 'B', 'A'], 56.35465722402587) + (['A', 'B', 'C', 'A'], 56.35465722402588) """ validate_graph(graph_points) From e7e2971efddf53d53bfcc402a0ca1f1c9dc371ea Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 13 Sep 2026 00:08:00 +0200 Subject: [PATCH 10/13] Fix error messages in TSP validation functions --- graphs/travelling_salesman_problem.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/graphs/travelling_salesman_problem.py b/graphs/travelling_salesman_problem.py index 063c2d0a7c5d..c104b4c95d6a 100644 --- a/graphs/travelling_salesman_problem.py +++ b/graphs/travelling_salesman_problem.py @@ -22,7 +22,7 @@ def euclidean_distance(point1: list[float], point2: list[float]) -> float: 0.0 >>> euclidean_distance([1, 1], ['a', 1]) Traceback (most recent call last): - ... + ... ValueError: Invalid input: Points must be numerical coordinates """ try: @@ -42,17 +42,17 @@ def validate_graph(graph_points: dict[str, list[float]]) -> None: >>> validate_graph({"A": [10, 20], "B": [30, 21], "C": [15, 35]}) # Valid graph >>> validate_graph({"A": [10, 20], "B": [30, "invalid"], "C": [15, 35]}) Traceback (most recent call last): - ... + ... InvalidGraphError: Each node must have a valid 2D coordinate [x, y] >>> validate_graph([10, 20]) # Invalid input type Traceback (most recent call last): - ... + ... InvalidGraphError: Graph must be a dictionary with node names and coordinates >>> validate_graph({"A": [10, 20], "B": [30, 21], "C": [15]}) # Missing coordinate Traceback (most recent call last): - ... + ... InvalidGraphError: Each node must have a valid 2D coordinate [x, y] """ if not isinstance(graph_points, dict): From 2eeb150148c9f719e5a090bdfd05f4ae882788d9 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 13 Sep 2026 00:19:49 +0200 Subject: [PATCH 11/13] Modify doctest to ignore exception details Updated doctest to ignore exception details for invalid graph input. --- graphs/travelling_salesman_problem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphs/travelling_salesman_problem.py b/graphs/travelling_salesman_problem.py index c104b4c95d6a..b08e151adbe9 100644 --- a/graphs/travelling_salesman_problem.py +++ b/graphs/travelling_salesman_problem.py @@ -40,7 +40,7 @@ def validate_graph(graph_points: dict[str, list[float]]) -> None: :raises InvalidGraphError: If the graph points are not valid >>> validate_graph({"A": [10, 20], "B": [30, 21], "C": [15, 35]}) # Valid graph - >>> validate_graph({"A": [10, 20], "B": [30, "invalid"], "C": [15, 35]}) + >>> validate_graph({"A": [10, 20], "B": [30, "invalid"], "C": [15, 35]}). # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... InvalidGraphError: Each node must have a valid 2D coordinate [x, y] From db161163d4d646763ca269444f3412884e808361 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 13 Sep 2026 01:06:29 +0200 Subject: [PATCH 12/13] Fix doctest formatting for validate_graph function Updated doctest format for invalid graph validation. --- graphs/travelling_salesman_problem.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/graphs/travelling_salesman_problem.py b/graphs/travelling_salesman_problem.py index b08e151adbe9..ee6f79be1ab7 100644 --- a/graphs/travelling_salesman_problem.py +++ b/graphs/travelling_salesman_problem.py @@ -40,7 +40,9 @@ def validate_graph(graph_points: dict[str, list[float]]) -> None: :raises InvalidGraphError: If the graph points are not valid >>> validate_graph({"A": [10, 20], "B": [30, 21], "C": [15, 35]}) # Valid graph - >>> validate_graph({"A": [10, 20], "B": [30, "invalid"], "C": [15, 35]}). # doctest: +IGNORE_EXCEPTION_DETAIL + >>> validate_graph( # doctest: +IGNORE_EXCEPTION_DETAIL + ... {"A": [10, 20], "B": [30, "invalid"], "C": [15, 35]} + ... ) Traceback (most recent call last): ... InvalidGraphError: Each node must have a valid 2D coordinate [x, y] From a7c477d818ff3a5dbbe16805fccecd963f6f026a Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 13 Sep 2026 01:10:20 +0200 Subject: [PATCH 13/13] Update doctests in validate_graph function --- graphs/travelling_salesman_problem.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/graphs/travelling_salesman_problem.py b/graphs/travelling_salesman_problem.py index ee6f79be1ab7..0fe84bf2dc1e 100644 --- a/graphs/travelling_salesman_problem.py +++ b/graphs/travelling_salesman_problem.py @@ -47,12 +47,14 @@ def validate_graph(graph_points: dict[str, list[float]]) -> None: ... InvalidGraphError: Each node must have a valid 2D coordinate [x, y] - >>> validate_graph([10, 20]) # Invalid input type + >>> validate_graph([10, 20]) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... InvalidGraphError: Graph must be a dictionary with node names and coordinates - >>> validate_graph({"A": [10, 20], "B": [30, 21], "C": [15]}) # Missing coordinate + >>> validate_graph( # doctest: +IGNORE_EXCEPTION_DETAIL + ... {"A": [10, 20], "B": [30, 21], "C": [15]} + ... ) # Missing coordinate Traceback (most recent call last): ... InvalidGraphError: Each node must have a valid 2D coordinate [x, y]