from numpy import *
import numpy.linalg as la
import binascii, struct
from pyec.config import Config, ConfigBuilder
from pyec.util.registry import BENCHMARKS
from pyec.util.TernaryString import TernaryString
[docs]class Distribution(object):
"""A Distribution that can be sampled"""
def __init__(self, config):
super(Distribution, self).__init__()
self.config = config
def __call__(self, **kwargs):
"""Get a single sample from the distribution"""
return self.batch(1)[0]
[docs] def batch(self, sampleSize):
"""Get a sample from the distribution"""
pass
[docs]class ProposalDistribution(Distribution):
"""A proposal distribution for e.g. simulated annealing"""
[docs] def adjust(self, rate):
"""given the current acceptance rate, alter the distribution as needed"""
pass
[docs] def densityRatio(self, x1, x2, i=None):
"""given two points, give the ratio of the densities p(x1) / p(x2)"""
pass
[docs]class PopulationDistribution(Distribution):
"""A distribution governing a population algorithm"""
[docs] def update(self, n, population):
"""
Update the distribution with the latest population
population is a list of (point, score) tuples
If config.sorted is True, the list will be sorted by descending score.
"""
pass
[docs] def run(self, segment='test', fitness=None, extraArgs=[], **kwargs):
from pyec.trainer import Trainer
if fitness is None:
fitness = BENCHMARKS.load(self.config.function, *extraArgs)
self.config.segment = segment
if hasattr(fitness, 'center'):
self.config.center = fitness.center
self.config.scale = fitness.scale
if hasattr(fitness, 'numInputs'):
self.config.numInputs = fitness.numInputs
self.config.numOutputs = fitness.numOutputs
self.config.numHidden = fitness.numHidden
self.config.netPattern = fitness.netPattern
self.config.fitness = fitness
try:
fitness.algorithm = self
fitness.config = self.config
except:
pass
if hasattr(fitness, 'initial'):
self.config.initialDistribution = fitness.initial
self.initial = self.config.initialDistribution
trainer = Trainer(fitness, self, **kwargs)
trainer.train()
self.trainer = trainer
return fitness
[docs] def convert(self, x):
"""
Convert a point to a scorable representation
x - the candidate solution
"""
return x
@classmethod
@classmethod
[docs] def configurator(cls):
"""
Return a ConfigurationBuilder
"""
return ConfigBuilder()
[docs]class Gaussian(ProposalDistribution, PopulationDistribution):
""" A Gaussian Proposal Distribution """
def __init__(self, config):
super(Gaussian, self).__init__(config)
self.var = 1.
if hasattr(config, 'spaceScale') and config.spaceScale is not None:
self.var = config.spaceScale / 2.
elif hasattr(config, 'varInit') and config.varInit is not None:
self.var = config.varInit
self.varIncr = 1.05
self.usePrior = hasattr(config, 'usePrior') and config.usePrior or False
def __call__(self, **kwargs):
center = zeros(self.config.dim)
if self.usePrior and kwargs.has_key('prior'):
center = kwargs['prior']
# vary the mixture point
var = self.variance()
if kwargs.has_key('idx') and hasattr(var, '__len__'):
var = var[kwargs['idx']]
varied = random.randn(self.config.dim) * var + center
# check bounds; call again if outside bounds
if self.config.bounded:
try:
if not self.config.in_bounds(varied):
return self.__call__(**kwargs)
except RuntimeError, msg:
print "Recursion error: ", varied
print abs(varied - self.config.center)
print self.config.scale
return varied
def batch(self, popSize):
return [self.__call__(idx=i) for i in xrange(popSize)]
def density(self, x, center):
var = self.variance()
if isinstance(var, ndarray):
var = var[0]
#print x
diff = center - x
if sqrt((diff * diff).sum()) > 5. * var:
return 0.0
covar = var * var * identity(len(x))
diff = center - x
pow = -.5 * dot(diff, dot(la.inv(covar), diff))
d = ((((2*pi)**(len(x))) * la.det(covar)) ** -.5) * exp(pow)
return d
def variance(self):
return self.var
def adjust(self, rate):
if not hasattr(rate, '__len__'):
if rate < .23:
self.var /= self.varIncr
else:
if self.var < self.config.scale / 5.:
self.var *= self.varIncr
return
self.var = self.var * ones(len(rate))
for i in xrange(len(rate)):
if rate[i] < .23:
self.var[i] /= self.varIncr
else:
if self.var[i] < self.config.scale / 5.:
self.var[i] *= self.varIncr
#print rate, self.var
def densityRatio(self, x1, x2, i = None):
if self.usePrior:
return 1.
else:
if i is None:
var = self.var
else:
var = self.var[i]
return exp((1./(2*(var**2))) * ((x2 ** 2).sum() - (x1 ** 2).sum()))
[docs]class Bernoulli(Distribution):
def __init__(self, config):
super(Bernoulli, self).__init__(config)
self.dim = config.dim
self.bitFlipProb = .5
def __call__(self, **kwargs):
return random.binomial(1, self.bitFlipProb, self.dim)
def batch(self, popSize):
return [self.__call__() for i in xrange(popSize)]
[docs]class BernoulliTernary(Distribution):
def __init__(self, config):
super(BernoulliTernary, self).__init__(config)
self.dim = config.dim
def __call__(self, **kwargs):
numBytes = int(ceil(self.dim / 8.0))
numFull = self.dim / 8
initial = ''
if numBytes != numFull:
extra = self.dim % 8
initMask = 0
for i in xrange(extra):
initMask <<= 1
initMask |= 1
initial = struct.pack('B',initMask)
base = long(binascii.hexlify(random.bytes(numBytes)), 16)
known = long(binascii.hexlify(initial + '\xff'*numFull), 16)
return TernaryString(base, known)
def batch(self, popSize):
return [self.__call__() for i in xrange(popSize)]
[docs]class FixedCube(ProposalDistribution):
def __init__(self, config):
super(FixedCube, self).__init__(config)
self.scale = self.config.scale
self.center = self.config.center
if hasattr(config, 'in_bounds'):
if hasattr(config.in_bounds, 'extent'):
self.center, self.scale = config.in_bounds.extent()
def __call__(self, **kwargs):
point = (random.random_sample(self.config.dim) - .5) * 2 * self.scale + self.center
while self.config.bounded and not self.config.in_bounds(point):
point = (random.random_sample(self.config.dim) - .5) * 2 * self.scale + self.center
return point
def batch(self, popSize):
return [self.__call__() for i in xrange(popSize)]
def densityRatio(self, x1, x2):
return 1.
def adjust(self, rate):
pass