KARTHIK REDDY commited on
Commit
d03075d
1 Parent(s): ed8abd5

lets do this

Browse files
Files changed (3) hide show
  1. Dockerfile +16 -0
  2. app.py +372 -0
  3. requirements.txt +10 -0
Dockerfile ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
2
+ # you will also find guides on how best to write your Dockerfile
3
+
4
+ FROM python:3.9
5
+
6
+ WORKDIR /code
7
+
8
+ COPY ./requirements.txt /code/requirements.txt
9
+
10
+ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
11
+
12
+ COPY . .
13
+
14
+ ENV BOKEH_ALLOW_WS_ORIGIN=kkr5155-distributedswarmintelligence.hf.space
15
+
16
+ CMD ["panel", "serve", "/code/app.py", "--address", "0.0.0.0","--port", "7860", "--allow-websocket-origin=kkr5155-distributedswarmintelligence.hf.space"]
app.py ADDED
@@ -0,0 +1,372 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+ import numpy as np
3
+ import threading
4
+ import panel as pn
5
+ pn.extension(template='bootstrap')
6
+ import holoviews as hv
7
+ import time
8
+ import pandas as pd
9
+ from holoviews.streams import Stream
10
+ hv.extension('bokeh', logo=False)
11
+
12
+ # Particle class: Each particle will be an object of this class with all the properties defined in __init__() method
13
+ class Particle():
14
+
15
+ # Method to initialize particle properties
16
+ def __init__(self, initial):
17
+ self.position = []
18
+ self.velocity = []
19
+ self.initial = initial
20
+ self.best_position = []
21
+ self.best_error = float('inf') # Initialize best_error with infinity
22
+ self.error = float('inf') # Initialize error with infinity
23
+ self.num_dimensions = 2
24
+ for i in range(0, self.num_dimensions):
25
+ self.velocity.append(random.uniform(-1, 1))
26
+ self.position.append(initial[i])
27
+
28
+ # Method to update velocity of a particle object
29
+ def update_velocity(self, global_best_position, max_iter, iter_count):
30
+ c1_start = 2.5
31
+ c1_end = 0.5
32
+ c2_start = 0.5
33
+ c2_end = 2.5
34
+ w = 0.7298
35
+
36
+ c1 = c1_start - (c1_start - c1_end) * (iter_count / max_iter)
37
+ c2 = c2_start + (c2_end - c2_start) * (iter_count / max_iter)
38
+
39
+ for i in range(0, self.num_dimensions):
40
+ r1 = random.random()
41
+ r2 = random.random()
42
+
43
+ cog_vel = c1 * r1 * (self.best_position[i] - self.position[i])
44
+ social_vel = c2 * r2 * (global_best_position[i] - self.position[i])
45
+ self.velocity[i] = w * self.velocity[i] + cog_vel + social_vel
46
+
47
+ # Method to update position of a particle object
48
+ def update_position(self, bounds):
49
+ for i in range(0, self.num_dimensions):
50
+ self.position[i] = self.position[i] + self.velocity[i]
51
+
52
+ if self.position[i] > bounds[i][1]:
53
+ self.position[i] = bounds[i][1]
54
+
55
+ if self.position[i] < bounds[i][0]:
56
+ self.position[i] = bounds[i][0]
57
+
58
+ # Method to evaluate fitness of a particle
59
+ def evaluate_fitness(self, number, target, function):
60
+ if number == 1:
61
+ self.error = fitness_function(self.position, target)
62
+ else:
63
+ self.error = cost_function(self.position, function)
64
+
65
+ if self.error < self.best_error:
66
+ self.best_position = self.position[:] # Create a copy of the position list
67
+ self.best_error = self.error
68
+
69
+ # Getter method to return the present error of a particle
70
+ def get_error(self):
71
+ return self.error
72
+
73
+ # Getter method to return the best position of a particle
74
+ def get_best_pos(self):
75
+ return self.best_position[:] # Return a copy of the best position list
76
+
77
+ # Getter method to return the best error of a particle
78
+ def get_best_error(self):
79
+ return self.best_error
80
+
81
+ # Getter method to return the best position of a particle
82
+ def get_pos(self):
83
+ return self.position[:] # Return a copy of the position list
84
+
85
+ # Getter method to return the velocity of a particle
86
+ def get_velocity(self):
87
+ return self.velocity[:] # Return a copy of the velocity list
88
+
89
+ # Function to calculate the euclidean distance from a particle to target
90
+ def fitness_function(particle_position, target):
91
+ x_pos, y_pos = float(target[0]), float(target[1])
92
+ return (x_pos - particle_position[0])**2 + (y_pos - particle_position[1])**2
93
+
94
+ # Function to calculate the value of the mathematical function at the position of a particle
95
+ import sympy as sp
96
+
97
+ def cost_function(particle_position, function_str):
98
+ x, y = sp.symbols('x y')
99
+ function = sp.sympify(function_str)
100
+ return function.subs({x: particle_position[0], y: particle_position[1]})
101
+
102
+ # Interactive Class: to create a swarm of particles and an interactive PSO
103
+ class Interactive_PSO():
104
+
105
+ # Method to initialize properties of an Interactive PSO
106
+ def __init__(self):
107
+ self._running = False
108
+ self.max_iter = 500 # Set the desired maximum number of iterations
109
+ self.num_particles = 25
110
+ self.initial = [5, 5]
111
+ self.bounds = [(-500, 500), (-500, 500)]
112
+ self.x_axis = []
113
+ self.y_axis = []
114
+ self.target = [5] * 2
115
+ self.global_best_error = float('inf') # Initialize global_best_error with infinity
116
+ self.update_particles_position_lists_with_random_values()
117
+ self.global_best_position = [0, 0]
118
+
119
+ # Method to initialize swarm to find the target in a given search space
120
+ # Method to initialize swarm to find the target in a given search space
121
+ def swarm_initialization(self, number, max_iter):
122
+ swarm = []
123
+ self.global_best_position = [0, 0]
124
+ self.global_best_error = float('inf') # Initialize global_best_error with infinity
125
+ self.gamma = 0.0001
126
+ function = function_select.value
127
+
128
+ for i in range(0, self.num_particles): # For loop to initialize the swarm of particles
129
+ swarm.append(Particle([self.x_axis[i], self.y_axis[i]]))
130
+
131
+ iter_count = 0
132
+ while self._running: # Loop to identify the best solution depending upon the problem
133
+ if self.global_best_error <= 0.00001:
134
+ break
135
+
136
+ for j in range(0, self.num_particles):
137
+ swarm[j].evaluate_fitness(number, self.target, function)
138
+
139
+ if swarm[j].get_error() < self.global_best_error:
140
+ self.global_best_position = swarm[j].get_best_pos()
141
+ self.global_best_error = swarm[j].get_best_error()
142
+
143
+ for j in range(0, self.num_particles):
144
+ swarm[j].update_velocity(self.global_best_position, max_iter, iter_count)
145
+ swarm[j].update_position(self.bounds)
146
+ self.x_axis[j] = swarm[j].get_pos()[0]
147
+ self.y_axis[j] = swarm[j].get_pos()[1]
148
+
149
+ # Add a delay to see the particle movement
150
+ time.sleep(0.05) # Adjust the delay as needed
151
+
152
+ iter_count += 1
153
+
154
+ # Update the table with the current global best position
155
+ update_table = True # <-- Set update_table to True
156
+ hv.streams.Stream.trigger(table_dmap.streams)
157
+
158
+ self.initial = self.global_best_position
159
+ self._running = False
160
+ print('Best Position:', self.global_best_position)
161
+ print('Best Error:', self.global_best_error)
162
+ print('Function:', function)
163
+
164
+
165
+ # Method to terminate finding the solution of a problem
166
+ def terminate(self):
167
+ self._running = False
168
+
169
+ # Method to set _running parameter before initializing the swarm
170
+ def starting(self):
171
+ self._running = True
172
+
173
+ # Method to check if the swarm of particles are in action
174
+ def isrunning(self):
175
+ return self._running
176
+
177
+ # Getter method to return the number of particles
178
+ def get_num_particles(self):
179
+ return self.num_particles
180
+
181
+ # Setter method to update the number of particles
182
+ def update_num_particles(self, new_value):
183
+ self.num_particles = new_value
184
+
185
+ # Getter method to return the x_axis position list for particles in a swarm
186
+ def get_xaxis(self):
187
+ return self.x_axis[:] # Return a copy
188
+
189
+ # Getter method to return the y_axis position list for particles in a swarm
190
+ def get_yaxis(self):
191
+ return self.y_axis[:] # Return a copy
192
+
193
+ # Setter method to update the target position
194
+ def set_target(self, x, y):
195
+ self.target = [x, y]
196
+
197
+ # Getter method to return the target position
198
+ def get_target(self):
199
+ return self.target[:] # Return a copy
200
+
201
+ # Method to update the length of particles position lists if there is a change in num of particles
202
+ def update_particles_position_lists(self, updated_num_particles):
203
+ old_x_value = self.x_axis[0]
204
+ old_y_value = self.y_axis[0]
205
+ if updated_num_particles > self.num_particles:
206
+ for i in range(self.num_particles, updated_num_particles):
207
+ self.x_axis.append(old_x_value)
208
+ self.y_axis.append(old_y_value)
209
+ else:
210
+ for i in range((self.num_particles) - 1, updated_num_particles - 1, -1):
211
+ self.x_axis.pop(i)
212
+ self.y_axis.pop(i)
213
+
214
+ # Method to initialize the particles positions randomly
215
+ def update_particles_position_lists_with_random_values(self):
216
+ self.x_axis = random.sample(range(-500, 500), self.num_particles)
217
+ self.y_axis = random.sample(range(-500, 500), self.num_particles)
218
+
219
+ pso_swarm = Interactive_PSO() # Creating an interactive PSO to find the target
220
+ pso_computation_swarm = Interactive_PSO() # Creating an interactive PSO to find the optimal solution of a mathematical function
221
+
222
+ update_table = False
223
+
224
+ # Method to initialize swarm to find the target in a given search space
225
+ def start_finding_the_target():
226
+ pso_swarm.swarm_initialization(1, pso_swarm.max_iter)
227
+
228
+ # Method to initialize swarm to compute an optimal solution for a given problem
229
+ def start_computation():
230
+ pso_computation_swarm.swarm_initialization(2, pso_computation_swarm.max_iter)
231
+
232
+ # On event function for single tap to create and return the target with updated position
233
+ def create_target_element(x, y):
234
+ pso_swarm.terminate()
235
+ if x is not None:
236
+ pso_swarm.set_target(x, y)
237
+ return hv.Points((x, y, 1), label='Target').opts(color='red', marker='^', size=10)
238
+
239
+ # Function to stream the particles of pso_swarm to dynamic map in regular intervals
240
+ def update():
241
+ x_axis = pso_swarm.get_xaxis()
242
+ y_axis = pso_swarm.get_yaxis()
243
+ data = (x_axis, y_axis, np.random.random(size=len(x_axis)))
244
+ pop_scatter = hv.Scatter(data, vdims=['y_axis', 'z'])
245
+ pop_scatter.opts(size=8, color='z', cmap='Coolwarm_r')
246
+ return pop_scatter
247
+
248
+ # On event function for update button click to update the number of particles in both the swarms
249
+ def computational_update():
250
+ x_axis = pso_computation_swarm.get_xaxis()
251
+ y_axis = pso_computation_swarm.get_yaxis()
252
+ data = (x_axis, y_axis, np.random.random(size=len(x_axis)))
253
+ pop_scatter1 = hv.Scatter(data, vdims=['y_axis', 'z'])
254
+ pop_scatter1.opts(size=8, color='z', cmap='Coolwarm_r')
255
+ return pop_scatter1
256
+
257
+ # On event function for update button click to update the number of particles in both the swarms
258
+ def update_num_particles_event(event):
259
+ if population_slider.value == pso_swarm.get_num_particles():
260
+ return
261
+ pso_swarm.terminate()
262
+ pso_computation_swarm.terminate()
263
+ time.sleep(1)
264
+ updated_num_particles = population_slider.value
265
+ pso_swarm.update_particles_position_lists(updated_num_particles)
266
+ pso_swarm.update_num_particles(updated_num_particles)
267
+ pso_computation_swarm.update_num_particles(updated_num_particles)
268
+ pso_computation_swarm.update_particles_position_lists_with_random_values()
269
+ pso_swarm.update_particles_position_lists_with_random_values() # Update positions for pso_swarm as well
270
+ hv.streams.Stream.trigger(pso_scatter1.streams)
271
+ hv.streams.Stream.trigger(pso_scatter.streams)
272
+
273
+ # Periodic Callback function for every 3 seconds to stream the data to dynamic maps
274
+ def trigger_streams():
275
+ global update_table
276
+ hv.streams.Stream.trigger(pso_scatter.streams)
277
+ hv.streams.Stream.trigger(pso_scatter1.streams)
278
+ if update_table:
279
+ update_table = False
280
+ hv.streams.Stream.trigger(table_dmap.streams)
281
+
282
+ # Update the target position
283
+ tap.event(x=pso_swarm.get_target()[0], y=pso_swarm.get_target()[1])
284
+
285
+ # Slow down the swarm's speed
286
+ time.sleep(0.05) # Adjust the delay as needed
287
+
288
+ # On event function for begin the hunting button click to start hunting for the target
289
+ def hunting_button_event(event):
290
+ if not pso_swarm.isrunning():
291
+ pso_swarm.starting()
292
+ threading.Thread(target=start_finding_the_target).start()
293
+
294
+ # On event function for start the computation button click to start computation for a mathematical function
295
+ def computation_button_event(event):
296
+ if not pso_computation_swarm.isrunning():
297
+ pso_computation_swarm.starting()
298
+ threading.Thread(target=start_computation).start()
299
+
300
+ def table():
301
+ position = pso_computation_swarm.global_best_position
302
+ df = pd.DataFrame({
303
+ 'x_position': [round(position[0])],
304
+ 'y_position': [round(position[1])]
305
+ })
306
+
307
+ # Create an hv.Table with the data
308
+ hv_table = hv.Table(df).opts(width=300, height=100)
309
+
310
+ return hv_table
311
+
312
+
313
+ # Function to update the mathematical function for which swarm finds the optimal solution
314
+ def update_function(event):
315
+ pso_computation_swarm.terminate()
316
+ time.sleep(1)
317
+ pso_computation_swarm.update_particles_position_lists_with_random_values()
318
+
319
+
320
+ # Two dynamic maps for two interactive PSOs, one for finding a target and one for computation of a mathematical function
321
+ pso_scatter = hv.DynamicMap(update, streams=[Stream.define('Next')()]).opts(xlim=(-500, 500), ylim=(-500, 500),
322
+ title="Plot 2 : PSO for target finding ")
323
+ pso_scatter1 = hv.DynamicMap(computational_update, streams=[Stream.define('Next')()]).opts(xlim=(-500, 500),
324
+ ylim=(-500, 500),
325
+ title="Plot 1 : PSO for a mathematical computation")
326
+
327
+ # Dynamic map to update and display target
328
+ tap = hv.streams.SingleTap(x=pso_swarm.get_target()[0], y=pso_swarm.get_target()[1])
329
+ target_dmap = hv.DynamicMap(create_target_element, streams=[tap])
330
+
331
+ # Define custom CSS styles for the table container
332
+ custom_style = {
333
+ 'background': '##4287f5', # Background color
334
+ 'border': '1px solid black', # Border around the table
335
+ 'padding': '8px', # Padding inside the container
336
+ 'box-shadow': '5px 5px 5px #bcbcbc' # Box shadow for a 3D effect
337
+ }
338
+
339
+ # Dynamic map to update the table with continuous global best position of the swarm
340
+ table_dmap = hv.DynamicMap(table,streams=[hv.streams.Stream.define('Next')()])
341
+ table_label = pn.pane.Markdown("Once an optimal solution is found in plot 1 it is updated in the below table")
342
+
343
+ # Button to order the swarm of particles to start finding the target
344
+ start_hunting_button = pn.widgets.Button(name=' Click to find target for plot 2 ', width=50)
345
+ start_hunting_button.on_click(hunting_button_event)
346
+
347
+ # Button to order the swarm of particles to start computation for selected mathematical function
348
+ start_finding_button = pn.widgets.Button(name=' Click to start computation for plot 1', width=50)
349
+ start_finding_button.on_click(computation_button_event)
350
+
351
+ # Button to update number of particles
352
+ update_num_particles_button = pn.widgets.Button(name='Update number of particles', width=50)
353
+ update_num_particles_button.on_click(update_num_particles_event)
354
+
355
+ # periodic callback for every three seconds to trigger streams method
356
+ pn.state.add_periodic_callback(trigger_streams, 3)
357
+
358
+ # Slider to change the number of particles
359
+ population_slider = pn.widgets.IntSlider(name='Number of praticles', start=10, end=100, value=25)
360
+
361
+ # Dropdown list to select a mathematical function
362
+ function_select = pn.widgets.Select(name='Select', options=['x^2+(y-100)^2','(x-234)^2+(y+100)^2', 'x^3 + y^3 - 3*x*y', 'x^2 * y^2'])
363
+ function_select.param.watch(update_function,'value')
364
+
365
+ #combining the dynamic maps with particles and target into one dynamicmap
366
+ plot_for_finding_the_target = pso_scatter*target_dmap
367
+
368
+ # Building the layout and returning the dashboard
369
+ dashboard = pn.Column(pn.Row(pn.Row(pso_scatter1.opts(width=500, height=500)), pn.Column(plot_for_finding_the_target.opts(width=500, height=500)),
370
+ pn.Column(pn.Column(table_label, table_dmap, styles=custom_style), start_finding_button, start_hunting_button, update_num_particles_button, population_slider,function_select)))
371
+
372
+ pn.panel(dashboard).servable(title='Swarm Particles Visualization')
requirements.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ panel
2
+ bokeh
3
+ numpy
4
+ holoviews
5
+ pandas
6
+ colormap
7
+ matplotlib
8
+ webcolors
9
+ thread6
10
+ sympy