import numpy as np
import pybrain.structure as pys
from pybrain.tools.shortcuts import buildNetwork
from pybrain.tools.functions import sigmoidPrime
from math import sin,cos,exp
import random as rnd
from multiprocessing import Pool
import functools
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
[docs]def initialize_nn():
"""Initializes a neural network using PyBrain with 6 hidden layers, 1 input layerm and 2 output layers.
"""
net = buildNetwork(1,6,2,hiddenclass=pys.SigmoidLayer)
return(net)
[docs]def get_weights_by_layer(net, layername):
"""Get the weights of the layer specified by layername of a PyBrain neural network net.
Parameters
----------
net : PyBrain FeedForwardNetwork
A feed forward neural network created using PyBrain
layername : string
A string specifying the name of the layer that one wishes to get the weights of.
Returns
-------
weights : numpy array of floats
A Numpy array of floats containing the weights of the specified layer.
"""
connections = net.connections[net[layername]]
weights = np.concatenate([connection.params for connection in connections])
return(weights)
[docs]def weights_to_binary(net):
"""Converts the weights of a PyBrain neural network to the binary encoding specified by float_to_bin.
Parameters
----------
net : PyBrain FeedForwardNetwork
Returns
-------
gene : string
A string that corresponds to the binary encoding of the neural network which corresponds to the binary encoding of the neural network weights.
"""
weights = net.params
binary_list = [float_to_bin(weight) for weight in weights]
return("".join(binary_list))
[docs]def get_random_gene(gene_length):
"""Returns a random gene which corresponds to a random binary sequence of length specified by gene_length.
Parameters
----------
gene_length : int
Length og the random gene to generate
Returns
-------
gene : string
A string representing a random gene of length gene_length
"""
format_string = "{:0"+str(gene_length)+"b}"
gene = format_string.format(rnd.randint(0,2**gene_length-1))
return(gene)
[docs]def system_to_gene(net,energy):
"""Converts a system specified by the given PyBrain neural network and energy to a binary encoding specified by float_to_bin.
Parameters
----------
net : PyBrain FeedForwardNetwork
A PyBrain neural network that corresponds to the wavefunction guess
energy : float
The guessed energy that corresponds to the given neural network
Returns
-------
string
A string which specifies the binary encoding of the given system.
"""
weights = net.params
binary_list = [float_to_bin(weight) for weight in weights]
binary_list.append(float_to_bin(energy))
return("".join(binary_list))
[docs]def gene_to_system(gene):
"""Converts a gene to a system which corresponds to a set of neural network weights and an energy.
Parameters
----------
gene : string
A string representing the binary encoding of the system
Returns
-------
weights,energy : (float list,float)
A tuple containing the weights and energy of the system.
"""
num_length = 10
array = np.fromstring(gene,dtype=np.dtype('S1'))
split_array = np.split(array,27)
iterable = ("".join(subgene) for subgene in split_array)
subgenes = np.fromiter(iterable,dtype=np.dtype('S10'))
weights = map(bin_to_float,subgenes[:-1])
energy = bin_to_float(subgenes[-1])
return((weights,energy))
[docs]def natural_selection(genes,net,num_survivors,points,mass):
"""Applies "natural selection" to a list of genes using the tournament methodology.
In this methodology genes are pitted against each other in pairs and the gene with the greatest fitness wins.
This continues until the specified amount of genes are obtained.
Parameters
----------
genes : list of strings
A list of strings each of which corresponds to a gene.
net : PyBrain FeedForwardNetwork
The neural network used to approximate the wavefunction
num_survivors : int
The number of survivors that are desired to be left.
points : array of floats
A set of points one wishes to evaluate the genes fitness on.
mass : float
The reduced mass used in the Schrodinger equation to evaluate fitness.
Returns
-------
survivors, survivor_fitnesses : (list of strings, list of floats)
A list of survivor genes and their associated fitnesses.
"""
survivors = np.zeros(num_survivors,dtype=np.dtype('S270'))
survivor_fitnesses = np.zeros(num_survivors)
fitness_func = functools.partial(gene_fitness,net=net,points=points,mass=mass)
with ThreadPoolExecutor(max_workers=5) as executor:
result = executor.map(fitness_func,genes)
fitnesses=list(result)
for i in range(num_survivors):
gene1,gene2 = np.random.choice(np.arange(len(genes)),replace=False,size=2)
#fitness1,fitness2 = gene_fitness(gene1,net,points,mass), gene_fitness(gene2,net,points,mass)
selected_gene = gene1 if fitnesses[gene1]>fitnesses[gene2] else gene2
survivors[i] = genes[selected_gene]
survivor_fitnesses[i]=fitnesses[selected_gene]
return((survivors,survivor_fitnesses))
[docs]def breed_genes(gene1,gene2):
"""Mates two genes using the uniform crossover algorithm.
Parameters
----------
gene1 : string
The string representing the first gene to mate
gene2 : string
The string representing the second gene to mate.
Returns
-------
newgene : string
The child of the two mated strings
"""
newgene=""
for i in range(len(gene1)):
if rnd.uniform(0,1)<0.5:
newgene+=gene1[i]
else:
newgene+=gene2[i]
return newgene
[docs]def nn_array(net,array):
"""Apply the neural network over an array.
Parameters
----------
net : PyBrain FeedForwardNetwork
Neural network to apply over an array.
array : array of floats
Array of floats to activate the neural network over
Returns
-------
array of floats
The output of the neural network for each element of the array
"""
vector = [np.array(element) for element in array]
return(map(net.activate,vector))
[docs]def nn_first_derivative(net,x):
"""The first derivative of a feedforward neural network with one hidden sigmoid layer.
Parameters
----------
net : PyBrain FeedForwardNetwork
The neural network one wishes to get the first derivative of.
x : float
The location x where one wants the derivative
Returns
-------
output1_derivative, output2_derivative : (float,float)
The first derivative of the first and second output of the neural network.
"""
inweights = get_weights_by_layer(net,'in')
hiddenweights = get_weights_by_layer(net,'hidden0')
iterable1 = (hiddenweight*sigmoidPrime(x)*inweight**2 for (inweight,hiddenweight) in zip(inweights,hiddenweights[0:6]))
iterable2 = (hiddenweight*sigmoidPrime(x)*inweight**2 for (inweight,hiddenweight) in zip(inweights,hiddenweights[6:12]))
output1_deriv = np.sum(np.fromiter(iterable1,dtype=float))
output2_deriv = np.sum(np.fromiter(iterable2,dtype=float))
return(output1_deriv,output2_deriv)
[docs]def nn_second_derivative(net,x):
"""The second derivative of a feedforward neural network with one hidden sigmoid layer.
Parameters
----------
net : PyBrain FeedForwardNetwork
The neural network one wishes to get the first derivative of.
x : float
The location x where one wants the derivative
Returns
-------
output1_derivative, output2_derivative : (float,float)
The second derivative of the first and second output of the neural network.
"""
inweights = get_weights_by_layer(net,'in')
hiddenweights = get_weights_by_layer(net,'hidden0')
iterable1 = (hiddenweight*sigmoidPrime(sigmoidPrime(x))*inweight**2 for (inweight,hiddenweight) in zip(inweights,hiddenweights[0:6]))
iterable2 = (hiddenweight*sigmoidPrime(sigmoidPrime(x))*inweight**2 for (inweight,hiddenweight) in zip(inweights,hiddenweights[6:12]))
output1_deriv = np.sum(np.fromiter(iterable1,dtype=float))
output2_deriv = np.sum(np.fromiter(iterable2,dtype=float))
return(output1_deriv,output2_deriv)
[docs]def eigenfunction(net,x):
"""Converts the two outputs of the neural network to the value of the wavefunction at x.
Parameters
----------
net : PyBrain FeedForwardNetwork
The neural network that represents the wavefunction.
x : float
The location at which one wants to evaluate the wavefunction.
Returns
-------
wavefunction : float
The value of the wavefunction at x
"""
(A,S)=net.activate(np.array([x]))
return(A*sin(S))
[docs]def eigenfunction_second_deriv(net,x):
"""Calculates the second derivative of the wavefunction represented by net.
Parameters
----------
net : PyBrain FeedForwardNetwork
The neural network representing the wavefunction.
x : float
The location at which one wishes to evaluate the wavefunction.
"""
(Aprime,Sprime) = nn_first_derivative(net,x)
(Aprime2,Sprime2) = nn_second_derivative(net,x)
(A,S) = net.activate(np.array([x]))
second_deriv = (Aprime2*sin(S)+Aprime*Sprime*cos(S)+
(Aprime*Sprime+A*Sprime2)*cos(S)-
A*Sprime*Sprime*sin(S))
return(second_deriv)
[docs]def hamiltonian(net,x,mass):
"""Calculates the hamiltonian of a harmonic oscillator of reduced mass "mass" applied to the wavefunction represented by net.
Parameters
----------
net : PyBrain FeedForwardNetwork
The neural network that represents the wavefunction one wishes to apply the hamiltonian to.
x : float
The location at which one wishes to get the value of the hamiltonian
mass : float
The reduced mass of the harmonic oscillator
Returns
-------
float
The value of the hamiltonian applied to the wavefunction
"""
hamiltonian = -(1./(2.*mass))*eigenfunction_second_deriv(net,x)+0.5*(x**2)*eigenfunction(net,x)
return(hamiltonian)
[docs]def get_random_points(xmin,xmax,M):
"""Gets n random points between xmin and xmax used for the random point evaluation method.
Parameters
----------
xmin : float
Minimum value that the points can take.
xmax : float
Maximum value that the points can take.
M : int
The number of points to generate.
Returns
-------
array of floats
An array of M random points.
"""
return(np.random.uniform(xmin,xmax,size=M))
[docs]def gene_fitness(gene,net,points,mass):
"""Calculates the fitness of the gene based on the difference between the energy and
the hamiltonian applied to the wavefunction squared evaluated at the points specified by points.
Parameters
----------
gene : string
The gene with which one wishes to get the fitness of.
net : PyBrain FeedForwardNetwork
The neural network used to represent the wavefunction.
points : array of floats
An array of random points with which one wishes to evaluate the hamiltonian.
mass : float
The reduced mass of the harmonic oscillator system.
Returns
-------
float
The fitness of the gene.
"""
weights,energy = gene_to_system(gene)
net._setParameters(weights)
energy_SSR_func = lambda x : (hamiltonian(net,x,mass)-energy*eigenfunction(net,x))**2
normalization_func = lambda x : eigenfunction(net,x)**2
numerator = np.sum(np.fromiter((energy_SSR_func(x) for x in points),dtype=float))
denominator = np.sum(np.fromiter((normalization_func(x) for x in points),dtype=float))
R = numerator/denominator
return np.exp(-R)
[docs]def bin_to_float(binary):
"""Converts a binary number encoded with fixed point format. Here the first digit is reserved for sign and the rest are binary places starting with 2^1 and decreasing.
Here we use ten bits to encode a number.
Parameters
----------
binary : string
A string of ten digits representing the number in binary fixed point format.
Returns
-------
number : float
A float that corresponds to the fixed point binary format.
"""
number = np.sum(np.fromiter((float(digit)*2.**(place) for digit,place in zip(binary[1:],range(1,-9,-1))),dtype=float))
number = number if int(binary[0])==0 else -number
return number
[docs]def float_to_bin(number):
"""Converts a floating point number to a ten bit fixed point format as specified in bin_to_float.
Parameters
----------
number : float
The floating point number to convert.
Returns
-------
binary : string
A string representing the binary encoding of the floating point number in ten bit fixed point format.
"""
if number==0:
return "0"*10
wrapped_value = abs(number) % 8
binary = "0" if number >=0 else "1"
for i in range(1,-9,-1):
if wrapped_value>=(2.**i):
wrapped_value-=(2.**i)
binary+="1"
else:
binary+="0"
return binary