Skip to content

Commit

Permalink
added progress bar to parallel loop, initialize pop and update pop, a…
Browse files Browse the repository at this point in the history
…dded __main__ function
  • Loading branch information
f0uriest committed Jan 1, 2019
1 parent 7acf001 commit 8217c75
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 34 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ __pycache__/
docs/_build
docs/_static
.DS_Store
.cache/
.cache/
*.egg-info
3 changes: 0 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
language: python
python:
- "2.7"
- "3.4"
- "3.5"
- "3.6"
matrix:
Expand All @@ -11,8 +10,6 @@ matrix:
sudo: true
allow_failures:
- python: "2.7"
- python: "3.4"
- python: "3.5"
# command to install dependencies
install:
# add new stuff to requirements file, not here
Expand Down
14 changes: 7 additions & 7 deletions gastop/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from gastop.truss import Truss
from gastop.evaluator import Evaluator
from gastop.crossover import Crossover
from gastop.mutator import Mutator
from gastop.selector import Selector
from gastop.fitness import FitnessFunction
from gastop.genalg import GenAlg
from .truss import Truss
from .evaluator import Evaluator
from .crossover import Crossover
from .mutator import Mutator
from .selector import Selector
from .fitness import FitnessFunction
from .genalg import GenAlg
98 changes: 98 additions & 0 deletions gastop/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
def main():
import numpy as np
import argparse
from gastop import GenAlg, utilities

parser = argparse.ArgumentParser(prog='gastop',
description="A Genetic Algorithm for Structural Design and Topological Optimization. See full documentation at gastop.readthedocs.io")
parser.add_argument("config_path", help="file path to gastop config file")
parser.add_argument("-p", "--pop_size", type=int,
help="population size. If not specified, defaults to what is in config.", metavar='')
parser.add_argument("-g", "--num_gens", type=int,
help="number of generations. If not specified, defaults to what is in config.", metavar='')
parser.add_argument("-t", "--num_threads", type=int,
help="number of threads to use. If not specified, defaults to what is in config.", metavar='')
group = parser.add_mutually_exclusive_group()
group.add_argument("-q", "--quiet", action="store_true",
help="hide progress display window")
group.add_argument("-d", "--display", action='store_true',
help="show progress display window")
args = parser.parse_args()

config = utilities.init_file_parser(args.config_path)

if args.display:
progress_display == 2
elif args.quiet:
progress_display == 1
else:
progress_display = None

if args.num_threads:
num_threads = args.num_threads
else:
num_threads = config['ga_params']['num_threads']

if args.num_gens:
num_generations = args.num_gens
else:
num_generations = config['ga_params']['num_generations']

if args.pop_size:
pop_size = args.pop_size
else:
pop_size = config['ga_params']['pop_size']

# Create the Genetic Algorithm Object
ga = GenAlg(config)
ga.initialize_population(pop_size)
best, progress_history = ga.run(num_generations=num_generations,
progress_display=progress_display,
num_threads=num_threads)

con = best.edges.copy()
matl = best.properties.copy()

# remove self connected edges and duplicate members
matl = matl[(con[:, 0]) >= 0]
con = con[(con[:, 0]) >= 0]
matl = matl[(con[:, 1]) >= 0]
con = con[(con[:, 1]) >= 0]
con = con.astype(int)

np.set_printoptions(precision=2)

print('\n')
print('Nodes:')
print(' x y z ')
print(best.rand_nodes)

print('\n')
print('Edges:')
print(con)

print('\n')
print('Properties:')
print(matl)

print('\n')
print('Mass: %.3f kg' % best.mass)

print('\n')
print('FoS:')
print(best.fos)

print('\n')
print('Deflections (m): ')
print(' Dx Dy Dz Rx Ry Rz ')
print(best.deflection[:, :, 0])

if progress_display == 2:
best.plot(domain=config['random_params']['domain'].T,
loads=config['evaluator_params']['boundary_conditions']['loads'],
fixtures=config['evaluator_params']['boundary_conditions']['fixtures'],
deflection=True, load_scale=.001, def_scale=100)


if __name__ == '__main__':
main()
49 changes: 32 additions & 17 deletions gastop/genalg.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def generate_random(self, _): # Dan

return Truss(user_spec_nodes, new_nodes, new_edges, new_properties)

def initialize_population(self, pop_size):
def initialize_population(self, pop_size=None):
'''Initializes population with randomly creates Truss objects
Args:
Expand All @@ -127,7 +127,10 @@ def initialize_population(self, pop_size):
Returns:
Updated self.population
'''
self.ga_params['pop_size'] = pop_size
if pop_size:
self.ga_params['pop_size'] = pop_size
else:
pop_size = self.ga_params['pop_size']

# Try 1: t= 0.298
# self.population = [self.generate_random() for i in range(pop_size)]
Expand All @@ -146,7 +149,8 @@ def initialize_population(self, pop_size):

# Try 4: t=0.232
pool = Pool()
self.population = pool.map(self.generate_random, range(pop_size))
self.population = list(tqdm(pool.imap(
self.generate_random, range(pop_size), int(np.sqrt(pop_size))), total=pop_size, leave=False, desc='Initializing Population', position=0))
pool.close()
pool.join()

Expand All @@ -167,39 +171,43 @@ def run(self, num_generations=None, progress_display=1, num_threads=None):
ax1 = fig.add_subplot(1, 1, 1)
plt.ylabel('fos')
plt.xlabel('iteration')
#

# wrapper function for evaluation and scoring
def score(truss):
return self.fitness_function(self.evaluator(truss))

# Loop over all generations:

# Without any parallelization:
# Try 1: time =
if num_threads == 1:
for current_gen in tqdm(range(num_generations), desc='Overall', position=0):
self.ga_params['current_generation'] = current_gen
# Loop over all trusses -> PARALLELIZE. Later
for current_truss in tqdm(self.population, desc='Current Generation', leave=False, position=1):
# Run evaluator method. Will store refitness_scoresults in Truss Object
for current_truss in tqdm(self.population, desc='Evaluating', position=1):
self.evaluator(current_truss)
# Assigns numerical score to each truss
for current_truss in tqdm(self.population, desc='Scoring', position=1):
self.fitness_function(current_truss)
# print(current_truss.fos)
if progress_display == 2:
self.progress_monitor(current_gen, progress_display, ax1)
self.update_population() # Determine which members to
if progress_display == 2:
plt.show() # sfr, keep plot from closing right after this completes, terminal will hang until this is closed
return self.population[0], self.pop_progress

# With parallelization
else:
# With parallelization
# Try 2: time =
for current_gen in tqdm(range(num_generations)):
#for current_gen in tqdm_notebook(range(num_generations),desc='Generation'):
if self.ga_params['pop_size'] < 1e4:
chunksize = self.ga_params['pop_size']/100
else:
chunksize = int(np.sqrt(self.ga_params['pop_size']))
for current_gen in tqdm(range(num_generations), desc='Overall', position=0):
# for current_gen in tqdm_notebook(range(num_generations),desc='Generation'):
self.ga_params['current_generation'] = current_gen
pool = Pool(num_threads)
self.population = pool.map(self.evaluator, self.population)
self.population = pool.map(
self.fitness_function, self.population)
self.population = list(tqdm(pool.imap(
self.evaluator, self.population, chunksize), total=self.ga_params['pop_size'], desc='Evaluating', position=1))
self.population = list(tqdm(pool.imap(
self.fitness_function, self.population, chunksize), total=self.ga_params['pop_size'], desc='Scoring', position=1))
pool.close()
pool.join()
if progress_display == 2:
Expand Down Expand Up @@ -309,6 +317,8 @@ def update_population(self): # Cristian
# Save most fit trusses as elites
pop_elite = population[:num_elite]

pbar = tqdm(total=(num_crossover+num_mutation+num_random),
desc='Updating', position=1)
# Portion of new population formed by crossover
pop_crossover = []
for i in range(0, num_crossover, 2):
Expand All @@ -318,6 +328,7 @@ def update_population(self): # Cristian
parent2 = population[parentindex2]
child1, child2 = crossover(parent1, parent2)
pop_crossover.extend((child1, child2))
pbar.update(2)

# Portion of new population formed by mutation
pop_mutation = []
Expand All @@ -326,9 +337,13 @@ def update_population(self): # Cristian
parent = population[parentindex]
child = mutator(parent)
pop_mutation.append(child)
pbar.update()

# Create new random trusses with remaining spots in generation
pop_random = [self.generate_random(2) for i in range(num_random)]
pop_random = []
for i in range(num_random):
pop_random.append(self.generate_random(2))
pbar.update()

# Append separate lists to form new generation
population = pop_elite + pop_crossover + pop_mutation + pop_random
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
--index-url https://pypi.python.org/simple/

numpy >= 1.13.0
matplotlib >= 2.0.0
matplotlib >= 3.0.0
configobj >= 5.0.6
tqdm >= 4.28.1
pytest >=3.0.5
Expand Down
11 changes: 6 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@
# There are some restrictions on what makes a valid project name
# specification here:
# https://packaging.python.org/specifications/core-metadata/#name
name='GASTOp', # Required
name='gastop', # Required

# Versions should comply with PEP 440:
# https://www.python.org/dev/peps/pep-0440/
#
# For a discussion on single-sourcing the version across setup.py and the
# project code, see
# https://packaging.python.org/en/latest/single_source_version.html
version='0.1.0', # Required
version='0.2.9', # Required

# This is a one-line description or tagline of what your project does. This
# corresponds to the "Summary" metadata field:
Expand Down Expand Up @@ -83,7 +83,7 @@

# This should be a valid email address corresponding to the author listed
# above.
author_email='wconlin@princeton.edu', # Optional
# author_email=

# Classifiers help users find your project by categorizing it.
#
Expand Down Expand Up @@ -129,7 +129,8 @@
#
# py_modules=["my_module"],
#
packages=find_packages(exclude=['docs', 'tests']), # Required
packages=find_packages(
exclude=['docs', 'tests', 'profiling', 'junk']), # Required

# This field lists other packages that your project depends on to run.
# Any package you put here will be installed by pip when your project is
Expand Down Expand Up @@ -177,7 +178,7 @@
# executes the function `main` from this package when invoked:
entry_points={ # Optional
'console_scripts': [
'gastop=gastop:main',
'gastop=gastop.__main__:main',
],
},

Expand Down

0 comments on commit 8217c75

Please sign in to comment.