Compare commits

..

10 Commits

12 changed files with 264 additions and 106 deletions

10
CITATION.cff Normal file
View File

@ -0,0 +1,10 @@
cff-version: 1.2.0
message: "If you use this software, please cite it as below."
authors:
- family-names: Wilczak
given-names: Daniel
- family-names: Nguyen
given-names: Jack
title: "EasyGA - Genetic Algorithms made Easy"
version: 2.0.4
date-released: 2021-13-05

View File

@ -28,12 +28,12 @@ from mutation import Mutation
from attributes import Attributes from attributes import Attributes
# Database class # Database class
from database import sql_database # from database import SQLDatabase
from sqlite3 import Error # from sqlite3 import Error
# Graphing package # Graphing package
from database import matplotlib_graph # 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:
@ -105,7 +108,6 @@ class GA(Attributes):
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,14 +168,15 @@ 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.chromosome_mutation_rate = average(
bounds[1], self.chromosome_mutation_rate)
self.gene_mutation_rate = average(bounds[2], self.gene_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:
""" """
Performs weighted crossover between the best chromosome and Performs weighted crossover between the best chromosome and
@ -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,7 +251,6 @@ 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[
@ -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,13 +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 sql_database, matplotlib_graph # from database import SQLDatabase, MatplotlibGraph, SQLDatabase as Database, MatplotlibGraph as Graph
from database import sql_database as Database, matplotlib_graph 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.
@ -37,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.
@ -56,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.
@ -75,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.
@ -91,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
@ -182,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=sql_database.SQL_Database) # 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] = matplotlib_graph.Matplotlib_Graph # graph: Callable[[Database], Graph] = MatplotlibGraph
def __post_init__(self: AttributesData) -> None: def __post_init__(self: AttributesData) -> None:
""" """
@ -312,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 #
@ -328,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: #
@ -347,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
@ -357,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
@ -367,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
@ -377,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
@ -394,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:
@ -410,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

@ -0,0 +1,2 @@
from .sql_database import SQLDatabase
from .matplotlib_graph import MatplotlibGraph

View File

@ -3,7 +3,7 @@ import matplotlib.pyplot as plt
import numpy as np import numpy as np
class Matplotlib_Graph: class MatplotlibGraph:
"""Prebuilt graphing functions to make visual """Prebuilt graphing functions to make visual
represention of fitness data.""" represention of fitness data."""

View File

@ -4,7 +4,7 @@ from sqlite3 import Error
from tabulate import tabulate from tabulate import tabulate
class SQL_Database: class SQLDatabase:
"""Main database class that controls all the functionality for input / """Main database class that controls all the functionality for input /
out of the database using SQLite3.""" out of the database using SQLite3."""

View File

@ -1,4 +1,4 @@
![](https://raw.githubusercontent.com/danielwilczak101/EasyGA/media/images/easyGA_logo.png) ![](https://raw.githubusercontent.com/danielwilczak101/EasyGA/media/images/banner-logo.png)
# EasyGA - Genetic Algorithms made Easy # EasyGA - Genetic Algorithms made Easy
@ -15,6 +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.
```Python ```Python
import EasyGA import EasyGA
@ -30,6 +33,7 @@ ga.print_population()
``` ```
### Output: ### Output:
```bash ```bash
Current Generation : 15 Current Generation : 15
Current population: Current population:
@ -46,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
@ -93,6 +98,7 @@ ga.graph.show()
``` ```
## Ouput: ## Ouput:
``` ```
Please enter a word: Please enter a word:
EasyGA EasyGA
@ -112,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
@ -122,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.

20
docs/Makefile Normal file
View File

@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

35
docs/make.bat Normal file
View File

@ -0,0 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd

52
docs/source/conf.py Normal file
View File

@ -0,0 +1,52 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
project = 'EasyGA'
copyright = '2021, Daniel Wilczak, Jack Nguyen'
author = 'Daniel Wilczak, Jack Nguyen'
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

20
docs/source/index.rst Normal file
View File

@ -0,0 +1,20 @@
.. EasyGA documentation master file, created by
sphinx-quickstart on Thu Aug 26 12:53:45 2021.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to EasyGA's documentation!
==================================
.. toctree::
:maxdepth: 2
:caption: Contents:
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

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",
# "pyserial ~= 3.4",
"pytest>=3.7", "pytest>=3.7",
"tabulate >=0.8.7" "tabulate >=0.8.7"
], ],
) )