Skip to content

Commit

Permalink
Changes and Init Bool
Browse files Browse the repository at this point in the history
Creates changes in _base.py and creates an initial boolean subclass
  • Loading branch information
AdarshPrusty7 committed Mar 6, 2023
1 parent a3edd63 commit 73c603c
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 13 deletions.
24 changes: 17 additions & 7 deletions sklearn/genetic_programming/_base.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

import random

def __memoize(f):
def memoize(f):
'Add a cache memory to the input function.'
f.cache = {}
def decorated_function(*args):
Expand All @@ -23,19 +23,19 @@ def __create_boolean_expression(self, depth, vars):
if random.random() < 1.0 / 3:
return 'not' + ' ' + self.__create_boolean_expression(depth - 1, vars)
else:
return '(' + self.__create_boolean_expression(depth - 1, vars) + ' ' + random.choice(['and', 'or']) + ' ' + self.__create_boolean_expression(depth - 1) + ')'
return '(' + self.__create_boolean_expression(depth - 1, vars) + ' ' + random.choice(['and', 'or']) + ' ' + self.__create_boolean_expression(depth - 1, vars) + ')'

def __create_boolean_function(self, depth, vars):
def create_boolean_function(self, depth, vars):
'Create a random Boolean function.'
expression = self.__create_boolean_expression(depth, vars)
boolean_function = eval('lambda ' + ', '.join(vars) + ': ' + expression) # create function of n input variables
boolean_function = __memoize(boolean_function) # add cache to the function
boolean_function = memoize(boolean_function) # add cache to the function
boolean_function.genotype = lambda: expression # store genotype within function
return boolean_function

def create_boolean_population(self, depth, vars, population_size):
'Create population of Boolean functions.d'
self.population = [self.__create_boolean_function(depth, vars) for _ in range(population_size)]
self.population = [self.create_boolean_function(depth, vars) for _ in range(population_size)]

def get_boolean_population(self):
'Return population of Boolean functions.'
Expand All @@ -47,8 +47,12 @@ def __init__(self):

def __create_arithmetic_expression(self, depth, vars):
'Create a random arithmetic expression using recursion.'
if depth == 1 or random.random() < 1.0 / (2 ** depth - 1):
return random.choice(vars)
pass



def __create_arithmetic_function(self, depth, vars):
'Create a random arithmetic function.'
pass
Expand All @@ -68,7 +72,9 @@ def __init__(self):

def __create_program_expression(self, depth, vars):
'Create a random program expression using recursion.'
pass
if depth == 1 or random.random() < 1.0 / (2 ** depth - 1):
return random.choice(vars)


def __create_program_function(self, depth, vars):
'Create a random program function.'
Expand All @@ -82,4 +88,8 @@ def get_program_population(self):
'Return population of program functions.'
pass



boolean = BooleanPopulation()
boolean.create_boolean_population(4, ['x1', 'x2', 'x3', 'x4', 'x5'], 5)
print(boolean.get_boolean_population()[0].genotype())
print([x.genotype() for x in boolean.get_boolean_population()])
54 changes: 54 additions & 0 deletions sklearn/genetic_programming/_gp1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@


import random

# Boolean Non-Semantic Genetic Operators
class GeneticProgram:
def __init__(self, numvars, depth, pop_size, generations, trunc, target_output) -> None:
self.numvars = numvars
self.depth = depth
self.pop_size = pop_size
self.generations = generations
self.trunc = trunc
self.target_output = target_output
self.vars = []
pass

def create_vars(self):
self.vars = ['x'+str(i) for i in range(self.numvars)]

def crossover(self, f1, f2):
'Crossover operator.'
return f1, f2

def mutation(self, f):
'Mutation operator.'
return f

def fitness(self, f):
'Fitness function.'
return f

def selection(self, f):
'Selection operator.'
return f

def create_boolean_expression(self, depth):
'Create a random Boolean expression using recursion.'
if depth == 1 or random.random() < 1.0 / (2 ** depth - 1):
return random.choice(self.vars)
if random.random() < 1.0 / 3:
return 'not' + ' ' + self.create_boolean_expression(depth - 1)
pass

def create_boolean_function(self):
'Create a random Boolean function.'
pass

def create_boolean_population(self):
'Create initial population of Boolean functions.'
pass

def run(self):
'Run the genetic program.'
# Create initial population
66 changes: 60 additions & 6 deletions sklearn/genetic_programming/_gsgp.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,73 @@
# Author: Adarsh Prusty

import _base
import itertools
import random

def targetfunct(*args):
'Parity function of any number of input variables'
return args.count(True) % 2 == 1

class GeometricSemanticGeneticProgram:
def __init__(self) -> None:
pass
def __init__(self, GENERATIONS, POPSIZE) -> None:
self.GENERATIONS = GENERATIONS
self.POPSIZE = POPSIZE
self.TRUNC = 0.5
self.depth = 4
self.vars = ['x1', 'x2', 'x3', 'x4', 'x5']
self.boo = _base.BooleanPopulation()

def boolean_fitness(self, p):
pass
# Uses an error value to determine the fitness of the individual
fit = 0
somelists = [[True, False] for i in range(5)]
# generate all possible combinations of inputs from somelists
for element in itertools.product(*somelists):
if p(*element) != targetfunct(*element):
fit = fit + 1
return fit

def boolean_crossover(self, p1, p2):
pass
mask = self.boo.create_boolean_function(self.depth, self.vars)
offspring = lambda *x: (p1(*x) and mask(*x)) or (p2(*x) and not mask(*x))
offspring = self.boo.memoize(offspring) # add cache
offspring.geno = lambda: '(('+ p1.geno() + ' and ' + mask.geno() + ') or (' + p2.geno() + ' and not ' + mask.geno() + '))'
return offspring

def boolean_mutation(self, p):
pass
mintermexpr = ' and '.join([random.choice([x,'not ' + x]) for x in self.vars]) # random minterm expression of n variables
minterm = eval('lambda ' + ', '.join(vars) + ': ' + mintermexpr) # turn minterm into a function
if random.random()<0.5:
offspring = lambda *x: p(*x) or minterm(*x)
offspring = self.boo.memoize(offspring) # add cache
offspring.geno = lambda: '(' + p.geno() + ' or ' + mintermexpr + ')' # to reconstruct genotype
else:
offspring = lambda *x: p(*x) and not minterm(*x)
offspring = self.boo.memoize(offspring) # add cache
offspring.geno = lambda: '(' + p.geno() + ' and not ' + mintermexpr + ')' # to reconstruct genotype
return offspring

def boolean_selection(self, p):
pass


def main(self):
'Main function.'
pop = [self.boo.create_boolean_function(self.depth, self.vars) for _ in range(self.POPSIZE) ] # initialise population
for gen in range(self.GENERATIONS+1):
graded_pop = [ (self.boolean_fitness(ind), ind) for ind in pop ] # evaluate population fitness
sorted_pop = [ ind[1] for ind in sorted(graded_pop)] # sort population on fitness
print ('gen: ', gen , ' min fit: ', self.boolean_fitness(sorted_pop[0]), ' avg fit: ', sum(ind[0] for ind in graded_pop)/(self.POPSIZE*1.0)) # print stats
parent_pop = sorted_pop[:int(self.TRUNC*self.POPSIZE)] # selected parents
if gen == self.GENERATIONS:
break
for i in range(self.POPSIZE): # create offspring population
par = random.sample(parent_pop, 2) # pick two random parents
pop[i] = self.boolean_mutation(self.boolean_crossover(par[0],par[1])) # create offspring

print ('Best individual in last population: ')
#print (sorted_pop[0]).geno() # reconstruct genotype of final solution (WARNING: EXPONENTIALLY LONG IN NUMBER OF GENERATIONS!)
print ('Query best individual in last population with all True inputs:')
print (sorted_pop[0](*([True] * 5))) # however querying it to make predictions is quick

gsgp = GeometricSemanticGeneticProgram(10, 10)
gsgp.main()

0 comments on commit 73c603c

Please sign in to comment.