Initial evaluation for removing matplotlib and pytest as dependencies.

This commit is contained in:
2023-01-02 18:44:41 +01:00
parent e620321ef4
commit a107bdf69e
4 changed files with 121 additions and 102 deletions

View File

@ -7,7 +7,7 @@ import decorators
# Import all the data structure prebuilt modules # Import all the data structure prebuilt modules
from structure import Population as make_population from structure import Population as make_population
from structure import Chromosome as make_chromosome from structure import Chromosome as make_chromosome
from structure import Gene as make_gene from structure import Gene as make_gene
from structure import Population from structure import Population
from structure import Chromosome from structure import Chromosome
from structure import Gene from structure import Gene
@ -17,23 +17,23 @@ from examples import Fitness
from termination import Termination from termination import Termination
# Parent/Survivor Selection Methods # Parent/Survivor Selection Methods
from parent import Parent from parent import Parent
from survivor import Survivor from survivor import Survivor
# Genetic Operator Methods # Genetic Operator Methods
from crossover import Crossover from crossover import Crossover
from mutation import Mutation from mutation import Mutation
# Default Attributes for the GA # Default Attributes for the GA
from attributes import Attributes from attributes import Attributes
# Database class # Database class
from database import SQLDatabase # from database import SQLDatabase
from sqlite3 import Error # from sqlite3 import Error
# Graphing package # Graphing package
from database import MatplotlibGraph # from database import MatplotlibGraph
import matplotlib.pyplot as plt # import matplotlib.pyplot as plt
class GA(Attributes): class GA(Attributes):
@ -46,7 +46,6 @@ class GA(Attributes):
https://github.com/danielwilczak101/EasyGA/wiki https://github.com/danielwilczak101/EasyGA/wiki
""" """
def evolve(self: GA, number_of_generations: float = float('inf'), consider_termination: bool = True) -> None: def evolve(self: GA, number_of_generations: float = float('inf'), consider_termination: bool = True) -> None:
""" """
Evolves the ga until the ga is no longer active. Evolves the ga until the ga is no longer active.
@ -63,9 +62,12 @@ class GA(Attributes):
if self.population is None: if self.population is None:
self.initialize_population() self.initialize_population()
cond1 = lambda: number_of_generations > 0 # Evolve the specified number of generations. # Evolve the specified number of generations.
cond2 = lambda: not consider_termination # If consider_termination flag is set: def cond1(): return number_of_generations > 0
cond3 = lambda: cond2() or self.active() # check termination conditions. # If consider_termination flag is set:
def cond2(): return not consider_termination
# check termination conditions.
def cond3(): return cond2() or self.active()
while cond1() and cond3(): while cond1() and cond3():
@ -74,10 +76,11 @@ class GA(Attributes):
# Create the database here to allow the user to change the # Create the database here to allow the user to change the
# database name and structure before running the function. # database name and structure before running the function.
self.database.create_all_tables(self) # self.database.create_all_tables(self)
# Add the current configuration to the config table # Add the current configuration to the config table
self.database.insert_config(self) # self.database.insert_config(self)
pass
# Otherwise evolve the population. # Otherwise evolve the population.
else: else:
@ -102,10 +105,9 @@ class GA(Attributes):
if int(adapt_counter) < int(adapt_counter + self.adapt_rate): if int(adapt_counter) < int(adapt_counter + self.adapt_rate):
self.adapt() self.adapt()
number_of_generations -= 1 number_of_generations -= 1
self.current_generation += 1 self.current_generation += 1
def update_population(self: GA) -> None: def update_population(self: GA) -> None:
""" """
Updates the population to the new population Updates the population to the new population
@ -113,7 +115,6 @@ class GA(Attributes):
""" """
self.population.update() self.population.update()
def reset_run(self: GA) -> None: def reset_run(self: GA) -> None:
""" """
Resets a run by re-initializing the Resets a run by re-initializing the
@ -123,7 +124,6 @@ class GA(Attributes):
self.current_generation = 0 self.current_generation = 0
self.run += 1 self.run += 1
def adapt(self: GA) -> None: def adapt(self: GA) -> None:
"""Adapts the ga to hopefully get better results.""" """Adapts the ga to hopefully get better results."""
@ -134,7 +134,6 @@ class GA(Attributes):
self.set_all_fitness() self.set_all_fitness()
self.sort_by_best_fitness() self.sort_by_best_fitness()
def adapt_probabilities(self: GA) -> None: def adapt_probabilities(self: GA) -> None:
""" """
Modifies the parent ratio and mutation rates based on the adapt Modifies the parent ratio and mutation rates based on the adapt
@ -154,7 +153,7 @@ class GA(Attributes):
# Difference between best and i-th chromosomes # Difference between best and i-th chromosomes
best_chromosome = self.population[0] best_chromosome = self.population[0]
tol = lambda i: self.dist(best_chromosome, self.population[i]) def tol(i): return self.dist(best_chromosome, self.population[i])
# Too few converged: cross more and mutate less # Too few converged: cross more and mutate less
if tol(amount_converged//2) > tol(amount_converged//4)*2: if tol(amount_converged//2) > tol(amount_converged//4)*2:
@ -169,13 +168,14 @@ class GA(Attributes):
self.max_gene_mutation_rate) self.max_gene_mutation_rate)
# Weighted average of x and y # Weighted average of x and y
average = lambda x, y: weight * x + (1-weight) * y def average(x, y): return weight * x + (1-weight) * y
# Adjust rates towards the bounds # Adjust rates towards the bounds
self.selection_probability = average(bounds[0], self.selection_probability) self.selection_probability = average(
self.chromosome_mutation_rate = average(bounds[1], self.chromosome_mutation_rate) bounds[0], self.selection_probability)
self.gene_mutation_rate = average(bounds[2], self.gene_mutation_rate) self.chromosome_mutation_rate = average(
bounds[1], self.chromosome_mutation_rate)
self.gene_mutation_rate = average(bounds[2], self.gene_mutation_rate)
def adapt_population(self: GA) -> None: def adapt_population(self: GA) -> None:
""" """
@ -202,7 +202,7 @@ class GA(Attributes):
self.crossover_individual_impl( self.crossover_individual_impl(
self.population[n], self.population[n],
parent, parent,
weight = -3/4, weight=-3/4,
) )
# If negative weights can't be used or division by 0, use positive weight # If negative weights can't be used or division by 0, use positive weight
@ -210,7 +210,7 @@ class GA(Attributes):
self.crossover_individual_impl( self.crossover_individual_impl(
self.population[n], self.population[n],
parent, parent,
weight = +1/4, weight=+1/4,
) )
# Stop if we've filled up an entire population # Stop if we've filled up an entire population
@ -218,20 +218,19 @@ class GA(Attributes):
break break
# Replace worst chromosomes with new chromosomes, except for the previous best chromosome # Replace worst chromosomes with new chromosomes, except for the previous best chromosome
min_len = min(len(self.population)-1, len(self.population.next_population)) min_len = min(len(self.population)-1,
len(self.population.next_population))
if min_len > 0: if min_len > 0:
self.population[-min_len:] = self.population.next_population[:min_len] self.population[-min_len:] = self.population.next_population[:min_len]
self.population.next_population = [] self.population.next_population = []
self.population.mating_pool = [] self.population.mating_pool = []
def initialize_population(self: GA) -> None: def initialize_population(self: GA) -> None:
""" """
Sets self.population using the chromosome implementation and population size. Sets self.population using the chromosome implementation and population size.
""" """
self.population = self.make_population(self.population_impl()) self.population = self.make_population(self.population_impl())
def set_all_fitness(self: GA) -> None: def set_all_fitness(self: GA) -> None:
""" """
Sets the fitness of each chromosome in the population. Sets the fitness of each chromosome in the population.
@ -252,15 +251,14 @@ class GA(Attributes):
if chromosome.fitness is None or self.update_fitness: if chromosome.fitness is None or self.update_fitness:
chromosome.fitness = self.fitness_function_impl(chromosome) chromosome.fitness = self.fitness_function_impl(chromosome)
def sort_by_best_fitness( def sort_by_best_fitness(
self: GA, self: GA,
chromosome_list: Optional[ chromosome_list: Optional[
Union[MutableSequence[Chromosome], Union[MutableSequence[Chromosome],
Iterable[Chromosome]] Iterable[Chromosome]]
] = None, ] = None,
in_place: bool = True, in_place: bool = True,
) -> MutableSequence[Chromosome]: ) -> MutableSequence[Chromosome]:
""" """
Sorts the chromosome list by fitness based on fitness type. Sorts the chromosome list by fitness based on fitness type.
1st element has best fitness. 1st element has best fitness.
@ -315,7 +313,6 @@ class GA(Attributes):
else: else:
return sorted(chromosome_list, key=key, reverse=reverse) return sorted(chromosome_list, key=key, reverse=reverse)
def get_chromosome_fitness(self: GA, index: int) -> float: def get_chromosome_fitness(self: GA, index: int) -> float:
""" """
Computes the converted fitness of a chromosome at an index. Computes the converted fitness of a chromosome at an index.
@ -339,7 +336,6 @@ class GA(Attributes):
""" """
return self.convert_fitness(self.population[index].fitness) return self.convert_fitness(self.population[index].fitness)
def convert_fitness(self: GA, fitness: float) -> float: def convert_fitness(self: GA, fitness: float) -> float:
""" """
Calculates a modified version of the fitness for various Calculates a modified version of the fitness for various
@ -376,23 +372,19 @@ class GA(Attributes):
return max_fitness - fitness + min_fitness return max_fitness - fitness + min_fitness
def print_generation(self: GA) -> None: def print_generation(self: GA) -> None:
"""Prints the current generation.""" """Prints the current generation."""
print(f"Current Generation \t: {self.current_generation}") print(f"Current Generation \t: {self.current_generation}")
def print_population(self: GA) -> None: def print_population(self: GA) -> None:
"""Prints the entire population.""" """Prints the entire population."""
print(self.population) print(self.population)
def print_best_chromosome(self: GA) -> None: def print_best_chromosome(self: GA) -> None:
"""Prints the best chromosome and its fitness.""" """Prints the best chromosome and its fitness."""
print(f"Best Chromosome \t: {self.population[0]}") print(f"Best Chromosome \t: {self.population[0]}")
print(f"Best Fitness \t: {self.population[0].fitness}") print(f"Best Fitness \t: {self.population[0].fitness}")
def print_worst_chromosome(self: GA) -> None: def print_worst_chromosome(self: GA) -> None:
"""Prints the worst chromosome and its fitness.""" """Prints the worst chromosome and its fitness."""
print(f"Worst Chromosome \t: {self.population[-1]}") print(f"Worst Chromosome \t: {self.population[-1]}")

View File

@ -6,8 +6,8 @@ from dataclasses import dataclass, field, _MISSING_TYPE
from types import MethodType from types import MethodType
import random import random
import sqlite3 # import sqlite3
import matplotlib.pyplot as plt # import matplotlib.pyplot as plt
from structure import Population from structure import Population
from structure import Chromosome from structure import Chromosome
@ -19,12 +19,13 @@ from parent import Parent
from survivor import Survivor from survivor import Survivor
from crossover import Crossover from crossover import Crossover
from mutation import Mutation from mutation import Mutation
from database import SQLDatabase, MatplotlibGraph, SQLDatabase as Database, MatplotlibGraph as Graph # from database import SQLDatabase, MatplotlibGraph, SQLDatabase as Database, MatplotlibGraph as Graph
#========================================# #========================================#
# Default methods not defined elsewhere. # # Default methods not defined elsewhere. #
#========================================# #========================================#
def rand_1_to_10(self: Attributes) -> int: def rand_1_to_10(self: Attributes) -> int:
""" """
Default gene_impl, returning a random integer from 1 to 10. Default gene_impl, returning a random integer from 1 to 10.
@ -36,6 +37,7 @@ def rand_1_to_10(self: Attributes) -> int:
""" """
return random.randint(1, 10) return random.randint(1, 10)
def use_genes(self: Attributes) -> Iterator[Any]: def use_genes(self: Attributes) -> Iterator[Any]:
""" """
Default chromosome_impl, generates a chromosome using the gene_impl and chromosome length. Default chromosome_impl, generates a chromosome using the gene_impl and chromosome length.
@ -55,6 +57,7 @@ def use_genes(self: Attributes) -> Iterator[Any]:
for _ in range(self.chromosome_length): for _ in range(self.chromosome_length):
yield self.gene_impl() yield self.gene_impl()
def use_chromosomes(self: Attributes) -> Iterator[Iterable[Any]]: def use_chromosomes(self: Attributes) -> Iterator[Iterable[Any]]:
""" """
Default population_impl, generates a population using the chromosome_impl and population size. Default population_impl, generates a population using the chromosome_impl and population size.
@ -74,6 +77,7 @@ def use_chromosomes(self: Attributes) -> Iterator[Iterable[Any]]:
for _ in range(self.population_size): for _ in range(self.population_size):
yield self.chromosome_impl() yield self.chromosome_impl()
def dist_fitness(self: Attributes, chromosome_1: Chromosome, chromosome_2: Chromosome) -> float: def dist_fitness(self: Attributes, chromosome_1: Chromosome, chromosome_2: Chromosome) -> float:
""" """
Measures the distance between two chromosomes based on their fitnesses. Measures the distance between two chromosomes based on their fitnesses.
@ -90,6 +94,7 @@ def dist_fitness(self: Attributes, chromosome_1: Chromosome, chromosome_2: Chrom
""" """
return sqrt(abs(chromosome_1.fitness - chromosome_2.fitness)) return sqrt(abs(chromosome_1.fitness - chromosome_2.fitness))
def simple_linear(self: Attributes, weight: float) -> float: def simple_linear(self: Attributes, weight: float) -> float:
""" """
Returns a random value between 0 and 1, with increased probability Returns a random value between 0 and 1, with increased probability
@ -181,26 +186,27 @@ class AttributesData:
parent_selection_impl: Callable[["Attributes"], None] = None parent_selection_impl: Callable[["Attributes"], None] = None
crossover_individual_impl: Callable[["Attributes"], None] = None crossover_individual_impl: Callable[["Attributes"], None] = None
crossover_population_impl: Callable[["Attributes", Chromosome, Chromosome], None] = None crossover_population_impl: Callable[[
"Attributes", Chromosome, Chromosome], None] = None
survivor_selection_impl: Callable[["Attributes"], None] = None survivor_selection_impl: Callable[["Attributes"], None] = None
mutation_individual_impl: Callable[["Attributes", Chromosome], None] = None mutation_individual_impl: Callable[["Attributes", Chromosome], None] = None
mutation_population_impl: Callable[["Attributes"], None] = None mutation_population_impl: Callable[["Attributes"], None] = None
termination_impl: Callable[["Attributes"], bool] = None termination_impl: Callable[["Attributes"], bool] = None
database: Database = field(default_factory=SQLDatabase) # database: Database = field(default_factory=SQLDatabase)
database_name: str = "database.db" #database_name: str = "database.db"
save_data: bool = True #save_data: bool = True
sql_create_data_structure: str = """ # sql_create_data_structure: str = """
CREATE TABLE IF NOT EXISTS data ( # CREATE TABLE IF NOT EXISTS data (
id INTEGER PRIMARY KEY, # id INTEGER PRIMARY KEY,
config_id INTEGER DEFAULT NULL, # config_id INTEGER DEFAULT NULL,
generation INTEGER NOT NULL, # generation INTEGER NOT NULL,
fitness REAL, # fitness REAL,
chromosome TEXT # chromosome TEXT
); # );
""" # """
graph: Callable[[Database], Graph] = MatplotlibGraph # graph: Callable[[Database], Graph] = MatplotlibGraph
def __post_init__(self: AttributesData) -> None: def __post_init__(self: AttributesData) -> None:
""" """
@ -311,7 +317,8 @@ class Attributes(AttributesData):
chromosome : Chromosome chromosome : Chromosome
The chromosome to be saved. The chromosome to be saved.
""" """
self.database.insert_current_chromosome(self.current_generation, chromosome) self.database.insert_current_chromosome(
self.current_generation, chromosome)
#===========================# #===========================#
# Descriptors which convert # # Descriptors which convert #
@ -327,13 +334,20 @@ class Attributes(AttributesData):
population_impl = AsMethod("population_impl", use_chromosomes) population_impl = AsMethod("population_impl", use_chromosomes)
dist = AsMethod("dist", dist_fitness) dist = AsMethod("dist", dist_fitness)
weighted_random = AsMethod("weighted_random", simple_linear) weighted_random = AsMethod("weighted_random", simple_linear)
parent_selection_impl = AsMethod("parent_selection_impl", Parent.Rank.tournament) parent_selection_impl = AsMethod(
crossover_individual_impl = AsMethod("crossover_individual_impl", Crossover.Individual.single_point) "parent_selection_impl", Parent.Rank.tournament)
crossover_population_impl = AsMethod("crossover_population_impl", Crossover.Population.sequential) crossover_individual_impl = AsMethod(
survivor_selection_impl = AsMethod("survivor_selection_impl", Survivor.fill_in_best) "crossover_individual_impl", Crossover.Individual.single_point)
mutation_individual_impl = AsMethod("mutation_individual_impl", Mutation.Individual.individual_genes) crossover_population_impl = AsMethod(
mutation_population_impl = AsMethod("mutation_population_impl", Mutation.Population.random_avoid_best) "crossover_population_impl", Crossover.Population.sequential)
termination_impl = AsMethod("termination_impl", Termination.fitness_generation_tolerance) survivor_selection_impl = AsMethod(
"survivor_selection_impl", Survivor.fill_in_best)
mutation_individual_impl = AsMethod(
"mutation_individual_impl", Mutation.Individual.individual_genes)
mutation_population_impl = AsMethod(
"mutation_population_impl", Mutation.Population.random_avoid_best)
termination_impl = AsMethod(
"termination_impl", Termination.fitness_generation_tolerance)
#=============# #=============#
# Properties: # # Properties: #
@ -346,7 +360,8 @@ class Attributes(AttributesData):
@run.setter @run.setter
def run(self: AttributesProperties, value: int) -> None: def run(self: AttributesProperties, value: int) -> None:
if not isinstance(value, int) or value < 0: if not isinstance(value, int) or value < 0:
raise ValueError("ga.run counter must be an integer greater than or equal to 0.") raise ValueError(
"ga.run counter must be an integer greater than or equal to 0.")
vars(self)["run"] = value vars(self)["run"] = value
@property @property
@ -356,7 +371,8 @@ class Attributes(AttributesData):
@current_generation.setter @current_generation.setter
def current_generation(self: AttributesProperties, value: int) -> None: def current_generation(self: AttributesProperties, value: int) -> None:
if not isinstance(value, int) or value < 0: if not isinstance(value, int) or value < 0:
raise ValueError("ga.current_generation must be an integer greater than or equal to 0") raise ValueError(
"ga.current_generation must be an integer greater than or equal to 0")
vars(self)["current_generation"] = value vars(self)["current_generation"] = value
@property @property
@ -366,7 +382,8 @@ class Attributes(AttributesData):
@chromosome_length.setter @chromosome_length.setter
def chromosome_length(self: AttributesProperties, value: int) -> None: def chromosome_length(self: AttributesProperties, value: int) -> None:
if not isinstance(value, int) or value <= 0: if not isinstance(value, int) or value <= 0:
raise ValueError("ga.chromosome_length must be an integer greater than and not equal to 0.") raise ValueError(
"ga.chromosome_length must be an integer greater than and not equal to 0.")
vars(self)["chromosome_length"] = value vars(self)["chromosome_length"] = value
@property @property
@ -376,7 +393,8 @@ class Attributes(AttributesData):
@population_size.setter @population_size.setter
def population_size(self: AttributesProperties, value: int) -> None: def population_size(self: AttributesProperties, value: int) -> None:
if not isinstance(value, int) or value <= 0: if not isinstance(value, int) or value <= 0:
raise ValueError("ga.population_size must be an integer greater than and not equal to 0.") raise ValueError(
"ga.population_size must be an integer greater than and not equal to 0.")
vars(self)["population_size"] = value vars(self)["population_size"] = value
@property @property
@ -393,7 +411,8 @@ class Attributes(AttributesData):
if value is None or (isinstance(value, (float, int)) and 0 <= value <= 1): if value is None or (isinstance(value, (float, int)) and 0 <= value <= 1):
vars(self)["max_chromosome_mutation_rate"] = value vars(self)["max_chromosome_mutation_rate"] = value
else: else:
raise ValueError("Max chromosome mutation rate must be between 0 and 1") raise ValueError(
"Max chromosome mutation rate must be between 0 and 1")
@property @property
def min_chromosome_mutation_rate(self: AttributesProperties) -> float: def min_chromosome_mutation_rate(self: AttributesProperties) -> float:
@ -409,26 +428,27 @@ class Attributes(AttributesData):
if value is None or (isinstance(value, (float, int)) and 0 <= value <= 1): if value is None or (isinstance(value, (float, int)) and 0 <= value <= 1):
vars(self)["min_chromosome_mutation_rate"] = value vars(self)["min_chromosome_mutation_rate"] = value
else: else:
raise ValueError("Min chromosome mutation rate must be between 0 and 1") raise ValueError(
"Min chromosome mutation rate must be between 0 and 1")
@property # @property
def database_name(self: AttributesProperties) -> str: # def database_name(self: AttributesProperties) -> str:
return vars(self)["database_name"] # return vars(self)["database_name"]
@database_name.setter # @database_name.setter
def database_name(self: AttributesProperties, name: str) -> None: # def database_name(self: AttributesProperties, name: str) -> None:
# Update the database's name. # # Update the database's name.
self.database._database_name = name # self.database._database_name = name
# Set the attribute for itself. # # Set the attribute for itself.
vars(self)["database_name"] = name # vars(self)["database_name"] = name
@property # @property
def graph(self: AttributesProperties) -> Graph: # def graph(self: AttributesProperties) -> Graph:
return vars(self)["graph"] # return vars(self)["graph"]
@graph.setter # @graph.setter
def graph(self: AttributesProperties, graph: Callable[[Database], Graph]) -> None: # def graph(self: AttributesProperties, graph: Callable[[Database], Graph]) -> None:
vars(self)["graph"] = graph(self.database) # vars(self)["graph"] = graph(self.database)
@property @property
def active(self: AttributesProperties) -> Callable[[], bool]: def active(self: AttributesProperties) -> Callable[[], bool]:

View File

@ -2,7 +2,7 @@
# EasyGA - Genetic Algorithms made Easy # EasyGA - Genetic Algorithms made Easy
EasyGA is a python package designed to provide an easy-to-use Genetic Algorithm. The package is designed to work right out of the box, while also allowing the user to customize features as they see fit. EasyGA is a python package designed to provide an easy-to-use Genetic Algorithm. The package is designed to work right out of the box, while also allowing the user to customize features as they see fit.
## Check out our [Wiki](https://github.com/danielwilczak101/EasyGA/wiki) or [Youtube](https://www.youtube.com/watch?v=jbuDKwIiYBw) for more information. ## Check out our [Wiki](https://github.com/danielwilczak101/EasyGA/wiki) or [Youtube](https://www.youtube.com/watch?v=jbuDKwIiYBw) for more information.
@ -15,7 +15,9 @@ pip3 install EasyGA
``` ```
## Getting started with EasyGA(Basic Example): ## Getting started with EasyGA(Basic Example):
The goal of the basic example is to get all 5's in the chromosome. The goal of the basic example is to get all 5's in the chromosome.
```Python ```Python
import EasyGA import EasyGA
@ -31,6 +33,7 @@ ga.print_population()
``` ```
### Output: ### Output:
```bash ```bash
Current Generation : 15 Current Generation : 15
Current population: Current population:
@ -47,6 +50,7 @@ Chromosome - 9 [7][2][8][10][3][5][5][8][1][7] / Fitness = 2
``` ```
## Getting started with EasyGA (Password Cracker Example): ## Getting started with EasyGA (Password Cracker Example):
```Python ```Python
import EasyGA import EasyGA
import random import random
@ -94,8 +98,9 @@ ga.graph.show()
``` ```
## Ouput: ## Ouput:
``` ```
Please enter a word: Please enter a word:
EasyGA EasyGA
Current Generation : 44 Current Generation : 44
Chromosome - 0 [E][a][s][y][G][A] / Fitness = 6 Chromosome - 0 [E][a][s][y][G][A] / Fitness = 6
@ -113,8 +118,8 @@ Chromosome - 9 [E][a][s][Y][G][A] / Fitness = 5
<img width="500px" src="https://raw.githubusercontent.com/danielwilczak101/EasyGA/media/images/password_cracker_results.png" /> <img width="500px" src="https://raw.githubusercontent.com/danielwilczak101/EasyGA/media/images/password_cracker_results.png" />
## Issues ## Issues
We would love to know if your having any issues. Please start a new issue on the [Issues Page](https://github.com/danielwilczak101/EasyGA/issues).
We would love to know if your having any issues. Please start a new issue on the [Issues Page](https://github.com/danielwilczak101/EasyGA/issues).
## Local System Approach ## Local System Approach
@ -123,6 +128,7 @@ Download the repository to some folder on your computer.
``` ```
https://github.com/danielwilczak101/EasyGA/archive/master.zip https://github.com/danielwilczak101/EasyGA/archive/master.zip
``` ```
Use the run.py file inside the EasyGA folder to run your code. This is a local version of the package.
Use the run.py file inside the EasyGA folder to run your code. This is a local version of the package.
## Check out our [wiki](https://github.com/danielwilczak101/EasyGA/wiki) for more information. ## Check out our [wiki](https://github.com/danielwilczak101/EasyGA/wiki) for more information.

View File

@ -13,16 +13,17 @@ setuptools.setup(
url="https://github.com/danielwilczak101/EasyGA", url="https://github.com/danielwilczak101/EasyGA",
author="Daniel Wilczak, Jack RyanNguyen, Ryley Griffith, Jared Curtis, Matthew Chase Oxamendi ", author="Daniel Wilczak, Jack RyanNguyen, Ryley Griffith, Jared Curtis, Matthew Chase Oxamendi ",
author_email="danielwilczak101@gmail.com", author_email="danielwilczak101@gmail.com",
long_description = long_description, long_description=long_description,
long_description_content_type = "text/markdown", long_description_content_type="text/markdown",
classifier=[ classifier=[
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",
"License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)", "License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)",
"Operating System :: OS Independent", "Operating System :: OS Independent",
], ],
install_requires = ["matplotlib ~= 3.3.2", install_requires=[
"pyserial ~= 3.4", # "matplotlib ~= 3.3.2",
"pytest>=3.7", # "pyserial ~= 3.4",
"tabulate >=0.8.7" "pytest>=3.7",
], "tabulate >=0.8.7"
) ],
)