From d6a868917500b3f32664b81e4d424f3ca8c845f3 Mon Sep 17 00:00:00 2001 From: Marco Thomas Date: Wed, 27 Jul 2022 14:32:37 +0200 Subject: [PATCH] graph: Adjust calculation to match exercise --- classes.py | 29 ++++++++---- dijkstra.py | 77 ------------------------------ dijsktra.py | 19 ++++++++ graph.py | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++++ main.py | 9 ++-- parser.py | 6 ++- 6 files changed, 183 insertions(+), 90 deletions(-) delete mode 100644 dijkstra.py create mode 100644 dijsktra.py create mode 100644 graph.py diff --git a/classes.py b/classes.py index 66516af..15ce554 100644 --- a/classes.py +++ b/classes.py @@ -15,22 +15,33 @@ class LocationType(Enum): class TransportData(): - def __init__(self, co2: float, speed: float, distance_penalty: float): + def __init__(self, co2: float, speed: float): self.co2 = co2 self.speed = speed - self.distance_penalty = distance_penalty + + def __str__(self): + return f"{{co2: {self.co2}, speed: {self.speed}}}" + + def __repr__(self): + return self.__str__() class TransportMethod(Enum): + WALK = TransportData(0, 4) + CITY = TransportData(0.189, 30) + AUTOBAHN = TransportData(0.189, 100) + OEPNV = TransportData(0.055, 30) + ICE = TransportData(0.055, 100) + AIRPLANE = TransportData(0.2113, 900) + + +class TransportKind(Enum): """ - (co2 emission, speed, distance_penalty) + distance_penalty """ - WALK = TransportData(0, 4, 0.2) - CITY = TransportData(0.189, 30, 0.2) - AUTOBAHN = TransportData(0.189, 100, 0.2) - OEPNV = TransportData(0.055, 30, 0.1) - ICE = TransportData(0.055, 100, 0.1) - AIRPLANE = TransportData(0.2113, 900, 0.02) + INDIVIDUAL = 0.2 + TRAIN = 0.1 + FLYING = 0.02 class Coordinate: diff --git a/dijkstra.py b/dijkstra.py deleted file mode 100644 index 1dec038..0000000 --- a/dijkstra.py +++ /dev/null @@ -1,77 +0,0 @@ -""" -Module, which holds the logic for the solving algorithms -""" - -from classes import DataSet, LocationType, Location, TransportMethod - - -def calc_co2(distance: float, method: TransportMethod) -> float: - """ - Return co2 emission in kg - """ - return (distance * method.value.distance_penalty) * method.value.co2 - - -def calc_time(distance: float, method: TransportMethod) -> float: - """ - Return time taking in h - """ - return (distance * method.value.distance_penalty) / method.value.speed - - -def add_node(graph: dict, frm: str, to: str, locs: dict, method: TransportMethod): - """ - Add a node into the graph - """ - distance = locs[frm].distance(locs[to]) - co2 = calc_co2(distance, method) - time = calc_time(distance, method) - new_connection: dict = {to: {"type": method, "co2": co2, "time": time}} - graph[frm].append(new_connection) - print(f"Added node from {frm} to {to} with type {method}.") - - -def create_graph(dataset: DataSet) -> dict: - """ - Creates the initial graph, with all edges - """ - locations = dataset.locations - graph: dict = {} - - # add nodes with no edges - for start in locations: - graph.update({start: []}) - - # add edges - for start in locations: - for dest in locations: - # skip, if we wouldnt go anywhere - if start == dest: - continue - print(f"Searching for nodes from {start} to {dest}...") - distance = locations[start].distance(locations[dest]) - - # Individualtransport - if distance <= 1: - add_node(graph, start, dest, locations, TransportMethod.WALK) - if distance <= 10 + 1: - add_node(graph, start, dest, locations, TransportMethod.CITY) - if distance <= 2000 + 10 + 1: - add_node(graph, start, dest, locations, TransportMethod.AUTOBAHN) - - # Train - if locations[start].type == LocationType.HALTESTELLE: - dest_type = locations[dest].type - # there are only trains between haltestellen or airports - if dest_type == LocationType.HALTESTELLE or dest_type == LocationType.FLUGHAFEN: - if distance <= 25: - add_node(graph, start, dest, locations, TransportMethod.OEPNV) - else: - add_node(graph, start, dest, locations, TransportMethod.ICE) - - # Flying - flights = dataset.flights - for flight in flights: - add_node(graph, start, dest, locations, TransportMethod.AIRPLANE) - - return graph diff --git a/dijsktra.py b/dijsktra.py new file mode 100644 index 0000000..5f0ae82 --- /dev/null +++ b/dijsktra.py @@ -0,0 +1,19 @@ +""" +This module holds the logic for the solving +""" + + +class Dijkstra(): + def __init__(self, graph: dict): + """ + table: matrix with vertex as key, price and prev vertex as value + OL: open list, to keep track of finished vertecis + """ + self.graph = graph + self.table = {} + self.OL = [] + + def algorithm(self): + print("Performing Dijkstra on the following graph...") + for vertex in self.graph: + print(f"{vertex}: {self.graph[vertex]}") diff --git a/graph.py b/graph.py new file mode 100644 index 0000000..3badb23 --- /dev/null +++ b/graph.py @@ -0,0 +1,133 @@ +""" +Module, which creates a graph +""" + +from classes import DataSet, LocationType, TransportMethod, TransportKind + + +def calc_co2(distance: float, kind: TransportKind) -> float: + """ + Return co2 emission in kg + """ + match kind: + case TransportKind.INDIVIDUAL: + if distance <= 1: + return 0 + else: + # TODO: CITY == AUTOBAHN + return distance * TransportMethod.CITY.value.co2 + case TransportKind.TRAIN: + # TODO: OPENV == ICE + return distance * TransportMethod.ICE.value.co2 + case TransportKind.FLYING: + return distance * TransportMethod.AIRPLANE.value.co2 + + +def calc_time( + distance: float, + kind: TransportKind, + flights: dict = None, + frm: dict = None, + ) -> float: + """ + distance: distance without penalty + kind: kind of transport + flights: all flights (optional) + frm: start location (need for flights, optional) + """ + match kind: + case TransportKind.INDIVIDUAL: + distance += distance * TransportKind.INDIVIDUAL.value + if distance <= 1: + return distance / TransportMethod.WALK.value.speed + elif distance <= 10 + 1: + speed = 1 / TransportMethod.WALK.value.speed + return speed + ((distance - 1) / TransportMethod.CITY.value.speed) + elif distance < 2000 + 10 + 1: + speed = 1 / TransportMethod.WALK.value.speed + speed += 10 / TransportMethod.WALK.value.speed + return speed + ((distance - 1 - 10) / TransportMethod.AUTOBAHN.value.speed) + case TransportKind.TRAIN: + distance += distance * TransportKind.TRAIN.value + if distance <= 25: + return distance / TransportMethod.OEPNV.value.speed + else: + speed = 25 / TransportMethod.OEPNV.value.speed + # NOTE: should we subtract here? + return speed + ((distance - 25) / TransportMethod.ICE.value.speed) + case TransportKind.FLYING: + assert(flights != None) + assert(frm != None) + distance += distance * TransportKind.FLYING.value + stops = flights[frm]["stops"] + domestic = flights[frm]["domestic"] + if domestic: + time = 2 + else: + time = 3 + waittime = (stops + 1) * time + return waittime + (distance / TransportMethod.AIRPLANE.value.speed) + + +def create_graph(dataset: DataSet) -> dict: + """ + Creates the initial graph, with all edges + """ + locations = dataset.locations + graph: dict = {} + + # add nodes with no edges + for start in locations: + graph.update({start: []}) + + # add edges + for start in locations: + for dest in locations: + # skip, if we wouldnt go anywhere + if start == dest: + continue + print(f"Searching for nodes from {start} to {dest}...") + distance = locations[start].distance(locations[dest]) + + # Individualtransport + if distance <= 2000 + 10 + 1: + time = calc_time(distance, TransportKind.INDIVIDUAL) + co2 = calc_co2(distance, TransportKind.INDIVIDUAL) + new_connection: dict = {dest: + {"kind": TransportKind.INDIVIDUAL, + "co2": co2, + "time": time}} + graph[start].append(new_connection) + print(f"Added new INDIVIDUAL connection from {start} to {dest}") + + # Train + if locations[start].type == LocationType.HALTESTELLE: + dest_type = locations[dest].type + # there are only trains between haltestellen or airports + if dest_type == LocationType.HALTESTELLE or dest_type == LocationType.FLUGHAFEN: + time = calc_time(distance, TransportKind.TRAIN) + co2 = calc_co2(distance, TransportKind.TRAIN) + new_connection: dict = {dest: + {"kind": TransportKind.TRAIN, + "co2": co2, + "time": time}} + graph[start].append(new_connection) + print(f"Added new TRAIN connection from {start} to {dest}") + + # Flying + print("Adding flights...") + flights = dataset.flights + for flight in flights: + start = flight + dest = flights[flight]["to"] + distance = locations[start].distance(locations[dest]) + time = calc_time(distance, TransportKind.FLYING, flights, start) + co2 = calc_co2(distance, TransportKind.FLYING) + new_connection: dict = {dest: + {"kind": TransportKind.TRAIN, + "co2": co2, + "time": time}} + graph[start].append(new_connection) + print(f"Added new FLYING connection from {start} to {dest}") + + return graph diff --git a/main.py b/main.py index 61d4073..27f4cf1 100644 --- a/main.py +++ b/main.py @@ -4,7 +4,8 @@ Requires: python > 3.10.x """ from parser import parse -from dijkstra import create_graph +from graph import create_graph +from dijsktra import Dijkstra INPUTFILE = "file.txt" @@ -12,9 +13,11 @@ INPUTFILE = "file.txt" def main(): dataset: dict = parse(INPUTFILE) - print(dataset) + # print(dataset) graph: dict = create_graph(dataset) - print(graph) + # print(graph) + dijkstra = Dijkstra(graph) + dijkstra.algorithm() if __name__ == "__main__": diff --git a/parser.py b/parser.py index b34f28d..8162bbd 100644 --- a/parser.py +++ b/parser.py @@ -68,7 +68,11 @@ def parse(filename: str) -> DataSet: stops = splitted[2] else: stops = 0 - flights.update({id1: {"to": id2, "stops": stops}}) + if len(splitted) == 4: + domestic = True + else: + domestic = False + flights.update({id1: {"to": id2, "stops": stops, "domestic": domestic}}) continue case ParsingMode.FINDCONNECTION: print("Parsing connection...")