"""Python micro framework for building nature-inspired algorithms."""
from __future__ import print_function # for backward compatibility purpose
import os
import logging
import json
import datetime
import xlsxwriter
import numpy as np
from NiaPy import algorithms, benchmarks
__all__ = ['algorithms', 'benchmarks']
__project__ = 'NiaPy'
__version__ = '1.0.0'
VERSION = "{0} v{1}".format(__project__, __version__)
logging.basicConfig()
logger = logging.getLogger('NiaPy')
logger.setLevel('INFO')
[docs]class Runner(object):
r"""Runner utility feature.
Feature which enables running multiple algorithms with multiple benchmarks.
It also support exporting results in various formats (e.g. LaTeX, Excel, JSON)
"""
def __init__(self, D, NP, nFES, nRuns, useAlgorithms, useBenchmarks, A=0.5, r=0.5,
Qmin=0.0, Qmax=2.0, Pa=0.25, F=0.5, CR=0.9, alpha=0.5, betamin=0.2, gamma=1.0,
p=0.5, Ts=4, Mr=0.05, C1=2.0, C2=2.0, w=0.7, vMin=-4, vMax=4, Tao=0.1):
r"""Initialize Runner.
**__init__(self, D, NP, nFES, nRuns, useAlgorithms, useBenchmarks, ...)**
Arguments:
D {integer} -- dimension of problem
NP {integer} -- population size
nFES {integer} -- number of function evaluations
nRuns {integer} -- number of repetitions
useAlgorithms [] -- array of algorithms to run
useBenchmarks [] -- array of benchmarks to run
A {decimal} -- laudness
r {decimal} -- pulse rate
Qmin {decimal} -- minimum frequency
Qmax {decimal} -- maximum frequency
Pa {decimal} -- probability
F {decimal} -- scalling factor
CR {decimal} -- crossover rate
alpha {decimal} -- alpha parameter
betamin {decimal} -- betamin parameter
gamma {decimal} -- gamma parameter
p {decimal} -- probability switch
Ts {decimal} -- tournament selection
Mr {decimal} -- mutation rate
C1 {decimal} -- cognitive component
C2 {decimal} -- social component
w {decimal} -- inertia weight
vMin {decimal} -- minimal velocity
vMax {decimal} -- maximal velocity
Tao {decimal}
"""
self.D = D
self.NP = NP
self.nFES = nFES
self.nRuns = nRuns
self.useAlgorithms = useAlgorithms
self.useBenchmarks = useBenchmarks
self.A = A
self.r = r
self.Qmin = Qmin
self.Qmax = Qmax
self.Pa = Pa
self.F = F
self.CR = CR
self.alpha = alpha
self.betamin = betamin
self.gamma = gamma
self.p = p
self.Ts = Ts
self.Mr = Mr
self.C1 = C1
self.C2 = C2
self.w = w
self.vMin = vMin
self.vMax = vMax
self.Tao = Tao
self.results = {}
def __algorithmFactory(self, name, benchmark):
bench = benchmarks.utility.Utility().get_benchmark(benchmark)
algorithm = None
if name == 'BatAlgorithm':
algorithm = algorithms.basic.BatAlgorithm(
self.D, self.NP, self.nFES, self.A, self.r, self.Qmin, self.Qmax, bench)
elif name == 'DifferentialEvolutionAlgorithm':
algorithm = algorithms.basic.DifferentialEvolutionAlgorithm(
self.D, self.NP, self.nFES, self.F, self.CR, bench)
elif name == 'FireflyAlgorithm':
algorithm = algorithms.basic.FireflyAlgorithm(
self.D, self.NP, self.nFES, self.alpha, self.betamin, self.gamma, bench)
elif name == 'FlowerPollinationAlgorithm':
algorithm = algorithms.basic.FlowerPollinationAlgorithm(
self.D, self.NP, self.nFES, self.p, bench)
elif name == 'GreyWolfOptimizer':
algorithm = algorithms.basic.GreyWolfOptimizer(
self.D, self.NP, self.nFES, bench)
elif name == 'ArtificialBeeColonyAlgorithm':
algorithm = algorithms.basic.ArtificialBeeColonyAlgorithm(
self.D, self.NP, self.nFES, bench)
elif name == 'GeneticAlgorithm':
algorithm = algorithms.basic.GeneticAlgorithm(
self.D, self.NP, self.nFES, self.Ts, self.Mr, self.gamma, bench)
elif name == 'ParticleSwarmAlgorithm':
algorithm = algorithms.basic.ParticleSwarmAlgorithm(
self.D, self.NP, self.nFES, self.C1, self.C2, self.w, self.vMin, self.vMax, bench)
elif name == 'HybridBatAlgorithm':
algorithm = algorithms.modified.HybridBatAlgorithm(
self.D, self.NP, self.nFES, self.A, self.r, self.F, self.CR, self.Qmin, self.Qmax, bench)
elif name == 'SelfAdaptiveDifferentialEvolutionAlgorithm':
algorithm = algorithms.modified.SelfAdaptiveDifferentialEvolutionAlgorithm(
self.D, self.NP, self.nFES, self.F, self.CR, self.Tao, bench)
else:
raise TypeError('Passed benchmark is not defined!')
return algorithm
@classmethod
def __createExportDir(cls):
if not os.path.exists('export'):
os.makedirs('export')
@classmethod
def __generateExportName(cls, extension):
return 'export/' + str(datetime.datetime.now()).replace(':', '.') + '.' + extension
def __exportToLog(self):
print(self.results)
def __exportToJson(self):
self.__createExportDir()
with open(self.__generateExportName('json'), 'w') as outFile:
json.dump(self.results, outFile)
logger.info('Export to JSON completed!')
def __exportToXls(self):
self.__createExportDir()
workbook = xlsxwriter.Workbook(self.__generateExportName('xlsx'))
worksheet = workbook.add_worksheet()
row = 0
col = 0
nRuns = 0
for alg in self.results:
worksheet.write(row, col, alg)
col += 1
for bench in self.results[alg]:
worksheet.write(row, col, bench)
nRuns = len(self.results[alg][bench])
for i in range(len(self.results[alg][bench])):
row += 1
worksheet.write(row, col, self.results[alg][bench][i])
row -= len(self.results[alg][bench]) # jump back up
col += 1
row += 1 + nRuns # jump down to row after previous results
col -= 1 + len(self.results[alg])
workbook.close()
logger.info('Export to XLSX completed!')
def __exportToLatex(self):
self.__createExportDir()
metrics = ['Best', 'Median', 'Worst', 'Mean', 'Std.']
def only_upper(s):
return "".join(c for c in s if c.isupper())
with open(self.__generateExportName('tex'), 'a') as outFile:
outFile.write('\\documentclass{article}\n')
outFile.write('\\usepackage[utf8]{inputenc}\n')
outFile.write('\\usepackage{siunitx}\n')
outFile.write('\\sisetup{\n')
outFile.write('round-mode=places,round-precision=3}\n')
outFile.write('\\begin{document}\n')
outFile.write('\\begin{table}[h]\n')
outFile.write('\\centering\n')
begin_tabular = '\\begin{tabular}{cc'
for alg in self.results:
for _i in range(len(self.results[alg])):
begin_tabular += 'S'
firstLine = ' &'
for benchmark in self.results[alg].keys():
firstLine += ' & \\multicolumn{1}{c}{\\textbf{' + \
benchmark + '}}'
firstLine += ' \\\\'
break
begin_tabular += '}\n'
outFile.write(begin_tabular)
outFile.write('\\hline\n')
outFile.write(firstLine + '\n')
outFile.write('\\hline\n')
for alg in self.results:
for metric in metrics:
line = ''
if metric != 'Worst':
line += ' & ' + metric
else:
shortAlg = ''
if alg.endswith('Algorithm'):
shortAlg = only_upper(alg[:-9])
else:
shortAlg = only_upper(alg)
line += '\\textbf{' + shortAlg + '} & ' + metric
for benchmark in self.results[alg]:
if metric == 'Best':
line += ' & ' + \
str(np.amin(self.results[alg][benchmark]))
elif metric == 'Median':
line += ' & ' + \
str(np.median(self.results[alg][benchmark]))
elif metric == 'Worst':
line += ' & ' + \
str(np.amax(self.results[alg][benchmark]))
elif metric == 'Mean':
line += ' & ' + \
str(np.mean(self.results[alg][benchmark]))
else:
line += ' & ' + \
str(np.std(self.results[alg][benchmark]))
line += ' \\\\'
outFile.write(line + '\n')
outFile.write('\\hline\n')
outFile.write('\\end{tabular}\n')
outFile.write('\\end{table}\n')
outFile.write('\\end{document}')
logger.info('Export to Latex completed!')
def run(self, export='log', verbose=False):
"""Execute runner.
Keyword Arguments:
export {string} -- takes export type (e.g. log, json, xlsx, latex) (default: 'log')
verbose {boolean} -- switch for verbose logging (default: {False})
Raises:
TypeError -- Raises TypeError if export type is not supported
Returns:
Dictionary -- Returns dictionary of results
"""
for alg in self.useAlgorithms:
self.results[alg] = {}
if verbose:
logger.info('Running %s...', alg)
for bench in self.useBenchmarks:
benchName = ''
# check if passed benchmark is class
if not isinstance(bench, ''.__class__):
# set class name as benchmark name
benchName = str(type(bench).__name__)
else:
benchName = bench
if verbose:
logger.info(
'Running %s algorithm on %s benchmark...', alg, benchName)
self.results[alg][benchName] = []
for _i in range(self.nRuns):
algorithm = self.__algorithmFactory(alg, bench)
self.results[alg][benchName].append(algorithm.run())
if verbose:
logger.info(
'---------------------------------------------------')
if export == 'log':
self.__exportToLog()
elif export == 'json':
self.__exportToJson()
elif export == 'xlsx':
self.__exportToXls()
elif export == 'latex':
self.__exportToLatex()
else:
raise TypeError('Passed export type is not supported!')
return self.results