Passed
Pull Request — master (#101)
by Grega
02:11
created

Runner.__exportToXls()   B

Complexity

Conditions 4

Size

Total Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
c 1
b 0
f 0
dl 0
loc 31
rs 8.5806
1
"""Python micro framework for building nature-inspired algorithms."""
2
3
from __future__ import print_function  # for backward compatibility purpose
4
5
import os
6
import logging
7
import json
8
import datetime
9
import xlsxwriter
10
import numpy as np
11
from NiaPy import algorithms, benchmarks
12
13
__all__ = ['algorithms', 'benchmarks']
14
__project__ = 'NiaPy'
15
__version__ = '0.1.3a2'
16
17
VERSION = "{0} v{1}".format(__project__, __version__)
18
19
logging.basicConfig()
20
logger = logging.getLogger('NiaPy')
21
logger.setLevel('INFO')
22
23
24
class Runner(object):
25
    """Runner utility feature.
26
27
    Feature which enables running multiple algorithms with multiple benchmarks.
28
    It also support exporting results in various formats (e.g. LaTeX, Excel, JSON)
29
30
    """
31
32
    def __init__(self, D, NP, nFES, nRuns, useAlgorithms, useBenchmarks, A=0.5, r=0.5,
33
                 Qmin=0.0, Qmax=2.0, Pa=0.25, F=0.5, CR=0.9, alpha=0.5, betamin=0.2, gamma=1.0,
34
                 p=0.5, Ts=4, Mr=0.05, C1=2.0, C2=2.0, w=0.7, vMin=-4, vMax=4, Tao=0.1):
35
        r"""Initialize Runner.
36
37
        **__init__(self, D, NP, nFES, nRuns, useAlgorithms, useBenchmarks, A=0.5, r=0.5,
38
                   Qmin=0.0, Qmax=2.0, Pa=0.25, F=0.5, CR=0.9, alpha=0.5, betamin=0.2, gamma=1.0,
39
                   p=0.5, Ts=4, Mr=0.05, C1=2.0, C2=2.0, w=0.7, vMin=-4, vMax=4, Tao=0.1)**
40
41
        Arguments:
42
            D {integer} -- dimension of problem
43
44
            NP {integer} -- population size
45
46
            nFES {integer} -- number of function evaluations
47
48
            nRuns {integer} -- number of repetitions
49
50
            useAlgorithms [] -- array of algorithms to run
51
52
            useBenchmarks [] -- array of benchmarks to run
53
54
            A {decimal} -- laudness
55
56
            r {decimal} -- pulse rate
57
58
            Qmin {decimal} -- minimum frequency
59
60
            Qmax {decimal} -- maximum frequency
61
62
            Pa {decimal} -- probability
63
64
            F {decimal} -- scalling factor
65
66
            CR {decimal} -- crossover rate
67
68
            alpha {decimal} -- alpha parameter
69
70
            betamin {decimal} -- betamin parameter
71
72
            gamma {decimal} -- gamma parameter
73
74
            p {decimal} -- probability switch
75
76
            Ts {decimal}
77
78
            Mr {decimal}
79
80
            C1 {decimal} -- cognitive component
81
82
            C2 {decimal} -- social component
83
84
            w {decimal} -- inertia weight
85
86
            vMin {decimal} -- minimal velocity
87
88
            vMax {decimal} -- maximal velocity
89
90
            Tao {decimal}
91
        """
92
93
        self.D = D
94
        self.NP = NP
95
        self.nFES = nFES
96
        self.nRuns = nRuns
97
        self.useAlgorithms = useAlgorithms
98
        self.useBenchmarks = useBenchmarks
99
        self.A = A
100
        self.r = r
101
        self.Qmin = Qmin
102
        self.Qmax = Qmax
103
        self.Pa = Pa
104
        self.F = F
105
        self.CR = CR
106
        self.alpha = alpha
107
        self.betamin = betamin
108
        self.gamma = gamma
109
        self.p = p
110
        self.Ts = Ts
111
        self.Mr = Mr
112
        self.C1 = C1
113
        self.C2 = C2
114
        self.w = w
115
        self.vMin = vMin
116
        self.vMax = vMax
117
        self.Tao = Tao
118
        self.results = {}
119
120
    def __algorithmFactory(self, name, benchmark):
121
        bench = benchmarks.utility.Utility().get_benchmark(benchmark)
122
        algorithm = None
123
124
        if name == 'BatAlgorithm':
125
            algorithm = algorithms.basic.BatAlgorithm(
126
                self.D, self.NP, self.nFES, self.A, self.r, self.Qmin, self.Qmax, bench)
127
        elif name == 'DifferentialEvolutionAlgorithm':
128
            algorithm = algorithms.basic.DifferentialEvolutionAlgorithm(
129
                self.D, self.NP, self.nFES, self.F, self.CR, bench)
130
        elif name == 'FireflyAlgorithm':
131
            algorithm = algorithms.basic.FireflyAlgorithm(
132
                self.D, self.NP, self.nFES, self.alpha, self.betamin, self.gamma, bench)
133
        elif name == 'FlowerPollinationAlgorithm':
134
            algorithm = algorithms.basic.FlowerPollinationAlgorithm(
135
                self.D, self.NP, self.nFES, self.p, bench)
136
        elif name == 'GreyWolfOptimizer':
137
            algorithm = algorithms.basic.GreyWolfOptimizer(
138
                self.D, self.NP, self.nFES, bench)
139
        elif name == 'ArtificialBeeColonyAlgorithm':
140
            algorithm = algorithms.basic.ArtificialBeeColonyAlgorithm(
141
                self.D, self.NP, self.nFES, bench)
142
        elif name == 'CuckooSearchAlgorithm':
143
            algorithm = algorithms.basic.CuckooSearchAlgorithm(
144
                self.D, self.NP, self.nFES, self.Pa, self.alpha, bench)
145
        elif name == 'GeneticAlgorithm':
146
            algorithm = algorithms.basic.GeneticAlgorithm(
147
                self.D, self.NP, self.nFES, self.Ts, self.Mr, self.gamma, bench)
148
        elif name == 'ParticleSwarmAlgorithm':
149
            algorithm = algorithms.basic.ParticleSwarmAlgorithm(
150
                self.D, self.NP, self.nFES, self.C1, self.C2, self.w, self.vMin, self.vMax, bench)
151
        elif name == 'HybridBatAlgorithm':
152
            algorithm = algorithms.modified.HybridBatAlgorithm(
153
                self.D, self.NP, self.nFES, self.A, self.r, self.F, self.CR, self.Qmin, self.Qmax, bench)
154
        elif name == 'SelfAdaptiveDifferentialEvolutionAlgorithm':
155
            algorithm = algorithms.modified.SelfAdaptiveDifferentialEvolutionAlgorithm(
156
                self.D, self.NP, self.nFES, self.F, self.CR, self.Tao, bench)
157
        else:
158
            raise TypeError('Passed benchmark is not defined!')
159
160
        return algorithm
161
162
    @classmethod
163
    def __createExportDir(cls):
164
        if not os.path.exists('export'):
165
            os.makedirs('export')
166
167
    @classmethod
168
    def __generateExportName(cls, extension):
169
        return 'export/' + str(datetime.datetime.now()).replace(':', '.') + '.' + extension
170
171
    def __exportToLog(self):
172
        print(self.results)
173
174
    def __exportToJson(self):
175
        self.__createExportDir()
176
        with open(self.__generateExportName('json'), 'w') as outFile:
177
            json.dump(self.results, outFile)
178
            logger.info('Export to JSON completed!')
179
180
    def __exportToXls(self):
181
        self.__createExportDir()
182
183
        workbook = xlsxwriter.Workbook(self.__generateExportName('xlsx'))
184
        worksheet = workbook.add_worksheet()
185
186
        row = 0
187
        col = 0
188
        nRuns = 0
189
190
        for alg in self.results:
191
            worksheet.write(row, col, alg)
192
            col += 1
193
194
            for bench in self.results[alg]:
195
                worksheet.write(row, col, bench)
196
197
                nRuns = len(self.results[alg][bench])
198
199
                for i in range(len(self.results[alg][bench])):
200
                    row += 1
201
                    worksheet.write(row, col, self.results[alg][bench][i])
202
203
                row -= len(self.results[alg][bench])  # jump back up
204
                col += 1
205
206
            row += 1 + nRuns  # jump down to row after previous results
207
            col -= 1 + len(self.results[alg])
208
209
        workbook.close()
210
        logger.info('Export to XLSX completed!')
211
212
    def __exportToLatex(self):
213
        self.__createExportDir()
214
215
        metrics = ['Best', 'Median', 'Worst', 'Mean', 'Std.']
216
217
        def only_upper(s):
218
            return "".join(c for c in s if c.isupper())
219
220
        with open(self.__generateExportName('tex'), 'a') as outFile:
221
            outFile.write('\\documentclass{article}\n')
222
            outFile.write('\\usepackage[utf8]{inputenc}\n')
223
            outFile.write('\\usepackage{siunitx}\n')
224
            outFile.write('\\sisetup{\n')
225
            outFile.write('round-mode=places,round-precision=3}\n')
226
            outFile.write('\\begin{document}\n')
227
            outFile.write('\\begin{table}[h]\n')
228
            outFile.write('\\centering\n')
229
230
            begin_tabular = '\\begin{tabular}{cc'
231
232
            for alg in self.results:
233
                for _i in range(len(self.results[alg])):
234
                    begin_tabular += 'S'
235
236
                firstLine = '   &'
237
238
                for benchmark in self.results[alg].keys():
239
                    firstLine += '  &   \\multicolumn{1}{c}{\\textbf{' + \
240
                        benchmark + '}}'
241
242
                firstLine += ' \\\\'
243
244
                break
245
246
            begin_tabular += '}\n'
247
            outFile.write(begin_tabular)
248
            outFile.write('\\hline\n')
249
            outFile.write(firstLine + '\n')
250
            outFile.write('\\hline\n')
251
252
            for alg in self.results:
253
                for metric in metrics:
254
                    line = ''
255
256
                    if metric != 'Worst':
257
                        line += '   &   ' + metric
258
                    else:
259
                        shortAlg = ''
260
                        if alg.endswith('Algorithm'):
261
                            shortAlg = only_upper(alg[:-9])
262
                        else:
263
                            shortAlg = only_upper(alg)
264
                        line += '\\textbf{' + shortAlg + '} &   ' + metric
265
266
                    for benchmark in self.results[alg]:
267
                        if metric == 'Best':
268
                            line += '   &   ' + \
269
                                str(np.amin(self.results[alg][benchmark]))
270
                        elif metric == 'Median':
271
                            line += '   &   ' + \
272
                                str(np.median(self.results[alg][benchmark]))
273
                        elif metric == 'Worst':
274
                            line += '   &   ' + \
275
                                str(np.amax(self.results[alg][benchmark]))
276
                        elif metric == 'Mean':
277
                            line += '   &   ' + \
278
                                str(np.mean(self.results[alg][benchmark]))
279
                        else:
280
                            line += '   &   ' + \
281
                                str(np.std(self.results[alg][benchmark]))
282
283
                    line += '   \\\\'
284
                    outFile.write(line + '\n')
285
286
                outFile.write('\\hline\n')
287
            outFile.write('\\end{tabular}\n')
288
            outFile.write('\\end{table}\n')
289
            outFile.write('\\end{document}')
290
291
        logger.info('Export to Latex completed!')
292
293
    def run(self, export='log', verbose=False):
294
        """Execute runner.
295
296
        Keyword Arguments:
297
            export  {string}  -- takes export type (e.g. log, json, xlsx, latex) (default: 'log')
298
            verbose {boolean} -- switch for verbose logging (default: {False})
299
300
        Raises:
301
            TypeError -- Raises TypeError if export type is not supported
302
303
        Returns:
304
            Dictionary -- Returns dictionary of results
305
306
        """
307
308
        for alg in self.useAlgorithms:
309
            self.results[alg] = {}
310
            if verbose:
311
                logger.info('Running %s...', alg)
312
            for bench in self.useBenchmarks:
313
                benchName = ''
314
                # check if passed benchmark is class
315
                if not isinstance(bench, ''.__class__):
316
                    # set class name as benchmark name
317
                    benchName = str(type(bench).__name__)
318
                else:
319
                    benchName = bench
320
321
                if verbose:
322
                    logger.info(
323
                        'Running %s algorithm on %s benchmark...', alg, benchName)
324
325
                self.results[alg][benchName] = []
326
327
                for _i in range(self.nRuns):
328
                    algorithm = self.__algorithmFactory(alg, bench)
329
                    self.results[alg][benchName].append(algorithm.run())
330
331
            if verbose:
332
                logger.info(
333
                    '---------------------------------------------------')
334
335
        if export == 'log':
336
            self.__exportToLog()
337
        elif export == 'json':
338
            self.__exportToJson()
339
        elif export == 'xlsx':
340
            self.__exportToXls()
341
        elif export == 'latex':
342
            self.__exportToLatex()
343
        else:
344
            raise TypeError('Passed export type is not supported!')
345
346
        return self.results
347