From 795565abbce05dc23660ccf6ef52ef537206b83b Mon Sep 17 00:00:00 2001 From: Kacper Kafara Date: Fri, 23 Dec 2022 19:24:55 +0100 Subject: [PATCH 1/5] Exclude venv from repo --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a533ef2..69be005 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ .envrc/ -.env/ +.venv/ .vscode/ *local.* From 2501e11f1dbf7de12cdb38e12d3e1c77960e2ddd Mon Sep 17 00:00:00 2001 From: Kacper Kafara Date: Sun, 25 Dec 2022 14:20:29 +0100 Subject: [PATCH 2/5] Update readme --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 69be005..26d5841 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ -.envrc/ +.envrc .venv/ .vscode/ *local.* + From ebb9051f3ddedc839bc97fb3a4b7219ba25fa351 Mon Sep 17 00:00:00 2001 From: Kacper Kafara Date: Sun, 25 Dec 2022 14:20:55 +0100 Subject: [PATCH 3/5] Add input/output files --- src/ectoolkit/main.py | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/ectoolkit/main.py b/src/ectoolkit/main.py index 378fa8f..cb89c03 100644 --- a/src/ectoolkit/main.py +++ b/src/ectoolkit/main.py @@ -1,2 +1,40 @@ +import os.path as path + +class Record: + def __init__(self) -> None: + pass + + +def parse_csv_probe_output(filename: str) -> list[Record]: + pass + +def main(): + # Read input file name + input_file = "../local.data/local.input" + + if not path.isfile(input_file): + print("Provided input file path: {} does not point to a valid file", input_file) + return 1 + + # Read output directory + output_dir = "../local.data" + + if not path.isdir(output_dir): + print("Provided output directory path: {} does not point to a valid directory", output_dir) + return 1 + + + + # Read & parse input file + + # Pass records to visualizer + + # Run visualizer + + # (Optional) Print some summary + + pass + + if __name__ == "__main__": - print("Hello world") + main() From 5df099a037b40cf8d409f23319abe1992cfb22d2 Mon Sep 17 00:00:00 2001 From: Kacper Kafara Date: Sun, 25 Dec 2022 14:36:48 +0100 Subject: [PATCH 4/5] Update min. req. python version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f7f8b8f..3804bed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ authors = [ ] description = "Tool for visualising results of evolutionary computation conducted with the ecrs library" readme = "README.md" -requires-python = ">=3.9" +requires-python = ">=3.10" classifiers = [ "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", From 6f201311fa2332858e312ed564a4014b68cfdb93 Mon Sep 17 00:00:00 2001 From: Kacper Kafara Date: Fri, 30 Dec 2022 22:34:26 +0100 Subject: [PATCH 5/5] Update work --- .gitignore | 1 + src/ectoolkit/individual.py | 17 +++++++ src/ectoolkit/main.py | 96 +++++++++++++++++++++++++++++++++---- src/ectoolkit/records.py | 31 ++++++++++++ 4 files changed, 135 insertions(+), 10 deletions(-) create mode 100644 src/ectoolkit/individual.py create mode 100644 src/ectoolkit/records.py diff --git a/.gitignore b/.gitignore index 26d5841..d15dc88 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ .venv/ .vscode/ *local.* +__pycache__/ diff --git a/src/ectoolkit/individual.py b/src/ectoolkit/individual.py new file mode 100644 index 0000000..14a0cd2 --- /dev/null +++ b/src/ectoolkit/individual.py @@ -0,0 +1,17 @@ +import json + +class Individual: + __slots__ = ('chromosome', 'fitness') + + def __init__(self, chromosome=None, fitness=None) -> None: + self.chromosome = chromosome + self.fitness = fitness + + @staticmethod + def from_json(json_str: str) -> 'Individual': + print(f"Parsing json: {json_str}") + return json.loads(json_str) + + @staticmethod + def from_dict(obj: dict[str]) -> 'Individual': + return Individual(obj['chromosome'], obj['fitness']) diff --git a/src/ectoolkit/main.py b/src/ectoolkit/main.py index cb89c03..d5d69d9 100644 --- a/src/ectoolkit/main.py +++ b/src/ectoolkit/main.py @@ -1,31 +1,107 @@ +import os import os.path as path +import csv +import json -class Record: - def __init__(self) -> None: - pass +import numpy as np +import matplotlib.pyplot as plt +from records import * +from individual import Individual -def parse_csv_probe_output(filename: str) -> list[Record]: - pass +EVENT_TYPE_NEW_GEN = 'new_gen' +EVENT_TYPE_INIT_GEN = 'init_gen' +EVENT_TYPE_NEW_BEST = 'new_best' +EVENT_TYPE_END = 'end' + +def average_fitness_in_pop(population: list[Individual]) -> float: + return np.average(list(map(lambda idv: idv.fitness, population))) + +def plot_avg_fitness_in_pop(generation_records: list[tuple[int, int, list[Individual]]], axes: plt.Axes): + first_gen = generation_records[0][1] + last_gen = generation_records[-1][1] + + x_data = np.arange(first_gen, last_gen + 1, 1) + y_data = [average_fitness_in_pop(record[2]) for record in generation_records] + + axes.plot(x_data, y_data) + + +def dispatch_record(record: list[str], + new_gen_list: list, + init_gen_list: list, + new_best_list: list, + end_list: list): + # print(record) + match record: + case ['new_gen', dur, gen, *population]: + print(EVENT_TYPE_NEW_GEN) + print(type(population), len(population), type(population[0])) + # raise ValueError + population = json.loads(population[0]) + individuals = [Individual.from_json(idv_json) for idv_json in population] + new_gen_list.append((int(dur), int(gen), individuals)) + raise ValueError + + case ['new_best', dur, gen, indiv]: + print(EVENT_TYPE_NEW_BEST) + + case ['init_gen', *population]: + print(EVENT_TYPE_INIT_GEN) + + case ['end', dur, gen, best, *population]: + print(EVENT_TYPE_END) + + case _: + raise ValueError(f"Unhandled record type: {record[0]}") + + + +def parse_csv_probe_output(filename: str) -> list: + new_gen_list = [ ] + with open(filename, 'r') as input_file: + reader = csv.reader(input_file, delimiter='|') + + # I assume here there are no headers + for line in reader: + dispatch_record(line, new_gen_list, None, None, None) + + return new_gen_list + def main(): # Read input file name - input_file = "../local.data/local.input" + input_file = "local.data/local.input" if not path.isfile(input_file): - print("Provided input file path: {} does not point to a valid file", input_file) + print(f"Provided input file path: {input_file} does not point to a valid file") + print(f"cwd: {os.getcwd()}") return 1 # Read output directory - output_dir = "../local.data" + output_dir = "local.data" if not path.isdir(output_dir): - print("Provided output directory path: {} does not point to a valid directory", output_dir) + print(f"Provided output directory path: {output_dir} does not point to a valid directory") + print(f"cwd: {os.getcwd()}") return 1 - + individual = Individual([0, 0, 0], 42) + + print(json.dumps({ + "chromosome": [0.0, 1.0, 2.0], + "fitness": 42 + })) + return # Read & parse input file + new_gen_list = parse_csv_probe_output(input_file) + + fig, axes = plt.subplots(nrows=1, ncols=1, figsize=(12, 7)) + + plot_avg_fitness_in_pop(new_gen_list, axes) + + fig.show() # Pass records to visualizer diff --git a/src/ectoolkit/records.py b/src/ectoolkit/records.py new file mode 100644 index 0000000..3a388f6 --- /dev/null +++ b/src/ectoolkit/records.py @@ -0,0 +1,31 @@ +# EVENT_TYPE_NEW_GEN = 'new_gen' +# EVENT_TYPE_INIT_GEN = 'init_gen' +# EVENT_TYPE_NEW_BEST = 'new_best' +# EVENT_TYPE_END = 'end' + + +# class Record(object): +# __slots__ = ('type') + +# def __init__(self, type: str) -> None: +# self.type = type + + +# class NewGenRecord(Record): +# def __init__(self) -> None: +# super().__init__(EVENT_TYPE_NEW_GEN) + + +# class InitGenRecord(Record): +# def __init__(self) -> None: +# super().__init__(EVENT_TYPE_INIT_GEN) + + +# class NewBestRecord(Record): +# def __init__(self) -> None: +# super().__init__(EVENT_TYPE_NEW_BEST) + + +# class EndRecord(Record): +# def __init__(self) -> None: +# super().__init__(EVENT_TYPE_END)