Skip to content

Commit

Permalink
formatted the Python modules to be at least partially pep8-compliant;…
Browse files Browse the repository at this point in the history
… I also improved the README file with more info; I added the full GNU v3 license for reference.
  • Loading branch information
nbro committed Dec 30, 2020
1 parent e523587 commit 58b375d
Show file tree
Hide file tree
Showing 64 changed files with 1,642 additions and 914 deletions.
675 changes: 674 additions & 1 deletion LICENSE

Large diffs are not rendered by default.

41 changes: 18 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,28 @@
# Introduction
--------------

Grammatical Evolution (GE) is a population-based evolutionary algorithm, where a BNF-style grammar is used in the genotype to phenotype mapping process [O'Neill & Ryan, 2003].

PonyGE2 is an implementation of GE in Python. It's intended as an advertisement and a starting-point for those new to GE, a reference for students and researchers, a rapid-prototyping medium for our own experiments, and as a Python workout.

The original version of PonyGE (https://github.com/jmmcd/ponyge) was originally designed to be a small single-file implementation of GE. However, over time this has grown to a stage where a more formal structured approach was needed. This has led to the development of PonyGE2 (https://github.com/PonyGE/PonyGE2), presented here.

A technical paper describing PonyGE2 has been published and been made freely available on arXiv at:

https://arxiv.org/abs/1703.08535
A technical paper describing PonyGE2 has been published and been made freely available on arXiv [here](https://arxiv.org/abs/1703.08535).

PonyGE2 can be referenced using the following citation:

Fenton, M., McDermott, J., Fagan, D., Forstenlechner, S., Hemberg, E., and O'Neill, M. PonyGE2: Grammatical Evolution in Python. arXiv preprint, arXiv:1703.08535, 2017.

The PonyGE2 development team can be contacted via [GitHub](https://github.com/jmmcd/PonyGE2/issues/new).

PonyGE2 is copyright (C) 2009-2017

> Fenton, M., McDermott, J., Fagan, D., Forstenlechner, S., Hemberg, E., and O'Neill, M. PonyGE2: Grammatical Evolution in Python. arXiv preprint, arXiv:1703.08535, 2017.
# Requirements
--------------

PonyGE2 requires Python 3.5 or higher.
Using matplotlib, numpy, scipy, scikit-learn (sklearn), pandas.
We don't provide any `setup.py` script for now, so you cannot yet pip-install PonyGE2. PonyGE2 requires Python 3.5 (or higher) and it uses matplotlib, numpy, scipy, scikit-learn (sklearn), pandas, which can be installed as follows

All requirements can be satisfied with [Anaconda](https://www.continuum.io/downloads).
pip install -r requirements.txt

All requirements can be satisfied with [Anaconda](https://www.continuum.io/downloads).

# Running PonyGE2
-----------------

We don't provide any setup script. You can run an example problem (the default is regression, see below) just by typing:
You can run an example problem (the default is regression, see below) just by typing:

$ cd src
$ python ponyge.py
Expand All @@ -50,20 +40,25 @@ There are a number of arguments that can be used for passing values via the comm


# About PonyGE2
---------------

Grammatical Evolution (GE) [O'Neill & Ryan, 2003] is a grammar-based form of Genetic Programming [Koza, 1992]. It marries principles from molecular biology to the representational power of formal grammars. GE’s rich modularity gives a unique flexibility, making it possible to use alternative search strategies, whether evolutionary, deterministic or some other approach, and to radically change its behaviour by merely changing the grammar supplied. As a grammar is used to describe the structures that are generated by GE, it is trivial to modify the output structures by simply editing the plain text grammar. This is one of the main advantages that makes the GE approach so attractive. The genotype-phenotype mapping also means that instead of operating exclusively on solution trees, as in standard GP, GE allows search operators to be performed on the genotype (e.g., integer or binary chromosomes), in addition to partially derived phenotypes, and the fully formed phenotypic derivation trees themselves.

PonyGE2 is primarily a Python implementation of canonical Grammatical Evolution, but it also includes a number of other popular techniques and EC aspects.

# For full documentation of PonyGE2, see the [wiki](https://github.com/PonyGE/PonyGE2/wiki).
---------------------------------------------------------------------------
# Documentation

For full documentation of PonyGE2, see the [wiki](https://github.com/PonyGE/PonyGE2/wiki).

# Contact

The PonyGE2 development team can be contacted via [GitHub](https://github.com/jmmcd/PonyGE2/issues/new).

# License

https://github.com/PonyGE/PonyGE2/wiki
PonyGE2 is hereby licensed for use under the GNU General Public License v3.0. See the file [LICENSE](./LICENSE) for more details. PonyGE2 is copyright (C) 2009-2017.

# References
------------

Michael O'Neill and Conor Ryan, "Grammatical Evolution: Evolutionary Automatic Programming in an Arbitrary Language", Kluwer Academic Publishers, 2003.
- Michael O'Neill and Conor Ryan, "Grammatical Evolution: Evolutionary Automatic Programming in an Arbitrary Language", Kluwer Academic Publishers, 2003.

Koza, J.R., 1992. "Genetic programming: on the programming of computers by means of natural selection (Vol. 1)". MIT press.
- Koza, J.R., 1992. "Genetic programming: on the programming of computers by means of natural selection (Vol. 1)". MIT press.
40 changes: 20 additions & 20 deletions src/agent/agent.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from operators.initialisation import initialisation
from fitness.evaluation import evaluate_fitness
from stats.stats import stats, get_stats
from operators.crossover import crossover
from operators.initialisation import initialisation
from operators.mutation import mutation
from operators.replacement import replacement, steady_state
from operators.replacement import replacement
from operators.selection import selection
from stats.stats import get_stats

class Agent():

class Agent:
"""
Class representing individual robots/agents. The agents has three main methods.
Sense - method responsible for getting information from the environment from different sensors
Expand All @@ -19,15 +20,14 @@ def __init__(self, ip):
self.interaction_probability = ip

# Only initialize single individual. Single agent can only have single genetic information
self.individual = initialisation(1)
self.individual = initialisation(1)

# Evaluate the fitness for the the individual
self.individual = evaluate_fitness(self.individual)

# Flag which store the boolean value for other neighbouring agents found or not
self.agents_found = False


def sense(self, agents):
# This part makes this GE algorithm useful for multi-agent systems. This method is responsible to sense
# information from the environment
Expand All @@ -40,25 +40,26 @@ def sense(self, agents):
# the agent has found some nearby agents. Higher the probability, better the chance of agent to share its
# gnome with other agents
if random.random() > self.interaction_probability:
#Turn the flag to True
# Turn the flag to True
self.agents_found = True

# Getting values to sample agents for interaction
range_min = int( (self.interaction_probability * len(agents) ) / 3)
range_max = int( (self.interaction_probability * len(agents) ) / 2)
range_avg = int( (range_min + range_max) / 2)
range_min = int((self.interaction_probability * len(agents)) / 3)
range_max = int((self.interaction_probability * len(agents)) / 2)
range_avg = int((range_min + range_max) / 2)

# Sample the agents from the list of agents. The number of samples depend on above values
no_agents_found = random.sample(range (len(agents) ), random.choice( [range_min,range_max,range_avg] ) )

no_agents_found = random.sample(range(len(agents)), random.choice(
[range_min, range_max, range_avg]))

# Extract the individuals from the nearby agents and store the individuals in the class variable
self.nearby_agents = [ agents[id].individual[0] for id in no_agents_found ]
self.nearby_agents = [agents[id].individual[0] for id in
no_agents_found]

def act(self):
# Process the information if the agent has sense nearby agents
if self.agents_found:

# Combine the original individual and individuals found by interacting with nearby agents to form
# Combine the original individual and individuals found by interacting with nearby agents to form
# a population
individuals = self.individual + self.nearby_agents

Expand All @@ -79,16 +80,15 @@ def act(self):

# Generate statistics for run so far
get_stats(individuals)

# Sort the individuals list
individuals.sort(reverse=True)

# Get the highest performing individual from the sorted population
self.new_individual = individuals[0]

def update(self):
#Update the information if the agent has sense nearby agents
# Update the information if the agent has sense nearby agents
if self.agents_found:

# Replace the individual with the highest performing individual obtained from act method
self.individual = [self.new_individual]
self.individual = [self.new_individual]
29 changes: 16 additions & 13 deletions src/algorithm/distributed_algorithm/search_loop.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,33 @@
from agent.agent import Agent
from algorithm.parameters import params
#from fitness.evaluation import evaluate_fitness
from stats.stats import stats
#from utilities.stats import trackers
#from operators.initialisation import initialisation
#from utilities.algorithm.initialise_run import pool_init

def create_agents(n,p):

# from fitness.evaluation import evaluate_fitness
# from utilities.stats import trackers
# from operators.initialisation import initialisation
# from utilities.algorithm.initialise_run import pool_init

def create_agents(n, p):
"""
Create a list of agent specified by n parameter
"""
return [ Agent(p) for a in range(n) ]
return [Agent(p) for a in range(n)]


def search_loop():
"""
This loop is used when the multi-agent parameter is passed
"""
# Create a list of agents based on the parameter passed
agents = create_agents(params['AGENT_SIZE'],params['INTERACTION_PROBABILITY'])
agents = create_agents(params['AGENT_SIZE'],
params['INTERACTION_PROBABILITY'])

##Multi-Agent based GE
for generation in range(1,(params['GENERATIONS']+1)):
## Multi-Agent based GE
for generation in range(1, (params['GENERATIONS'] + 1)):
stats['gen'] = generation
#New generation

# New generation
agents = params['STEP'](agents)
return [agent.individual[0] for agent in agents]

return [agent.individual[0] for agent in agents]
9 changes: 4 additions & 5 deletions src/algorithm/distributed_algorithm/step.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

def step(agents):
"""
Runs a single generation of the evolutionary algorithm process
Expand All @@ -7,11 +6,11 @@ def step(agents):
for agent in agents:
# Sense the environment
agent.sense(agents)

# Based on the values from the sensor perform action
agent.act()

# Update the state of the agent
agent.update()
return agents
agent.update()

return agents
31 changes: 15 additions & 16 deletions src/algorithm/hill_climbing.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from algorithm.parameters import params
from fitness.evaluation import evaluate_fitness
from stats.stats import stats, get_stats
from stats.stats import get_stats, stats
from utilities.stats import trackers


"""Hill-climbing is just about the simplest meta-heuristic there
is. It's of interest in GP/GE because of the lingering suspicion among
many researchers that crossover just doesn't work. This goes back to
Expand Down Expand Up @@ -92,15 +91,15 @@ def LAHC_search_loop():

# Find the best individual so far.
best = trackers.best_ever

# Set history.
Lfa = params['HILL_CLIMBING_HISTORY']
history = [best for _ in range(Lfa)]

# iters is the number of individuals examined so far.
iters = len(individuals)
for generation in range(1, (params['GENERATIONS']+1)):

for generation in range(1, (params['GENERATIONS'] + 1)):

this_gen = []

Expand All @@ -119,16 +118,16 @@ def LAHC_search_loop():
# Find the index of the relevant individual from the late
# acceptance history.
idx = iters % Lfa

if candidate_best >= history[idx]:
best = candidate_best # Accept the candidate

else:
pass # reject the candidate

# Set the new best into the history.
history[idx] = best

# Increment evaluation counter.
iters += 1

Expand Down Expand Up @@ -194,7 +193,7 @@ def SCHC_search_loop():
:return: The final population.
"""

# Calculate maximum number of evaluation iterations.
max_its = params['POPULATION_SIZE'] * params['GENERATIONS']
count_method = params['SCHC_COUNT_METHOD']
Expand All @@ -215,11 +214,11 @@ def SCHC_search_loop():
# Set history and counter.
history = params['HILL_CLIMBING_HISTORY']
counter = 0

# iters is the number of individuals examined/iterations so far.
iters = len(individuals)
for generation in range(1, (params['GENERATIONS']+1)):

for generation in range(1, (params['GENERATIONS'] + 1)):

this_gen = []

Expand All @@ -238,15 +237,15 @@ def SCHC_search_loop():
# count
if count_method == "count_all": # we count all iterations (moves)
counter += 1 # increment the counter

elif count_method == "acp": # we count accepted moves only
if candidate_best > cost_bound or candidate_best >= best:
counter += 1 # increment the counter

elif count_method == "imp": # we count improving moves only
if candidate_best > best:
counter += 1 # increment the counter

else:
s = "algorithm.hill_climbing.SCHC_search_loop\n" \
"Error: Unknown count method: %s" % (count_method)
Expand All @@ -255,7 +254,7 @@ def SCHC_search_loop():
# accept
if candidate_best > cost_bound or candidate_best >= best:
best = candidate_best # accept the candidate

else:
pass # reject the candidate

Expand Down
Loading

0 comments on commit 58b375d

Please sign in to comment.