TroglodyteDerivations commited on
Commit
7775b72
1 Parent(s): 47d7fb1

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +500 -0
app.py ADDED
@@ -0,0 +1,500 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import math
3
+ import matplotlib.pyplot as plt
4
+ from io import BytesIO
5
+ from PIL import Image
6
+ import gradio as gr
7
+ from mpl_toolkits.mplot3d import Axes3D
8
+
9
+ class Objective:
10
+ def Evaluate(self, p):
11
+ return -5.0*np.exp(-0.5*((p[0]+2.2)**2/0.4+(p[1]-4.3)**2/0.4)) + -2.0*np.exp(-0.5*((p[0]-2.2)**2/0.4+(p[1]+4.3)**2/0.4))
12
+
13
+ # Create an instance of the Objective class
14
+ obj = Objective()
15
+
16
+ # Evaluate the fitness of a position
17
+ position = np.array([-2.2, 4.3])
18
+ fitness = obj.Evaluate(position)
19
+
20
+ print(f"The fitness of the position {position} is {fitness}")
21
+
22
+ class Bounds:
23
+ def __init__(self, lower, upper, enforce="clip"):
24
+ self.lower = np.array(lower)
25
+ self.upper = np.array(upper)
26
+ self.enforce = enforce.lower()
27
+
28
+ def Upper(self):
29
+ return self.upper
30
+
31
+ def Lower(self):
32
+ return self.lower
33
+
34
+ def Limits(self, pos):
35
+ npart, ndim = pos.shape
36
+ for i in range(npart):
37
+ for j in range(ndim):
38
+ if pos[i, j] < self.lower[j]:
39
+ if self.enforce == "clip":
40
+ pos[i, j] = self.lower[j]
41
+ elif self.enforce == "resample":
42
+ pos[i, j] = self.lower[j] + np.random.random() * (self.upper[j] - self.lower[j])
43
+ elif pos[i, j] > self.upper[j]:
44
+ if self.enforce == "clip":
45
+ pos[i, j] = self.upper[j]
46
+ elif self.enforce == "resample":
47
+ pos[i, j] = self.lower[j] + np.random.random() * (self.upper[j] - self.lower[j])
48
+ pos[i] = self.Validate(pos[i])
49
+ return pos
50
+
51
+ def Validate(self, pos):
52
+ return pos
53
+
54
+ # Define the bounds
55
+ lower_bounds = [-6, -6]
56
+ upper_bounds = [6, 6]
57
+
58
+ # Create an instance of the Bounds class
59
+ bounds = Bounds(lower_bounds, upper_bounds, enforce="clip")
60
+
61
+ # Define a set of positions
62
+ positions = np.array([[15, 15], [-15, -15], [5, 15], [15, 5]])
63
+
64
+ # Enforce the bounds on the positions
65
+ valid_positions = bounds.Limits(positions)
66
+
67
+ print(f"Valid positions: {valid_positions}")
68
+
69
+ # Define the bounds
70
+ lower_bounds = [-6, -6]
71
+ upper_bounds = [6, 6]
72
+
73
+ # Create an instance of the Bounds class
74
+ bounds = Bounds(lower_bounds, upper_bounds, enforce="resample")
75
+
76
+ # Define a set of positions
77
+ positions = np.array([[15, 15], [-15, -15], [5, 15], [15, 5]])
78
+
79
+ # Enforce the bounds on the positions
80
+ valid_positions = bounds.Limits(positions)
81
+
82
+ print(f"Valid positions: {valid_positions}")
83
+
84
+ class QuasiRandomInitializer:
85
+ def __init__(self, npart=10, ndim=2, bounds=None, k=1, jitter=0.0):
86
+ self.npart = npart
87
+ self.ndim = ndim
88
+ self.bounds = bounds
89
+ self.k = k
90
+ self.jitter = jitter
91
+ self.primes = [
92
+ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,
93
+ 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197,
94
+ 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313,
95
+ 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439,
96
+ 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571,
97
+ 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659
98
+ ]
99
+
100
+ def Halton(self, i, b):
101
+ f = 1.0
102
+ r = 0
103
+ while i > 0:
104
+ f = f / b
105
+ r = r + f * (i % b)
106
+ i = math.floor(i / b)
107
+ return r
108
+
109
+ def InitializeSwarm(self):
110
+ self.swarm = np.zeros((self.npart, self.ndim))
111
+ lo = np.zeros(self.ndim)
112
+ hi = np.ones(self.ndim)
113
+ if self.bounds is not None:
114
+ lo = self.bounds.Lower()
115
+ hi = self.bounds.Upper()
116
+
117
+ for i in range(self.npart):
118
+ for j in range(self.ndim):
119
+ h = self.Halton(i + self.k, self.primes[j % len(self.primes)])
120
+ q = self.jitter * (np.random.random() - 0.5)
121
+ self.swarm[i, j] = lo[j] + (hi[j] - lo[j]) * h + q
122
+
123
+ if self.bounds is not None:
124
+ self.swarm = self.bounds.Limits(self.swarm)
125
+
126
+ return self.swarm
127
+
128
+ # Define the bounds
129
+ lower_bounds = [-6, -6]
130
+ upper_bounds = [6, 6]
131
+ bounds = Bounds(lower_bounds, upper_bounds, enforce="clip")
132
+
133
+ # Create an instance of the QuasiRandomInitializer class
134
+ init = QuasiRandomInitializer(npart=50, ndim=2, bounds=bounds)
135
+
136
+ # Initialize the swarm
137
+ swarm_positions = init.InitializeSwarm()
138
+
139
+ print(f"Initial swarm positions: {swarm_positions}")
140
+
141
+ # Define the bounds
142
+ lower_bounds = [-6, -6]
143
+ upper_bounds = [6, 6]
144
+ bounds = Bounds(lower_bounds, upper_bounds, enforce="resample")
145
+
146
+ # Create an instance of the QuasiRandomInitializer class
147
+ init = QuasiRandomInitializer(npart=50, ndim=2, bounds=bounds)
148
+
149
+ # Initialize the swarm
150
+ swarm_positions = init.InitializeSwarm()
151
+
152
+ print(f"Initial swarm positions: {swarm_positions}")
153
+
154
+ class GWO:
155
+ def __init__(self, obj, eta=2.0, npart=10, ndim=2, max_iter=200,tol=None,init=None,done=None,bounds=None):
156
+ self.obj = obj
157
+ self.npart = npart
158
+ self.ndim = ndim
159
+ self.max_iter = max_iter
160
+ self.init = init
161
+ self.done = done
162
+ self.bounds = bounds
163
+ self.tol = tol
164
+ self.eta = eta
165
+ self.initialized = False
166
+
167
+ def Initialize(self):
168
+ """Set up the swarm"""
169
+
170
+ self.initialized = True
171
+ self.iterations = 0
172
+
173
+ self.pos = self.init.InitializeSwarm() # initial swarm positions
174
+ self.vpos= np.zeros(self.npart)
175
+ for i in range(self.npart):
176
+ self.vpos[i] = self.obj.Evaluate(self.pos[i])
177
+
178
+ # Initialize the list to store positions at each iteration
179
+ self.all_positions = []
180
+ self.all_positions.append(self.pos.copy()) # Store the initial positions
181
+
182
+ # Swarm bests
183
+ self.gidx = []
184
+ self.gbest = []
185
+ self.gpos = []
186
+ self.giter = []
187
+ idx = np.argmin(self.vpos)
188
+ self.gidx.append(idx)
189
+ self.gbest.append(self.vpos[idx])
190
+ self.gpos.append(self.pos[idx].copy())
191
+ self.giter.append(0)
192
+
193
+ # 1st, 2nd, and 3rd best positions
194
+ idx = np.argsort(self.vpos)
195
+ self.alpha = self.pos[idx[0]].copy()
196
+ self.valpha= self.vpos[idx[0]]
197
+ self.beta = self.pos[idx[1]].copy()
198
+ self.vbeta = self.vpos[idx[1]]
199
+ self.delta = self.pos[idx[2]].copy()
200
+ self.vdelta= self.vpos[idx[2]]
201
+
202
+ # *** Gradio app method optimize created [leveraged vis-a-vis optimize function on the outside of the underlying anatomy of GWO class] ***
203
+ def optimize(self):
204
+ """
205
+ Run a full optimization and return the best positions and fitness values.
206
+ This method is designed to be used with Gradio.
207
+ """
208
+ # Initialize the swarm
209
+ self.Initialize()
210
+
211
+ # Lists to store the best positions and fitness values at each step
212
+ best_positions = []
213
+ best_fitness = []
214
+
215
+ # Main loop
216
+ while not self.Done():
217
+ self.Step() # Perform an optimization step
218
+ # Update best_positions and best_fitness with the current best values
219
+ best_positions.append(self.gbest[-1])
220
+ best_fitness.append(self.gpos[-1])
221
+
222
+ # Print the best positions and fitness found
223
+ print("Best Positions:", best_positions)
224
+ print("Best Fitness:", best_fitness)
225
+
226
+ # Return the best positions and fitness after the optimization
227
+ return best_positions, best_fitness
228
+
229
+ def Step(self):
230
+ """Do one swarm step"""
231
+
232
+ # a from eta ... zero (default eta is 2)
233
+ a = self.eta - self.eta*(self.iterations/self.max_iter)
234
+
235
+ # Update everyone
236
+ for i in range(self.npart):
237
+ A = 2*a*np.random.random(self.ndim) - a
238
+ C = 2*np.random.random(self.ndim)
239
+ Dalpha = np.abs(C*self.alpha - self.pos[i])
240
+ X1 = self.alpha - A*Dalpha
241
+
242
+ A = 2*a*np.random.random(self.ndim) - a
243
+ C = 2*np.random.random(self.ndim)
244
+ Dbeta = np.abs(C*self.beta - self.pos[i])
245
+ X2 = self.beta - A*Dbeta
246
+
247
+ A = 2*a*np.random.random(self.ndim) - a
248
+ C = 2*np.random.random(self.ndim)
249
+ Ddelta = np.abs(C*self.delta - self.pos[i])
250
+ X3 = self.delta - A*Ddelta
251
+
252
+ self.pos[i,:] = (X1+X2+X3) / 3.0
253
+
254
+ # Keep in bounds
255
+ if (self.bounds != None):
256
+ self.pos = self.bounds.Limits(self.pos)
257
+
258
+ # Get objective function values and check for new leaders
259
+ for i in range(self.npart):
260
+ self.vpos[i] = self.obj.Evaluate(self.pos[i])
261
+
262
+ # new alpha?
263
+ if (self.vpos[i] < self.valpha):
264
+ self.vdelta = self.vbeta
265
+ self.delta = self.beta.copy()
266
+ self.vbeta = self.valpha
267
+ self.beta = self.alpha.copy()
268
+ self.valpha = self.vpos[i]
269
+ self.alpha = self.pos[i].copy()
270
+
271
+ # new beta?
272
+ if (self.vpos[i] > self.valpha) and (self.vpos[i] < self.vbeta):
273
+ self.vdelta = self.vbeta
274
+ self.delta = self.beta.copy()
275
+ self.vbeta = self.vpos[i]
276
+ self.beta = self.pos[i].copy()
277
+
278
+ # new delta?
279
+ if (self.vpos[i] > self.valpha) and (self.vpos[i] < self.vbeta) and (self.vpos[i] < self.vdelta):
280
+ self.vdelta = self.vpos[i]
281
+ self.delta = self.pos[i].copy()
282
+
283
+ # is alpha new swarm best?
284
+ if (self.valpha < self.gbest[-1]):
285
+ self.gidx.append(i)
286
+ self.gbest.append(self.valpha)
287
+ np.save('best_fitness.npy', np.array(self.gbest))
288
+ self.gpos.append(self.alpha.copy())
289
+ np.save('best_positions.npy', np.array(self.gpos))
290
+ # Save the positions at the current iteration
291
+ self.all_positions.append(self.pos.copy())
292
+ np.save('all_positions.npy', np.array(self.all_positions), allow_pickle=True)
293
+ self.giter.append(self.iterations)
294
+
295
+ self.iterations += 1
296
+
297
+ def Done(self):
298
+ """Check if we are done"""
299
+
300
+ if (self.done == None):
301
+ if (self.tol == None):
302
+ return (self.iterations == self.max_iter)
303
+ else:
304
+ return (self.gbest[-1] < self.tol) or (self.iterations == self.max_iter)
305
+ else:
306
+ return self.done.Done(self.gbest,
307
+ gpos=self.gpos,
308
+ pos=self.pos,
309
+ max_iter=self.max_iter,
310
+ iteration=self.iterations)
311
+
312
+ def Evaluate(self, pos):
313
+ p = np.zeros(self.npart)
314
+ for i in range(self.npart):
315
+ p[i] = self.obj.Evaluate(pos[i])
316
+ return p
317
+
318
+ def Results(self):
319
+ if (not self.initialized):
320
+ return None
321
+ return {
322
+ "npart": self.npart,
323
+ "ndim": self.ndim,
324
+ "max_iter": self.max_iter,
325
+ "iterations": self.iterations,
326
+ "tol": self.tol,
327
+ "eta": self.eta,
328
+ "gbest": self.gbest,
329
+ "giter": self.giter,
330
+ "gpos": self.gpos,
331
+ "gidx": self.gidx,
332
+ "pos": self.pos,
333
+ "vpos": self.vpos
334
+ }
335
+ def plot_contour_and_wolves(self, wolf_positions):
336
+ # Define the objective function
337
+ def objective_function(x, y):
338
+ return -5.0*np.exp(-0.5*((x+2.2)**2/0.4+(y-4.3)**2/0.4)) + -2.0*np.exp(-0.5*((x-2.2)**2/0.4+(y+4.3)**2/0.4))
339
+
340
+ best_postions_npy = np.load('best_positions.npy')
341
+
342
+ wolf_positions = best_positions_npy
343
+
344
+ # Determine the search space boundaries based on the wolf positions
345
+ x_min, x_max = wolf_positions[:, 0].min() - 1, wolf_positions[:, 0].max() + 1
346
+ y_min, y_max = wolf_positions[:, 1].min() - 1, wolf_positions[:, 1].max() + 1
347
+
348
+ # Generate a grid of points within the determined search space
349
+ x = np.linspace(x_min, x_max, 100)
350
+ y = np.linspace(y_min, y_max, 100)
351
+ X, Y = np.meshgrid(x, y)
352
+
353
+ # Evaluate the objective function on the grid
354
+ Z = objective_function(X, Y)
355
+
356
+ # Plot the contour map
357
+ plt.figure(figsize=(10, 8))
358
+ contour = plt.contour(X, Y, Z, levels=20, cmap='viridis')
359
+ plt.colorbar(contour)
360
+
361
+ # Plot the wolf positions
362
+ plt.plot(wolf_positions[:, 0], wolf_positions[:, 1], 'ro', markersize=5, label='Wolves')
363
+
364
+ # Set plot title and labels
365
+ plt.title('Contour Map of Wolves Oscillating Over Search Space')
366
+ plt.xlabel('x')
367
+ plt.ylabel('y')
368
+ plt.legend()
369
+
370
+ # Set the x and y limits of the plot based on the determined search space
371
+ plt.xlim(x_min, x_max)
372
+ plt.ylim(y_min, y_max)
373
+
374
+ # Convert the plot to a PIL Image and return it
375
+ buf = BytesIO()
376
+ plt.savefig(buf, format='png')
377
+ buf.seek(0)
378
+ plt.close() # Close the figure to free up memory
379
+ return Image.open(buf)
380
+
381
+ def Dispersion(self):
382
+ """Calculate the dispersion of the swarm"""
383
+ x, y = self.gpos[:, 0], self.gpos[:, 1]
384
+ dx = x.max() - x.min()
385
+ dy = y.max() - y.min()
386
+ return (dx + dy) / 2.0
387
+
388
+ def plot_dispersion(self):
389
+ """Plot the dispersion over time"""
390
+ # Assuming self.giter stores the iteration number at which each best position was found
391
+ # and self.gbest stores the corresponding best fitness values
392
+ dispersion_values = [self.Dispersion() for _ in range(self.max_iter)]
393
+
394
+ plt.figure(figsize=(10, 6))
395
+ plt.plot(range(self.max_iter), dispersion_values, label='Dispersion')
396
+ plt.xlabel('Iteration')
397
+ plt.ylabel('Dispersion')
398
+ plt.title('Evolution of Dispersion')
399
+ plt.legend()
400
+ plt.show()
401
+
402
+ # Convert the plot to a PIL Image and return it
403
+ buf = BytesIO()
404
+ plt.savefig(buf, format='png')
405
+ buf.seek(0)
406
+ plt.close() # Close the figure to free up memory
407
+ return Image.open(buf)
408
+
409
+ def plot_dispersion_heatmap(self, x_range, y_range, grid_size=100):
410
+ """Plot a heatmap of dispersion over a grid of positions"""
411
+ x_values = np.linspace(x_range[0], x_range[1], grid_size)
412
+ y_values = np.linspace(y_range[0], y_range[1], grid_size)
413
+ X, Y = np.meshgrid(x_values, y_values)
414
+ positions = np.vstack([X.ravel(), Y.ravel()]).T
415
+
416
+ # Calculate dispersion for each position in the grid
417
+ dispersion_values = np.array([self.Dispersion(pos) for pos in positions])
418
+ Z = dispersion_values.reshape(X.shape)
419
+
420
+ plt.figure(figsize=(10, 8))
421
+ plt.imshow(Z, extent=(x_range[0], x_range[1], y_range[0], y_range[1]), origin='lower', cmap='viridis')
422
+ plt.colorbar(label='Dispersion')
423
+ plt.title('Heatmap of Dispersion')
424
+ plt.xlabel('X')
425
+ plt.ylabel('Y')
426
+ plt.show()
427
+
428
+ # Convert the plot to a PIL Image and return it
429
+ buf = BytesIO()
430
+ plt.savefig(buf, format='png')
431
+ buf.seek(0)
432
+ plt.close() # Close the figure to free up memory
433
+ return Image.open(buf)
434
+
435
+
436
+
437
+
438
+
439
+ def optimize(npart, ndim, max_iter):
440
+ # Initialize the GWO algorithm with the provided parameters
441
+ gwo = GWO(obj=obj, npart=npart, ndim=ndim, max_iter=max_iter, init=init, bounds=bounds)
442
+
443
+ # Run the optimization
444
+ best_positions, best_fitness = gwo.optimize()
445
+
446
+ # Get the best fitness and positions at the last iteration
447
+ last_best_fitness = best_fitness[-1]
448
+ last_best_positions = best_positions[-1]
449
+
450
+ # Format the output strings
451
+ best_fitness_text = f"Best Positions: {last_best_fitness}"
452
+ best_positions_text = f"Best Fitness: {last_best_positions}"
453
+
454
+ # Get the contour plot as a PIL Image
455
+ contour_plot = gwo.plot_contour_and_wolves(wolf_positions)
456
+
457
+ # Calculate the dispersion
458
+ dispersion = gwo.Dispersion()
459
+
460
+ # Format the output strings
461
+ best_fitness_text = f"Best Positions: {last_best_fitness}"
462
+ best_positions_text = f"Best Fitness: {last_best_positions}"
463
+ dispersion_text = f"Dispersion: {dispersion}"
464
+
465
+ # Plot the dispersion over time
466
+ dispersion_plot = gwo.plot_dispersion()
467
+
468
+ # Plot the dispersion heatmap
469
+ dispersion_heatmap_plot = gwo.plot_dispersion_heatmap(x_range=(-6,6), y_range=(-6,6))
470
+
471
+
472
+ # Return the positions plot, the text for the best fitness and positions, and the loaded data
473
+ return [contour_plot, dispersion_plot, dispersion_heatmap_plot], best_fitness_text, best_positions_text, best_fitness_npy, best_positions_npy, dispersion_text
474
+
475
+
476
+
477
+ # Define the Gradio interface
478
+ iface = gr.Interface(
479
+ fn=optimize, # Pass the optimize function object
480
+ inputs=[
481
+ gr.components.Slider(10, 50, 50, step=1, label="Number of Wolves [Default = 50 Wolves]"),
482
+ gr.components.Slider(2, 2, 2, step=1, label="Two-Dimensional Search Space"),
483
+ gr.components.Slider(100, 200, 200, step=1, label="Maximum Iterations [Default = 200 Epochs]"),
484
+ ],
485
+ outputs=[
486
+ gr.components.Image(type="pil", label="Contour Map of Wolves Oscillating Over Search Space"),
487
+ gr.components.Image(type="pil", label="Dispersion Plot"),
488
+ gr.components.Image(type="pil", label="Dispersion Heatmap Plot"),
489
+ gr.components.Textbox(label="Dispersion Values"),
490
+ gr.components.Textbox(label="Best Positions"),
491
+ gr.components.Textbox(label="Best Fitness")
492
+
493
+ ],
494
+ title="Grey Wolf Optimizer",
495
+ description="Optimize the objective function using the Grey Wolf Optimizer.",
496
+ article="## Grey Wolf Optimizer\nThe Grey Wolf Optimizer (GWO) is a population-based metaheuristic optimization algorithm inspired by the social behavior of grey wolves in nature."
497
+ )
498
+
499
+ # Launch the Gradio interface
500
+ iface.launch()