From 450dab0d8e96c2d32c86c5bd9589665bc118c2f6 Mon Sep 17 00:00:00 2001 From: Vishal Paudel <95016059+vishalpaudel@users.noreply.github.com> Date: Sat, 28 Oct 2023 02:24:43 +0530 Subject: [PATCH] :zap: Made blitting/ truly blazingly fast --- __init__.py | 0 pyproject.toml | 3 ++ src/searchViz/Game.py | 58 ++++++++++++++++++++++---------------- src/searchViz/Graph.py | 53 ++++++++++++++++++---------------- src/searchViz/Search.py | 4 ++- src/searchViz/__init__.py | 0 src/searchViz/constants.py | 1 + src/searchViz/models.py | 4 ++- test.py | 48 ------------------------------- 9 files changed, 71 insertions(+), 100 deletions(-) create mode 100644 __init__.py create mode 100644 pyproject.toml create mode 100644 src/searchViz/__init__.py delete mode 100644 test.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..357ad8c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ + +[build-system] +requires = ["setuptools", "wheel"] diff --git a/src/searchViz/Game.py b/src/searchViz/Game.py index c7cdc55..821644f 100644 --- a/src/searchViz/Game.py +++ b/src/searchViz/Game.py @@ -2,6 +2,9 @@ import pygame as pg +import time +import numpy as np + from .constants import SCR_SIZE, BG_COLOR, RED, WHITE, NODE_RADIUS, SEARCH_RATE @@ -10,7 +13,7 @@ class Game: - def __init__(self, search_method: Search, num_nodes: int): + def __init__(self, search_method: Search, num_nodes: int) -> None: pg.init() # main attributes of the game @@ -33,7 +36,7 @@ def __init__(self, search_method: Search, num_nodes: int): print("šŸ¼ searchViz has been initialized! šŸŽ‰") - def handle_events(self): + def handle_events(self) -> None: for event in pg.event.get(): if event.type == pg.QUIT: self.running = False @@ -57,34 +60,39 @@ def handle_events(self): return None - def draw_graph(self): - # unpacking frequently used variables - graph_surf = self.graph_surf - num_nodes = self.Graph.num_nodes - nodes = self.Graph.nodes - colors = self.Graph.colors - radius = self.Graph.radius - edges = self.Graph.edges - - # need to use this when traversing - # edge_colors = self.Graph.edge_color + def draw_graph(self) -> None: + _start_time = time.time() + print("āœļø\tDrawing the Graph") - # Draw nodes and their edges + # Unpack frequently used variables + graph_surf = self.graph_surf + num_nodes = self.Graph.N_num + nodes = self.Graph.N_locs + colors = self.Graph.N_colors + radius = self.Graph.N_radii + edges = self.Graph.edge_connections + + # Draw nodes + # TODO: vectorization of this possible? Probably not for i in range(num_nodes): - # Unique (uni-directional) edges - for j in range(i, num_nodes): - if edges[i, j]: - pg.draw.line(graph_surf, WHITE, nodes[i], nodes[j]) - - node = nodes[i] pg.draw.circle( - graph_surf, - color=tuple(colors[i]), - center=node, - radius=radius[i], + graph_surf, color=colors[i], center=nodes[i], radius=radius[i] ) - def run(self): + # Draw edges + edge_indices = np.transpose(np.where(edges)) + for i, j in edge_indices: + pg.draw.line(graph_surf, WHITE, nodes[i], nodes[j]) + + _end_time = time.time() + _elapsed_time = _end_time - _start_time + print("āœļø\tCompleted the drawing!") + + print(f"\tšŸ•°ļø Took {_elapsed_time:.3f} seconds.\n") + + return None + + def run(self) -> None: last_time = pg.time.get_ticks() step = 0 self.draw_graph() diff --git a/src/searchViz/Graph.py b/src/searchViz/Graph.py index 37ef877..b665097 100644 --- a/src/searchViz/Graph.py +++ b/src/searchViz/Graph.py @@ -1,7 +1,7 @@ #!/usr/bin/env python from .constants import NODES_X_DISTRIBUTION, NODES_Y_DISTRIBUTION -from .constants import EDGE_CONFIDENCE, RED, BLUE, NODE_RADIUS +from .constants import EDGE_CONFIDENCE, BLUE, NODE_RADIUS, WHITE import numpy as np import time @@ -20,16 +20,25 @@ def __init__(self, id: np.uint16): class Graph: - def __init__(self, num_nodes: int): - self.num_nodes = num_nodes + def __init__(self, n: int): + self.N_num = n _start_time = time.time() - self.nodes, self.colors, self.radius = create_nodes(num_nodes) - self.edges, self.edge_colors = create_edges(self.nodes) - start_id, end_id = np.random.randint(0, self.num_nodes, size=2, dtype=np.uint16) - self.start_node = Node(start_id) - self.end_node = Node(end_id) + # start and end nodes + start, end = np.random.randint(0, self.N_num, size=2, dtype=np.uint16) + self.start_node = Node(start) + self.end_node = Node(end) + + # Node stuff + self.N = [Node(np.uint16(i)) for i in range(self.N_num)] + self.N_locs = create_nodes(self.N_num) + + self.N_colors = np.full((self.N_num, 4), BLUE, dtype=np.uint8) + self.N_radii = np.full((self.N_num,), NODE_RADIUS, dtype=np.uint8) + + # Edge stuff + self.edge_connections, self.edge_colors = create_edges(self.N_locs) _end_time = time.time() _elapsed_time = _end_time - _start_time @@ -47,12 +56,12 @@ def moveGen(self, state: Node) -> List[Node]: neighbors = [] id = np.uint16(0) - while id < self.num_nodes: - if [id, state.id] == 1: + while id < self.N_num: + if self.edge_connections[id, state.id] == 1: neighbors.append(Node(id)) id += 1 - print() + print() return neighbors def goalTest(self, state: Node) -> bool: @@ -76,24 +85,18 @@ def create_nodes(n: int): x_values = NODES_X_DISTRIBUTION(n) y_values = NODES_Y_DISTRIBUTION(n) - colors = np.full((n, 4), fill_value=BLUE, dtype=np.uint8) - radii = np.full((n,), fill_value=NODE_RADIUS, dtype=np.uint8) - - # generating the goal node - goal_loc = np.random.randint(0, n, 1) - colors[goal_loc] = RED - radii[goal_loc] = 3 * NODE_RADIUS + node_locs = np.column_stack((x_values, y_values)) - nodes = np.column_stack((x_values, y_values)) print("āœ…\tFinished creating nodes!\n") - return nodes, colors, radii + return node_locs def create_edges(nodes: np.ndarray): """ Creates edges for the give nodes locations """ + # TODO: need to seperate the threading or remove the animation global done done = threading.Event() @@ -101,23 +104,23 @@ def create_edges(nodes: np.ndarray): dot_thread.start() n = len(nodes) - edges = np.zeros((n, n)) - i, j = np.triu_indices(n, k=1) + edge_connections = np.zeros((n, n)) + i, j = np.triu_indices(n, k=1) # only one way edges _toss = np.random.rand(n, n) _confidence = np.zeros((n, n)) _confidence[i, j] = EDGE_CONFIDENCE(nodes[i], nodes[j]) # confidence number of times there will be an edge - edges = _toss <= _confidence - edge_color = (edges).astype(int) + edge_connections = _toss <= _confidence + edge_colors = np.full((n, 4), fill_value=WHITE, dtype=np.uint8) # Stop the dot animation done.set() dot_thread.join() # Wait for the animation thread to finish print("\nāœ…\tFinished creating edges!\n") - return edges, edge_color + return edge_connections, edge_colors # for animating while the edges get created diff --git a/src/searchViz/Search.py b/src/searchViz/Search.py index a237e51..5c29848 100644 --- a/src/searchViz/Search.py +++ b/src/searchViz/Search.py @@ -4,6 +4,8 @@ from .Graph import Node +import numpy as np + class Search: def __init__(self, search: Callable[[Node], List[Node]], name: str): @@ -26,5 +28,5 @@ def breadth_first_search(startState: Node) -> List[Node]: if __name__ == "__main__": - startState = Node() + startState = Node(np.uint16(1)) dfs.search(startState) diff --git a/src/searchViz/__init__.py b/src/searchViz/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/searchViz/constants.py b/src/searchViz/constants.py index 776507c..6c0c2ce 100644 --- a/src/searchViz/constants.py +++ b/src/searchViz/constants.py @@ -39,3 +39,4 @@ RED = (255, 0, 0, 255) BLUE = (0, 255, 255, 255) WHITE = (255, 255, 255, 200) +YELLOW = (255, 255, 153, 200) diff --git a/src/searchViz/models.py b/src/searchViz/models.py index e298675..6c74121 100644 --- a/src/searchViz/models.py +++ b/src/searchViz/models.py @@ -4,10 +4,12 @@ import numpy as np +import numpy.typing as npt + # distribution of nodes def nodes_dist_wrapper(length: int, model: str): - def nodes_uniform(n: int): + def nodes_uniform(n: int) -> npt.NDArray[np.float64]: # return np.random.normal(length / 2, length / 5, n) return np.random.uniform(0, length, n) diff --git a/test.py b/test.py deleted file mode 100644 index f765957..0000000 --- a/test.py +++ /dev/null @@ -1,48 +0,0 @@ -""" ANT TOUR """ -cities = [i for i in range(N)] - - -def make_the_ant_traverse(): - # Start with a random city - tour = [] - start_city = random.choice(cities) - current_city = start_city - - remaining_cities = [i for i in range(N)] - remaining_cities = remaining_cities.remove(current_city) - - while len(remaining_cities) != 0: - probababilites_of_transition = [] - for v in remaining_cities: - probababilites_of_transition.append(probability(u, v)) - - next_city = np.random.choose(remaining_cities, 1, probababilites_of_transition) - remaining_cities.remove(next_city) - tour.append(next_city) - current_city = next_city - - return tour - - -""" OPTIMISATION LOOP """ -num_iterations = 100 -list_of_tours = [] - -i = 0 -while i < num_iterations: - latest_tour = make_the_ant_traverse() - list_of_tours.append(new_tour) - - cost_of_tour = cost( - latest_tour - ) # the traversal cost of the most recently calculated tour - # update the rewards, sliding window of 2 cities - start_city = latest_tour[0] - - i = 0 - u = latest_tour[0] - v = latest_tour[1] - while v != start_city: - reward(u, v, cost_of_tour) - - i += 1