Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
yangliu28
GitHub Repository: yangliu28/swarm_formation_sim
Path: blob/master/loop_reshape_1_static.py
104 views
1
# static version of the probabilistic approach for the loop reshape formation
2
# There are two sections of this program, first section generates two formations,
3
# one for initial setup formation, one for target formation; second section
4
# illustrates the dynamics of the preferability distribution of all nodes.
5
6
# command line arguments passing format:
7
# ex1: "gen_save initial_gen target_gen"
8
# ex1 will generate two formations, and save both to files.
9
# ex2: "gen_discard initial_gen target_read target_filename"
10
# ex2 will generate initial formation, and read target formation from file
11
# generated formation will be discarded
12
# ex3: "gen_save initial_read initial_filename target_gen"
13
# ex3 will read initial formation from file, and generate target formation
14
# generated target formation will be saved
15
16
# Random equilateral polygon generating method:
17
# Given all the side length of a n-side polygon, it can still varies in shape. The number of
18
# degree of freedom is (n-3). Equilateral polygon also has fixed side length, the way to
19
# generate such random polygon is to treat first (n-3) number of interior angles as DOFs.
20
# The rest of the polygon can be determined uniquely in either a convex or concave triangle.
21
# To make sure the polygon can be formed, the guesses for interior angles can not be too
22
# wild. Here a normal distribution is used to constrain the guesses within an appropriate
23
# range of the interior angle of corresponding regular polygon.
24
# Another check during the polygon generating is there should be no collapse or intersecting
25
# inside the polygon. That is, any non-neighbor nodes should not be closer than loop space.
26
27
# Comments on bar graph animation:
28
# Two animation methods are tried here, one is using matplotlib library, the other is using
29
# matlab engine to plot the graphs. Since animation results from both are not smooth enough,
30
# I'll try to bear with what I can get.
31
# The bar graph animation form matplotlib runs a little better in linux than in windows. I am
32
# already using the less effort way possible, only set the heights of the bars instead of
33
# redrawing the entire graph. The matplotlib.animation.FuncAnimation may work no better than
34
# my method right now.
35
# Comment and uncomment two chunks of code related to graphics in the following to choose
36
# whether matplotlib or matlab engine will be used.
37
38
# Comments on linear distribution summation method:
39
# The last step in the iteration is to combine the host node's distribution and two neighbors'
40
# distributions linearly, with a measure of unipolarity being the coefficients. The result of
41
# simulation is that distributions of all nodes will always converge to the same one. But the
42
# distribution they converge to often does not have a very good unipolarity, sometimes even far
43
# from the ideal one-pole-all-rest-zero distribution. The quality of final distribution is
44
# limited by the best in the starting distributions, because the linear distribution summation
45
# will compromise between all distribution, it will not intentionally seek better distributions.
46
47
# Comments on power method summation method:
48
# Power function with exponent higher than 1 will increase the unipolarity of a distribution.
49
# The higher the exponent, the faster the unipolarity increase. This aims to improve the quality
50
# of the final converged distribution, because it will intentionally optimized local
51
# distributions.
52
# Find the 'loop_reshape_test_power.py' to see how power function increase unipolarity.
53
# The problem with this method is that, the unipolarity increases so fast that often times best
54
# unipolarity will appear locally and fight each other, so the evolution won't converge.
55
56
57
import pygame
58
from formation_functions import *
59
import matplotlib.pyplot as plt
60
from matplotlib import gridspec
61
import matlab.engine
62
63
import sys, os, math, random
64
import numpy as np
65
66
# Read simulation options from passed arguments, the structure is:
67
# 1st argument decides whether to save all or none of generated formations.
68
# 'gen_save' will save all generated formations, 'gen_discard' will discard them
69
# 2nd argument decides whether initial formation is from random generation or file
70
# 'initial_gen' is for random generation, 'initial_read' is for read from file
71
# If 2nd argument is 'initial_read', 3rd argument will be the filename for the formation
72
# Next argument decides whether target formation is from random generation or file
73
# 'target_gen' is for random generation, 'target_read' is for read from file
74
# If previous argument is 'target_read', next argument will be the corresponding filename.
75
# All the formation data will be read from folder 'loop-data'.
76
# Any generated formation will be saved as a file under folder 'loop-data'.
77
save_opt = True # variable indicating if need to save generated formations
78
form_opts = [0,0] # variable for the results parsed from arguments
79
# first value for initial formation, second for target
80
# '0' for randomly generated
81
# '1' for read from file
82
form_files = [0,0] # filename for the formation if read from file
83
# following starts to read initial formation option
84
# start with argv[1], argv[0] is for the filename of this script when run in command line
85
save_option = sys.argv[1]
86
if save_option == 'gen_save':
87
save_opt = True
88
elif save_option == 'gen_discard':
89
save_opt = False
90
else:
91
# unregognized argument for saving formations
92
print 'arg "{}" for saving generated formations is invalid'.format(save_option)
93
initial_option = sys.argv[2]
94
if initial_option == 'initial_gen':
95
form_opts[0] = 0
96
elif initial_option == 'initial_read':
97
form_opts[0] = 1
98
# get the filename for the initial formation
99
form_files[0] = sys.argv[3]
100
else:
101
# unrecognized argument for initial formation
102
print 'arg "{}" for initial formation is invalid'.format(initial_option)
103
sys.exit()
104
# continue to read target formation option
105
target_option = 0
106
if form_opts[0] == 0:
107
target_option = sys.argv[3]
108
else:
109
target_option = sys.argv[4]
110
if target_option == 'target_gen':
111
form_opts[1] = 0
112
elif target_option == 'target_read':
113
form_opts[1] = 1
114
# get the filename for the target formation
115
if form_opts[0] == 0:
116
form_files[1] = sys.argv[4]
117
else:
118
form_files[1] = sys.argv[5]
119
else:
120
# unregocnized argument for target formation
121
print 'arg "{}" for target formation is invalid'.format(target_option)
122
sys.exit()
123
124
# The file structure for the loop formation data:
125
# First line is an integer for the number of sides of this polygon.
126
# From the second line, each line is an float number for an interior angle. The interior
127
# angles are arranged in ccw order along the loop. The reason of using interior angle
128
# instead of node position, is that it is independent of the side length.
129
# Not all interior angles are recorded, only the first (n-3) are. Since the polygon is
130
# equilateral, (n-3) of interior angles are enough to determine the shape.
131
# Filename is the time stamp when generating this file, there is no file extention.
132
133
134
########################### start of section 1 ###########################
135
136
# initialize the pygame
137
pygame.init()
138
139
# name of the folder under save directory that stores loop formation files
140
loop_folder = 'loop-data'
141
142
# parameters for display, window origin is at left up corner
143
screen_size = (600, 800) # width and height in pixels
144
# top half for initial formation, bottom half for target formation
145
background_color = (0,0,0) # black background
146
robot_color = (255,0,0) # red for robot and the line segments
147
robot_color_s = (255,153,153) # pink for the start robot
148
robot_size = 5 # robot modeled as dot, number of pixels for radius
149
150
# set up the simulation window and surface object
151
icon = pygame.image.load("icon_geometry_art.jpg")
152
pygame.display.set_icon(icon)
153
screen = pygame.display.set_mode(screen_size)
154
pygame.display.set_caption("Loop Reshape (static version)")
155
156
# for physics, continuous world, origin is at left bottom corner, starting (0, 0),
157
# with x axis pointing right, y axis pointing up.
158
# It's more natural to compute the physics in right hand coordiante system.
159
world_size = (100.0, 100.0 * screen_size[1]/screen_size[0])
160
161
# variables to configure the simulation
162
poly_n = 30 # number of nodes for the polygon, also the robot quantity, at least 3
163
loop_space = 4.0 # side length of the equilateral polygon
164
# the following are for the guessing of the free interior angles
165
int_angle_reg = math.pi - 2*math.pi/poly_n # interior angle of regular polygon
166
# standard deviation of the normal distribution of the guesses
167
int_angle_dev = (int_angle_reg - math.pi/3)/5
168
169
# instantiate the node positions variable
170
nodes = [[],[]] # node positions for two formation, index is the robot's identification
171
nodes[0].append([0, 0]) # first node starts at origin
172
nodes[0].append([loop_space, 0]) # second node is loop space away on the right
173
nodes[1].append([0, 0])
174
nodes[1].append([loop_space, 0])
175
for i in range(2, poly_n):
176
nodes[0].append([0, 0]) # filled with [0,0]
177
nodes[1].append([0, 0])
178
179
# construct the formation data for the two formation, either generating or from file
180
for i in range(2):
181
if form_opts[i] == 0: # option to generate a new formation
182
# process for generating the random equilateral polygon, two stages
183
poly_success = False # flag for succeed in generating the polygon
184
trial_count = 0 # record number of trials until a successful polygon is achieved
185
int_final = [] # interior angles to be saved later in file
186
while not poly_success:
187
trial_count = trial_count + 1
188
# print "trial {}: ".format(trial_count),
189
# continue trying until all the guesses can forming the desired polygon
190
# stage 1: guessing all the free interior angles
191
dof = poly_n-3 # number of free interior angles to be randomly generated
192
if dof > 0: # only continue guessing if at least one free interior angle
193
# generate all the guesses from a normal distribution
194
int_guesses = np.random.normal(int_angle_reg, int_angle_dev, dof).tolist()
195
ori_current = 0 # orientation of the line segment
196
no_irregular = True # flag indicating if the polygon is irregular or not
197
# example for irregular cases are intersecting of line segments
198
# or non neighbor nodes are closer than the loop space
199
# construct the polygon based on these guessed angles
200
for j in range(2, 2+dof): # for the position of j-th node
201
int_angle_t = int_guesses[j-2] # interior angle of previous node
202
ori_current = reset_radian(ori_current + (math.pi - int_angle_t))
203
nodes[i][j][0] = nodes[i][j-1][0] + loop_space*math.cos(ori_current)
204
nodes[i][j][1] = nodes[i][j-1][1] + loop_space*math.sin(ori_current)
205
# check the distance of node j to all previous nodes
206
# should not be closer than the loop space
207
for k in range(j-1): # exclude the previous neighbor
208
vect_temp = [nodes[i][k][0]-nodes[i][j][0],
209
nodes[i][k][1]-nodes[i][j][1]] # from j to k
210
dist_temp = math.sqrt(vect_temp[0]*vect_temp[0]+
211
vect_temp[1]*vect_temp[1])
212
if dist_temp < loop_space:
213
no_irregular = False
214
# print "node {} is too close to node {}".format(j, k)
215
break
216
if not no_irregular:
217
break
218
if not no_irregular:
219
continue # continue the while loop, keep trying new polygon
220
else: # if here, current interior angle guesses are good
221
int_final = int_guesses[:]
222
# although later check on the final node may still disqualify
223
# these guesses, the while loop will exit with a good int_final
224
# stage 2: use convex triangle for the rest, and deciding if polygon is possible
225
# solve the one last node
226
vect_temp = [nodes[i][0][0]-nodes[i][poly_n-2][0],
227
nodes[i][0][1]-nodes[i][poly_n-2][1]] # from n-2 to 0
228
dist_temp = math.sqrt(vect_temp[0]*vect_temp[0]+
229
vect_temp[1]*vect_temp[1])
230
# check distance between node n-2 and 0 to see if a convex triangle is possible
231
# the situation that whether node n-2 and 0 are too close has been excluded
232
if dist_temp > 2*loop_space:
233
# print("second last node is too far away from the starting node")
234
continue
235
else:
236
# calculate the position of the last node
237
midpoint = [(nodes[i][poly_n-2][0]+nodes[i][0][0])/2,
238
(nodes[i][poly_n-2][1]+nodes[i][0][1])/2]
239
perp_dist = math.sqrt(loop_space*loop_space - dist_temp*dist_temp/4)
240
perp_ori = math.atan2(vect_temp[1], vect_temp[0]) - math.pi/2
241
nodes[i][poly_n-1][0] = midpoint[0] + perp_dist*math.cos(perp_ori)
242
nodes[i][poly_n-1][1] = midpoint[1] + perp_dist*math.sin(perp_ori)
243
# also check any irregularity for the last node
244
no_irregular = True
245
for j in range(1, poly_n-2): # exclude starting node and second last node
246
vect_temp = [nodes[i][j][0]-nodes[i][poly_n-1][0],
247
nodes[i][j][1]-nodes[i][poly_n-1][1]] # from n-1 to j
248
dist_temp = math.sqrt(vect_temp[0]*vect_temp[0]+
249
vect_temp[1]*vect_temp[1])
250
if dist_temp < loop_space:
251
no_irregular = False
252
# print "last node is too close to node {}".format(j)
253
break
254
if no_irregular:
255
poly_success = True # reverse the flag
256
if i == 0: # for print message
257
print "initial formation generated at trial {}".format(trial_count)
258
else:
259
print "target formation generated at trial {}".format(trial_count)
260
# print("successful!")
261
# if here, a polygon has been successfully generated, save any new formation
262
if not save_opt: continue # skip following if option is not to save it
263
new_filename = get_date_time()
264
new_filepath = os.path.join(os.getcwd(), loop_folder, new_filename)
265
if os.path.isfile(new_filepath):
266
new_filename = new_filename + '-(1)' # add a suffix to avoid overwrite
267
new_filepath = new_filepath + '-(1)'
268
f = open(new_filepath, 'w')
269
f.write(str(poly_n) + '\n') # first line is the number of sides of the polygon
270
for j in int_final: # only recorded guessed interior angles
271
f.write(str(j) + '\n') # convert float to string
272
f.close()
273
# message for a file has been saved
274
if i == 0:
275
print('initial formation saved as "' + new_filename + '"')
276
else:
277
print('target formation saved as "' + new_filename + '"')
278
else: # option to read formation from file
279
new_filepath = os.path.join(os.getcwd(), loop_folder, form_files[i])
280
f = open(new_filepath, 'r') # read only
281
# check if the loop has the same number of side
282
if int(f.readline()) == poly_n:
283
# continue getting the interior angles
284
int_angles = []
285
new_line = f.readline()
286
while len(new_line) != 0: # not the end of the file yet
287
int_angles.append(float(new_line)) # add the new angle
288
new_line = f.readline()
289
# check if this file has the number of interior angles as it promised
290
if len(int_angles) != poly_n-3: # these many angles will determine the polygon
291
# the number of sides is not consistent inside the file
292
print 'file "{}" has incorrect number of interior angles'.format(form_files[i])
293
sys.exit()
294
# if here the data file is all fine, print message for this
295
if i == 0:
296
print 'initial formation read from file "{}"'.format(form_files[i])
297
else:
298
print 'target formation read from file "{}"'.format(form_files[i])
299
# construct the polygon from these interior angles
300
ori_current = 0 # orientation of current line segment
301
for j in range(2, poly_n-1):
302
int_angle_t = int_angles[j-2] # interior angle of previous node
303
ori_current = reset_radian(ori_current + (math.pi - int_angle_t))
304
nodes[i][j][0] = nodes[i][j-1][0] + loop_space*math.cos(ori_current)
305
nodes[i][j][1] = nodes[i][j-1][1] + loop_space*math.sin(ori_current)
306
# no need to check any irregularities
307
vect_temp = [nodes[i][0][0]-nodes[i][poly_n-2][0],
308
nodes[i][0][1]-nodes[i][poly_n-2][1]] # from node n-2 to 0
309
dist_temp = math.sqrt(vect_temp[0]*vect_temp[0]+
310
vect_temp[1]*vect_temp[1])
311
midpoint = [(nodes[i][poly_n-2][0]+nodes[i][0][0])/2,
312
(nodes[i][poly_n-2][1]+nodes[i][0][1])/2]
313
perp_dist = math.sqrt(loop_space*loop_space - dist_temp*dist_temp/4)
314
perp_ori = math.atan2(vect_temp[1], vect_temp[0]) - math.pi/2
315
nodes[i][poly_n-1][0] = midpoint[0] + perp_dist*math.cos(perp_ori)
316
nodes[i][poly_n-1][1] = midpoint[1] + perp_dist*math.sin(perp_ori)
317
else:
318
# the number of sides is not the same with poly_n specified here
319
print 'file "{}" has incorrect number of sides of polygon'.format(form_files[i])
320
sys.exit()
321
322
# shift the two polygon to the top and bottom halves
323
for i in range(2):
324
# calculate the geometry center of current polygon
325
geometry_center = [0, 0]
326
for j in range(poly_n):
327
geometry_center[0] = geometry_center[0] + nodes[i][j][0]
328
geometry_center[1] = geometry_center[1] + nodes[i][j][1]
329
geometry_center[0] = geometry_center[0]/poly_n
330
geometry_center[1] = geometry_center[1]/poly_n
331
# shift the polygon to the middle of the screen
332
for j in range(poly_n):
333
nodes[i][j][0] = nodes[i][j][0] - geometry_center[0] + world_size[0]/2
334
if i == 0: # initial formation shift to top half
335
nodes[i][j][1] = nodes[i][j][1] - geometry_center[1] + 3*world_size[1]/4
336
else: # target formation shift to bottom half
337
nodes[i][j][1] = nodes[i][j][1] - geometry_center[1] + world_size[1]/4
338
339
# draw the two polygons
340
screen.fill(background_color)
341
for i in range(2):
342
# draw the nodes and line segments
343
disp_pos = [[0,0] for j in range(poly_n)]
344
# pink color for the first robot
345
disp_pos[0] = world_to_display(nodes[i][0], world_size, screen_size)
346
pygame.draw.circle(screen, robot_color_s, disp_pos[0], robot_size, 0)
347
# red color for the rest robots and line segments
348
for j in range(1, poly_n):
349
disp_pos[j] = world_to_display(nodes[i][j], world_size, screen_size)
350
pygame.draw.circle(screen, robot_color, disp_pos[j], robot_size, 0)
351
for j in range(poly_n-1):
352
pygame.draw.line(screen, robot_color, disp_pos[j], disp_pos[j+1])
353
pygame.draw.line(screen, robot_color, disp_pos[poly_n-1], disp_pos[0])
354
pygame.display.update()
355
356
357
########################### start of section 2 ###########################
358
359
# calculate the interior angles of the two formations
360
# It's not necessary to do the calculation again, but may have this part ready
361
# for the dynamic version of the program for the loop reshape simulation.
362
inter_ang = [[0 for j in range(poly_n)] for i in range(2)]
363
for i in range(2):
364
for j in range(poly_n):
365
# for the interior angles of initial setup formation
366
node_m = nodes[i][j] # node in the moddle
367
node_l = nodes[i][(j-1)%poly_n] # node on the left
368
node_r = nodes[i][(j+1)%poly_n] # node on the right
369
vect_l = [node_l[0]-node_m[0], node_l[1]-node_m[1]] # from middle to left
370
vect_r = [node_r[0]-node_m[0], node_r[1]-node_m[1]] # from middle to right
371
# get the angle rotating from vect_r to vect_l
372
inter_ang[i][j] = math.acos((vect_l[0]*vect_r[0] + vect_l[1]*vect_r[1])/
373
(loop_space*loop_space))
374
if (vect_r[0]*vect_l[1] - vect_r[1]*vect_l[0]) < 0:
375
inter_ang[i][j] = 2*math.pi - inter_ang[i][j]
376
# the result interior angles should be in range of [0, 2*pi)
377
378
# rename the interior angle variables to be used in the second part
379
# use interior angle instead of deviation angle because they should be equivalent
380
inter_init = inter_ang[0][:] # interior angles of initial setup formation
381
inter_targ = inter_ang[1][:] # interior angles of target formation
382
# variable for the preferability distribution
383
pref_dist = np.zeros((poly_n, poly_n))
384
# modified standard deviation of each preferability distribution
385
# act as one way of measuring unipolarity of the distribution
386
std_dev = [0 for i in range(poly_n)]
387
# exponent of the power function in the preferability distribution evolution
388
exponent = 1.05
389
# largest probability in each distributions
390
# act as one way of measuring unipolarity of the distribution
391
larg_dist = [0 for i in range(poly_n)]
392
393
# calculate the preferability distribution of the initial formation
394
for i in range(poly_n):
395
# the angle difference of inter_init[i] to all angles in inter_targ
396
ang_diff = [0 for j in range(poly_n)]
397
ang_diff_sum = 0
398
for j in range(poly_n):
399
# angle difference represents the effort of change between two angles
400
# the more effort, the less the preferability, so take reciprocal
401
ang_diff[j] = 1/abs(inter_init[i]-inter_targ[j])
402
# summation of ang_diff
403
ang_diff_sum = ang_diff_sum + ang_diff[j]
404
# convert to preferability distribution
405
for j in range(poly_n):
406
# linearize all probabilities such that sum(pref_dist[i])=1
407
pref_dist[i][j] = ang_diff[j]/ang_diff_sum
408
409
### comment and uncomment following two chunks of code to choose graphics method
410
411
# 1.matplotlib method of bar graph animation (preferred)
412
# adjust figure and grid size here
413
fig = plt.figure(figsize=(18,12), tight_layout=True)
414
fig.canvas.set_window_title('Evolution of Preferability Distribution')
415
gs = gridspec.GridSpec(5, 6)
416
ax = [fig.add_subplot(gs[i]) for i in range(poly_n)]
417
rects = [] # bar chart subplot rectangle handler
418
x_pos = range(poly_n)
419
for i in range(poly_n):
420
rects.append(ax[i].bar(x_pos, pref_dist[i], align='center'))
421
ax[i].set_xlim(-1, poly_n) # y limit depends on data set
422
423
# # 2.matlab method of bar graph animation
424
# print("starting matlab engine ...")
425
# eng = matlab.engine.start_matlab()
426
# print("matlab engine is started")
427
# eng.figure('name', 'Evolution of Preferability Distribution', nargout=0)
428
# x_pos = eng.linspace(0.0, 29.0, 30.0)
429
430
# the loop
431
sim_exit = False # simulation exit flag
432
sim_pause = True # simulation pause flag
433
print_pause = False # print message for paused simulation
434
iter_count = 0
435
graph_iters = 10 # draw the distribution graphs every these many iterations
436
while not sim_exit:
437
# exit the program by close window button, or Esc or Q on keyboard
438
for event in pygame.event.get():
439
if event.type == pygame.QUIT:
440
sim_exit = True # exit with the close window button
441
if event.type == pygame.KEYUP:
442
if event.key == pygame.K_SPACE:
443
sim_pause = not sim_pause # reverse the pause flag
444
if sim_pause: print_pause = True # need to print pause msg once
445
if (event.key == pygame.K_ESCAPE) or (event.key == pygame.K_q):
446
sim_exit = True # exit with ESC key or Q key
447
448
# skip the rest of the loop if paused
449
if sim_pause:
450
if print_pause:
451
print('iteration paused')
452
print_pause = False
453
continue
454
455
# method 1 for measuring unipolarity, the modified standard deviation
456
for i in range(poly_n):
457
std_dev_t = [0 for j in range(poly_n)] # temporary standard deviation
458
# the j-th value is the modified standard deviation that takes
459
# j-th value in pref_dist[i] as the middle
460
for j in range(poly_n):
461
vari_sum = 0 # variable for the summation of the variance
462
for k in range(poly_n):
463
# get the closer index distance of k to j on the loop
464
index_dist = min((j-k)%poly_n, (k-j)%poly_n)
465
vari_sum = vari_sum + pref_dist[i][k]*(index_dist*index_dist)
466
std_dev_t[j] = math.sqrt(vari_sum)
467
# find the minimum in the std_dev_t, as node i's best possible deviation
468
std_dev_min = std_dev_t[0] # initialize with first one
469
for j in range(1, poly_n):
470
if std_dev_t[j] < std_dev_min:
471
std_dev_min = std_dev_t[j]
472
# the minimum standard deviation is the desired one
473
std_dev[i] = std_dev_min
474
475
# # method 2 of measuring unipolarity, simply the largest probability in the distribution
476
# for i in range(poly_n):
477
# larg_dist[i] = np.max(pref_dist[i])
478
479
# method 1 of preferability distribution evolution
480
# combine three distributions linearly, with unipolarity as coefficient
481
pref_dist_t = np.copy(pref_dist) # deep copy the 'pref_dist', intermediate variable
482
for i in range(poly_n):
483
i_l = (i-1)%poly_n # index of neighbor on the left
484
i_r = (i+1)%poly_n # index of neighbor on the right
485
# shifted distribution from left neighbor
486
dist_l = [pref_dist_t[i_l][-1]]
487
for j in range(poly_n-1):
488
dist_l.append(pref_dist_t[i_l][j])
489
# shifted distribution from right neighbor
490
dist_r = []
491
for j in range(1, poly_n):
492
dist_r.append(pref_dist_t[i_r][j])
493
dist_r.append(pref_dist_t[i_r][0])
494
# combine the three distributions
495
dist_sum = 0 # summation of the distribution
496
for j in range(poly_n):
497
# the smaller the standard deviation, the more it should stand out
498
# so use the reciprocal of the standard deviation
499
pref_dist[i][j] = (dist_l[j]/std_dev[i_l]+
500
pref_dist[i][j]/std_dev[i]+
501
dist_r[j]/std_dev[i_r])
502
dist_sum = dist_sum + pref_dist[i][j]
503
# linearize the distribution here
504
pref_dist[i] = pref_dist[i]/dist_sum
505
506
# # method 2 of preferability distribution evolution
507
# # combine distributions using power funciton, with coefficients
508
# pref_dist_t = np.copy(pref_dist)
509
# pref_dist_t = np.power(pref_dist_t, exponent)
510
# # for i in range(poly_n):
511
# # dist_sum = np.sum(pref_dist_t[i])
512
# # pref_dist_t[i] = pref_dist_t[i]/dist_sum
513
# for i in range(poly_n):
514
# i_l = (i-1)%poly_n # index of neighbor on the left
515
# i_r = (i+1)%poly_n # index of neighbor on the right
516
# # shifted distribution from left neighbor
517
# dist_l = [pref_dist_t[i_l][-1]]
518
# for j in range(poly_n-1):
519
# dist_l.append(pref_dist_t[i_l][j])
520
# # shifted distribution from right neighbor
521
# dist_r = []
522
# for j in range(1, poly_n):
523
# dist_r.append(pref_dist_t[i_r][j])
524
# dist_r.append(pref_dist_t[i_r][0])
525
# # combine three distributions using power method
526
# for j in range(poly_n):
527
# # the smaller the standard deviation, the more it should stand out
528
# # so use the reciprocal of the standard deviation
529
# pref_dist[i][j] = (larg_dist[i_l]*dist_l[j]+
530
# larg_dist[i]*pref_dist_t[i][j]+
531
# larg_dist[i_r]*dist_r[j])
532
# dist_sum = np.sum(pref_dist[i]) # summation of the distribution
533
# # linearize the distribution here
534
# pref_dist[i] = pref_dist[i]/dist_sum
535
536
# the graphics
537
print("current iteration count {}".format(iter_count))
538
if iter_count%graph_iters == 0:
539
# find the largest y data in all distributions as up limit in graphs
540
y_lim = 0.0
541
for i in range(poly_n):
542
for j in range(poly_n):
543
if pref_dist[i][j] > y_lim:
544
y_lim = pref_dist[i][j]
545
y_lim = min(1.0, y_lim*1.1) # leave some gap
546
547
### comment and uncomment following two chunks of code to choose graphics method
548
# be consistent with previous choice
549
550
# 1.matplotlib method (preferred)
551
for i in range(poly_n):
552
for j in range(poly_n):
553
rects[i][j].set_height(pref_dist[i][j])
554
ax[i].set_title('{} -> {:.4f}'.format(i, std_dev[i]))
555
ax[i].set_ylim(0.0, y_lim)
556
fig.canvas.draw()
557
fig.show()
558
559
# # 2.matlab method
560
# for i in range(poly_n):
561
# eng.subplot(5.0, 6.0, eng.double(i+1))
562
# eng.bar(x_pos, eng.cell2mat(pref_dist[i]))
563
# eng.xlim(eng.cell2mat([-1, poly_n]), nargout=0)
564
# eng.ylim(eng.cell2mat([0.0, y_lim]), nargout=0)
565
# eng.title("{} -> {:.4f}".format(i, std_dev[i]))
566
567
iter_count = iter_count + 1 # update iteration count
568
569
570
571
572