Skip to content

Commit e295eb3

Browse files
author
Tomasz bla Fortuna
committed
Pygene migrated to Python 3.
All examples executed (except for GUI salesman)
1 parent 07e9a5a commit e295eb3

23 files changed

+2361
-127
lines changed

examples3/demo_case.py

+165
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
#! /usr/bin/env python3
2+
"""
3+
demo that tries to show one reason why genetic algorithms are successful.
4+
5+
It tries to crack open a suitcase with a few locks, each with 1-5
6+
digits. Fitness function can only determine that the lock is open, not
7+
the progress of opening the lock (how much the lock is opened)
8+
9+
Genetic algorithm can keep partial results (e.g. 1 lock open) while
10+
trying other locks.
11+
12+
In general each lock represents a partial solution to the problem
13+
described by organism.
14+
"""
15+
16+
import random
17+
from time import time
18+
import sys
19+
20+
from pygene3.gene import IntGene, IntGeneRandom, IntGeneExchange
21+
from pygene3.organism import Organism, GenomeSplitOrganism
22+
from pygene3.population import Population
23+
24+
# Parameters
25+
locks = 8
26+
digits_in_lock = 3
27+
28+
# Generate codes
29+
codes = []
30+
for lock in range(locks):
31+
code = [random.randint(0, 9) for i in range(digits_in_lock)]
32+
codes.append(code)
33+
34+
35+
class DigitCodeGene(IntGeneRandom):
36+
"""
37+
a gene which holds a single digit, and can mutate into another digit.
38+
Mutation randomizes gene for IntGeneRandom class.
39+
"""
40+
mutProb = 0.3
41+
# mutAmt = 2
42+
randMin = 0
43+
randMax = 9
44+
45+
def __repr__(self):
46+
return str(self.value)
47+
48+
# generate a genome, one gene for each digit in suitcase
49+
genome = {}
50+
for l in range(locks):
51+
for d in range(digits_in_lock):
52+
key = '%d_%d' % (l, d)
53+
genome[key] = DigitCodeGene
54+
55+
# an organism that evolves towards the required string
56+
57+
class CodeHacker(GenomeSplitOrganism):
58+
59+
chromosome_intersections = 2
60+
genome = genome
61+
62+
def get_code(self, lock):
63+
"Decode the chromosome (genome) into code for specific lock"
64+
code = []
65+
for d in range(digits_in_lock):
66+
key = '%d_%d' % (lock, d)
67+
code.append(self[key])
68+
return code
69+
70+
def fitness(self):
71+
"calculate fitness - number of locks opened by genome."
72+
opened_locks = 0
73+
for l in range(locks):
74+
code = self.get_code(l)
75+
if code == codes[l]:
76+
opened_locks += 1
77+
78+
# The lower the better
79+
# add 0 - 0.5 to force randomization of organisms selection
80+
fitness = float(locks - opened_locks) #+ random.uniform(0, 0.5)
81+
return fitness
82+
83+
def __repr__(self):
84+
"Display result nicely"
85+
s='<CodeHacker '
86+
for l in range(locks):
87+
code = self.get_code(l)
88+
code_str = "".join(str(i) for i in code)
89+
if code == codes[l]:
90+
s += " %s " % code_str # space - opened lock
91+
else:
92+
s += "(%s)" % code_str # () - closed lock
93+
s = s.strip() + ">"
94+
return s
95+
96+
97+
class CodeHackerPopulation(Population):
98+
"Configuration of population"
99+
species = CodeHacker
100+
101+
initPopulation = 500
102+
103+
# Tips: Leave a space for mutants to live.
104+
105+
# cull to this many children after each generation
106+
childCull = 600
107+
108+
# number of children to create after each generation
109+
childCount = 500
110+
111+
# Add this many mutated organisms.
112+
mutants = 1.0
113+
114+
# Mutate organisms after mating (better results with False)
115+
mutateAfterMating = False
116+
117+
numNewOrganisms = 0
118+
119+
# Add X best parents into new population
120+
# Good configuration should in general work without an incest.
121+
# Incest can cover up too much mutation
122+
incest = 2
123+
124+
125+
def main():
126+
127+
# Display codes
128+
print("CODES TO BREAK:", end=' ')
129+
for code in codes:
130+
print("".join(str(digit) for digit in code), end=' ')
131+
print()
132+
133+
# Display some statistics
134+
combinations = 10**(locks * digits_in_lock)
135+
operations = 10000 * 10**6
136+
print("Theoretical number of combinations", combinations)
137+
print("Optimistic operations per second:", operations)
138+
print("Direct bruteforce time:", 1.0* combinations / operations / 60.0/60/24, "days")
139+
140+
# Hack the case.
141+
started = time()
142+
143+
# Create population
144+
ph = CodeHackerPopulation()
145+
146+
i = 0
147+
while True:
148+
b = ph.best()
149+
print("generation %02d: %s best=%s average=%s)" % (
150+
i, repr(b), b.get_fitness(), ph.fitness()))
151+
152+
if b.get_fitness() < 1:
153+
#for org in ph:
154+
# print " ", org
155+
156+
print("cracked in ", i, "generations and ", time() - started, "seconds")
157+
break
158+
159+
sys.stdout.flush()
160+
i += 1
161+
ph.gen()
162+
163+
164+
if __name__ == '__main__':
165+
main()

examples3/demo_config_genome.py

+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
#! /usr/bin/env python3
2+
3+
"""
4+
Example build on the demo_quadratic.py - understand that one first!
5+
6+
Program reads it's genome configuration from a config file (quadratic.ini)
7+
8+
Example config file:
9+
[x1]
10+
type = float
11+
randMin = -100.0
12+
randMax = 100.0
13+
mutProb = 0.1
14+
mutAmt = 0.1
15+
16+
[x2]
17+
type = int
18+
randMin = -50
19+
randMax = 50
20+
mutProb = 0.2
21+
mutAmt = 1
22+
23+
[x3]
24+
alias = x2
25+
26+
[x4]
27+
type = float
28+
value = 5.4
29+
30+
One section per gene.
31+
'type' is necessary - other fields depends on the selected type
32+
possible types (for current list see pygene/config.py):
33+
int, int_exchange, float, float_exchange, float_random, float_max, complex
34+
You can create a genes from previously specified ones using 'alias' field.
35+
36+
There might be available a special section 'population' with parameters
37+
for population. It's never treated as a gene.
38+
"""
39+
40+
from pygene3.gene import FloatGene, FloatGeneMax
41+
from pygene3.organism import Organism, MendelOrganism
42+
from pygene3.population import Population
43+
from pygene3.config import ConfigLoader
44+
45+
# parameters for quadratic equation
46+
# has roots 3 and 5
47+
if 1:
48+
a = 2
49+
b = -16
50+
c = 30
51+
else:
52+
# this alternate set has only 1 root, x=4
53+
a = 2.0
54+
b = 3.0
55+
c = -44.0
56+
57+
def quad(x):
58+
return a * x ** 2 + b * x + c
59+
60+
loader = ConfigLoader(filename="quadratic.ini", require_genes=['x1', 'x2'])
61+
62+
class QuadraticSolver(Organism):
63+
"""
64+
Implements the organism which tries
65+
to solve a quadratic equation
66+
"""
67+
genome = loader.load_genome()
68+
69+
def fitness(self):
70+
"""
71+
Implements the 'fitness function' for this species.
72+
Organisms try to evolve to minimise this function's value
73+
"""
74+
x1 = self['x1']
75+
x2 = self['x2']
76+
77+
# this formula punishes for roots being wrong, also for
78+
# roots being the same
79+
badness_x1 = abs(quad(x1)) # punish for incorrect first root
80+
badness_x2 = abs(quad(x2)) # punish for incorrect second root
81+
badness_equalroots = 1.0 / (abs(x1 - x2)) # punish for equal roots
82+
return badness_x1 + badness_x2 + badness_equalroots
83+
84+
def __repr__(self):
85+
return "<fitness=%f x1=%s x2=%s>" % (
86+
self.fitness(), self['x1'], self['x2'])
87+
88+
89+
QPopulation = loader.load_population("QPopulation", species=QuadraticSolver)
90+
91+
"""
92+
class QPopulation(Population):
93+
94+
species = QuadraticSolver
95+
initPopulation = 20
96+
97+
# cull to this many children after each generation
98+
childCull = 20
99+
100+
# number of children to create after each generation
101+
childCount = 50
102+
103+
mutants = 0.5
104+
105+
# create a new population, with randomly created members
106+
"""
107+
108+
pop = QPopulation()
109+
110+
111+
# now a func to run the population
112+
def main():
113+
from time import time
114+
s = time()
115+
try:
116+
generations = 0
117+
while True:
118+
# execute a generation
119+
pop.gen()
120+
generations += 1
121+
122+
# and dump it out
123+
#print [("%.2f %.2f" % (o['x1'], o['x2'])) for o in pop.organisms]
124+
best = pop.organisms[0]
125+
print("fitness=%f avg=%f x1=%f x2=%f" % (best.get_fitness(), pop.fitness(),
126+
best['x1'], best['x2']))
127+
if best.get_fitness() < 0.6:
128+
break
129+
130+
except KeyboardInterrupt:
131+
pass
132+
print("Executed", generations, "generations in", time() - s, "seconds")
133+
134+
135+
if __name__ == '__main__':
136+
main()
137+
138+

examples3/demo_converge.py

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#! /usr/bin/env python3
2+
3+
"""
4+
Very simple demo in which organisms try to minimise
5+
the output value of a function
6+
"""
7+
8+
from pygene3.gene import FloatGene, FloatGeneMax
9+
from pygene3.organism import Organism, MendelOrganism
10+
from pygene3.population import Population
11+
12+
class CvGene(FloatGeneMax):
13+
"""
14+
Gene which represents the numbers used in our organism
15+
"""
16+
# genes get randomly generated within this range
17+
randMin = -100.0
18+
randMax = 100.0
19+
20+
# probability of mutation
21+
mutProb = 0.1
22+
23+
# degree of mutation
24+
mutAmt = 0.1
25+
26+
27+
class Converger(MendelOrganism):
28+
"""
29+
Implements the organism which tries
30+
to converge a function
31+
"""
32+
genome = {'x':CvGene, 'y':CvGene}
33+
34+
def fitness(self):
35+
"""
36+
Implements the 'fitness function' for this species.
37+
Organisms try to evolve to minimise this function's value
38+
"""
39+
return self['x'] ** 2 + self['y'] ** 2
40+
41+
def __repr__(self):
42+
return "<Converger fitness=%f x=%s y=%s>" % (
43+
self.fitness(), self['x'], self['y'])
44+
45+
46+
# create an empty population
47+
48+
pop = Population(species=Converger, init=2, childCount=50, childCull=20)
49+
50+
51+
# now a func to run the population
52+
53+
def main():
54+
try:
55+
while True:
56+
# execute a generation
57+
pop.gen()
58+
59+
# get the fittest member
60+
best = pop.best()
61+
62+
# and dump it out
63+
print(best)
64+
65+
except KeyboardInterrupt:
66+
pass
67+
68+
69+
if __name__ == '__main__':
70+
main()
71+

0 commit comments

Comments
 (0)