diff --git a/setup.py b/setup.py index eeca904..4b4a251 100644 --- a/setup.py +++ b/setup.py @@ -26,6 +26,8 @@ setup( "License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)", "Operating System :: OS Independent", ], + install_requires = ["matplotlib ~= 3.3.2", + ], extra_require = { "dev": [ "pytest>=3.7", diff --git a/src/EasyGA.py b/src/EasyGA.py index de96100..5b73dd2 100644 --- a/src/EasyGA.py +++ b/src/EasyGA.py @@ -23,6 +23,9 @@ from attributes import Attributes from database import database from sqlite3 import Error +# Graphing package +import matplotlib.pyplot as plt + class GA(Attributes): """GA is the main class in EasyGA. Everything is run through the ga @@ -154,3 +157,34 @@ class GA(Attributes): """Prints the best chromosome and its fitness""" print(f"Best Chromosome \t: {self.population.get_chromosome(0)}") print(f"Best Fitness \t: {self.population.get_chromosome(0).get_fitness()}") + + def graph_scatter(self): + """Show a scatter plot of the database information.""" + + # Query the X data + generations = self.database.query_one_item("SELECT COUNT(DISTINCT generation) FROM data;") + + # Create the generations array + X = list(range(0, generations)) + + #Query the Y data + Y_data = self.database.query_all("SELECT SUM(fitness) FROM data GROUP BY generation;") + + # Format the Y data so we can use it to plot + Y = [i[0] for i in Y_data] + + # Set the plot size + plt.figure(figsize=[5, 5]) + + plt.scatter(X,Y) + # x and y labels + plt.xlabel('Generation') + plt.ylabel('Generation Total Fitness') + plt.title('Relationship Between Generations and Generation Total Fitness') + + # Show the plot + plt.show() + + + def graph_line(self): + pass diff --git a/src/attributes.py b/src/attributes.py index 6d84dba..ec5441a 100644 --- a/src/attributes.py +++ b/src/attributes.py @@ -24,6 +24,7 @@ from crossover import Crossover_Methods from database import database from sqlite3 import Error + class Attributes: """Default GA attributes can be found here. If any attributes have not been set then they will fall back onto the default attribute. All @@ -60,7 +61,7 @@ class Attributes: termination_impl = Termination_Methods.fitness_and_generation_based, database = None, database_name = 'database.db', - sql_create_data_structure = """CREATE TABLE IF NOT EXISTS data ( + sql_create_data_structure = """CREATE TABLE IF NOT EXISTS data ( id integer PRIMARY KEY, generation integer NOT NULL, fitness DOUBLE, diff --git a/src/database.db b/src/database.db new file mode 100644 index 0000000..d8f80b1 Binary files /dev/null and b/src/database.db differ diff --git a/src/database/database.py b/src/database/database.py index 7596288..78cd66a 100644 --- a/src/database/database.py +++ b/src/database/database.py @@ -5,7 +5,7 @@ import os class database: """Main database class that controls all the functionality for input / - out of the database.""" + out of the database.""" def __init__(self): @@ -13,7 +13,7 @@ class database: def create_connection(self, db_file): - """ create a database connection to the SQLite database + """Create a database connection to the SQLite database specified by db_file.""" conn = None @@ -27,11 +27,8 @@ class database: def create_table(self, create_table_sql): - """ create a table from the create_table_sql statement - :param conn: Connection object - :param create_table_sql: a CREATE TABLE statement - :return: - """ + """Create a table from the create_table_sql statement.""" + try: c = self.conn.cursor() c.execute(create_table_sql) @@ -39,20 +36,15 @@ class database: print(e) def insert_chromosome(self, generation, chromosome): - """ - Insert a new chromosome - :param conn: - :param generation: - :param chromosome: - :return: - """ + """ """ # Structure the insert data db_chromosome = (generation, chromosome.fitness, '[chromosome]') - + # Create sql query structure sql = ''' INSERT INTO data(generation,fitness,chromosome) - VALUES(?,?,?) ''' + VALUES(?,?,?) ''' + cur = self.conn.cursor() cur.execute(sql, db_chromosome) self.conn.commit() @@ -61,16 +53,20 @@ class database: def insert_current_population(self, ga): """ Insert current generations population """ + # For each chromosome in the population for chromosome in ga.population.chromosome_list: + # Insert the chromosome into the database self.insert_chromosome(ga.current_generation, chromosome) def create_data_table(self, ga): + """Create the data table that store generation data.""" try: - # Remove old database if there + # Remove old database file if it exists. os.remove(ga.database_name) except: + # If the database does not exist continue pass # create a database connection @@ -81,7 +77,23 @@ class database: # create projects table self.create_table(ga.sql_create_data_structure) - # create tasks table - # create_table(conn, sql_create_tasks_table) else: print("Error! cannot create the database connection.") + + + def query_all(self, query): + """Query for muliple rows of data""" + + cur = self.conn.cursor() + cur.execute(query) + + return cur.fetchall() + + def query_one_item(self, query): + """Query for single data point""" + + cur = self.conn.cursor() + cur.execute(query) + query_data = cur.fetchone() + + return query_data[0] diff --git a/src/database/graph.py b/src/database/graph.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/run_testing.py b/src/run_testing.py index b5f355a..84edc65 100644 --- a/src/run_testing.py +++ b/src/run_testing.py @@ -1,14 +1,14 @@ -import random import EasyGA +import random # Create the Genetic algorithm ga = EasyGA.GA() -ga.population_size = 3 -ga.generation_goal = 10 -# Evolve the genetic algorithm +ga.generation_goal = 200 +ga.population_size = 50 + ga.evolve() -# Print your default genetic algorithm -ga.print_generation() ga.print_population() + +ga.graph_scatter()