Path: blob/master/loop_reshape_1_static.py
104 views
# static version of the probabilistic approach for the loop reshape formation1# There are two sections of this program, first section generates two formations,2# one for initial setup formation, one for target formation; second section3# illustrates the dynamics of the preferability distribution of all nodes.45# command line arguments passing format:6# ex1: "gen_save initial_gen target_gen"7# ex1 will generate two formations, and save both to files.8# ex2: "gen_discard initial_gen target_read target_filename"9# ex2 will generate initial formation, and read target formation from file10# generated formation will be discarded11# ex3: "gen_save initial_read initial_filename target_gen"12# ex3 will read initial formation from file, and generate target formation13# generated target formation will be saved1415# Random equilateral polygon generating method:16# Given all the side length of a n-side polygon, it can still varies in shape. The number of17# degree of freedom is (n-3). Equilateral polygon also has fixed side length, the way to18# generate such random polygon is to treat first (n-3) number of interior angles as DOFs.19# The rest of the polygon can be determined uniquely in either a convex or concave triangle.20# To make sure the polygon can be formed, the guesses for interior angles can not be too21# wild. Here a normal distribution is used to constrain the guesses within an appropriate22# range of the interior angle of corresponding regular polygon.23# Another check during the polygon generating is there should be no collapse or intersecting24# inside the polygon. That is, any non-neighbor nodes should not be closer than loop space.2526# Comments on bar graph animation:27# Two animation methods are tried here, one is using matplotlib library, the other is using28# matlab engine to plot the graphs. Since animation results from both are not smooth enough,29# I'll try to bear with what I can get.30# The bar graph animation form matplotlib runs a little better in linux than in windows. I am31# already using the less effort way possible, only set the heights of the bars instead of32# redrawing the entire graph. The matplotlib.animation.FuncAnimation may work no better than33# my method right now.34# Comment and uncomment two chunks of code related to graphics in the following to choose35# whether matplotlib or matlab engine will be used.3637# Comments on linear distribution summation method:38# The last step in the iteration is to combine the host node's distribution and two neighbors'39# distributions linearly, with a measure of unipolarity being the coefficients. The result of40# simulation is that distributions of all nodes will always converge to the same one. But the41# distribution they converge to often does not have a very good unipolarity, sometimes even far42# from the ideal one-pole-all-rest-zero distribution. The quality of final distribution is43# limited by the best in the starting distributions, because the linear distribution summation44# will compromise between all distribution, it will not intentionally seek better distributions.4546# Comments on power method summation method:47# Power function with exponent higher than 1 will increase the unipolarity of a distribution.48# The higher the exponent, the faster the unipolarity increase. This aims to improve the quality49# of the final converged distribution, because it will intentionally optimized local50# distributions.51# Find the 'loop_reshape_test_power.py' to see how power function increase unipolarity.52# The problem with this method is that, the unipolarity increases so fast that often times best53# unipolarity will appear locally and fight each other, so the evolution won't converge.545556import pygame57from formation_functions import *58import matplotlib.pyplot as plt59from matplotlib import gridspec60import matlab.engine6162import sys, os, math, random63import numpy as np6465# Read simulation options from passed arguments, the structure is:66# 1st argument decides whether to save all or none of generated formations.67# 'gen_save' will save all generated formations, 'gen_discard' will discard them68# 2nd argument decides whether initial formation is from random generation or file69# 'initial_gen' is for random generation, 'initial_read' is for read from file70# If 2nd argument is 'initial_read', 3rd argument will be the filename for the formation71# Next argument decides whether target formation is from random generation or file72# 'target_gen' is for random generation, 'target_read' is for read from file73# If previous argument is 'target_read', next argument will be the corresponding filename.74# All the formation data will be read from folder 'loop-data'.75# Any generated formation will be saved as a file under folder 'loop-data'.76save_opt = True # variable indicating if need to save generated formations77form_opts = [0,0] # variable for the results parsed from arguments78# first value for initial formation, second for target79# '0' for randomly generated80# '1' for read from file81form_files = [0,0] # filename for the formation if read from file82# following starts to read initial formation option83# start with argv[1], argv[0] is for the filename of this script when run in command line84save_option = sys.argv[1]85if save_option == 'gen_save':86save_opt = True87elif save_option == 'gen_discard':88save_opt = False89else:90# unregognized argument for saving formations91print 'arg "{}" for saving generated formations is invalid'.format(save_option)92initial_option = sys.argv[2]93if initial_option == 'initial_gen':94form_opts[0] = 095elif initial_option == 'initial_read':96form_opts[0] = 197# get the filename for the initial formation98form_files[0] = sys.argv[3]99else:100# unrecognized argument for initial formation101print 'arg "{}" for initial formation is invalid'.format(initial_option)102sys.exit()103# continue to read target formation option104target_option = 0105if form_opts[0] == 0:106target_option = sys.argv[3]107else:108target_option = sys.argv[4]109if target_option == 'target_gen':110form_opts[1] = 0111elif target_option == 'target_read':112form_opts[1] = 1113# get the filename for the target formation114if form_opts[0] == 0:115form_files[1] = sys.argv[4]116else:117form_files[1] = sys.argv[5]118else:119# unregocnized argument for target formation120print 'arg "{}" for target formation is invalid'.format(target_option)121sys.exit()122123# The file structure for the loop formation data:124# First line is an integer for the number of sides of this polygon.125# From the second line, each line is an float number for an interior angle. The interior126# angles are arranged in ccw order along the loop. The reason of using interior angle127# instead of node position, is that it is independent of the side length.128# Not all interior angles are recorded, only the first (n-3) are. Since the polygon is129# equilateral, (n-3) of interior angles are enough to determine the shape.130# Filename is the time stamp when generating this file, there is no file extention.131132133########################### start of section 1 ###########################134135# initialize the pygame136pygame.init()137138# name of the folder under save directory that stores loop formation files139loop_folder = 'loop-data'140141# parameters for display, window origin is at left up corner142screen_size = (600, 800) # width and height in pixels143# top half for initial formation, bottom half for target formation144background_color = (0,0,0) # black background145robot_color = (255,0,0) # red for robot and the line segments146robot_color_s = (255,153,153) # pink for the start robot147robot_size = 5 # robot modeled as dot, number of pixels for radius148149# set up the simulation window and surface object150icon = pygame.image.load("icon_geometry_art.jpg")151pygame.display.set_icon(icon)152screen = pygame.display.set_mode(screen_size)153pygame.display.set_caption("Loop Reshape (static version)")154155# for physics, continuous world, origin is at left bottom corner, starting (0, 0),156# with x axis pointing right, y axis pointing up.157# It's more natural to compute the physics in right hand coordiante system.158world_size = (100.0, 100.0 * screen_size[1]/screen_size[0])159160# variables to configure the simulation161poly_n = 30 # number of nodes for the polygon, also the robot quantity, at least 3162loop_space = 4.0 # side length of the equilateral polygon163# the following are for the guessing of the free interior angles164int_angle_reg = math.pi - 2*math.pi/poly_n # interior angle of regular polygon165# standard deviation of the normal distribution of the guesses166int_angle_dev = (int_angle_reg - math.pi/3)/5167168# instantiate the node positions variable169nodes = [[],[]] # node positions for two formation, index is the robot's identification170nodes[0].append([0, 0]) # first node starts at origin171nodes[0].append([loop_space, 0]) # second node is loop space away on the right172nodes[1].append([0, 0])173nodes[1].append([loop_space, 0])174for i in range(2, poly_n):175nodes[0].append([0, 0]) # filled with [0,0]176nodes[1].append([0, 0])177178# construct the formation data for the two formation, either generating or from file179for i in range(2):180if form_opts[i] == 0: # option to generate a new formation181# process for generating the random equilateral polygon, two stages182poly_success = False # flag for succeed in generating the polygon183trial_count = 0 # record number of trials until a successful polygon is achieved184int_final = [] # interior angles to be saved later in file185while not poly_success:186trial_count = trial_count + 1187# print "trial {}: ".format(trial_count),188# continue trying until all the guesses can forming the desired polygon189# stage 1: guessing all the free interior angles190dof = poly_n-3 # number of free interior angles to be randomly generated191if dof > 0: # only continue guessing if at least one free interior angle192# generate all the guesses from a normal distribution193int_guesses = np.random.normal(int_angle_reg, int_angle_dev, dof).tolist()194ori_current = 0 # orientation of the line segment195no_irregular = True # flag indicating if the polygon is irregular or not196# example for irregular cases are intersecting of line segments197# or non neighbor nodes are closer than the loop space198# construct the polygon based on these guessed angles199for j in range(2, 2+dof): # for the position of j-th node200int_angle_t = int_guesses[j-2] # interior angle of previous node201ori_current = reset_radian(ori_current + (math.pi - int_angle_t))202nodes[i][j][0] = nodes[i][j-1][0] + loop_space*math.cos(ori_current)203nodes[i][j][1] = nodes[i][j-1][1] + loop_space*math.sin(ori_current)204# check the distance of node j to all previous nodes205# should not be closer than the loop space206for k in range(j-1): # exclude the previous neighbor207vect_temp = [nodes[i][k][0]-nodes[i][j][0],208nodes[i][k][1]-nodes[i][j][1]] # from j to k209dist_temp = math.sqrt(vect_temp[0]*vect_temp[0]+210vect_temp[1]*vect_temp[1])211if dist_temp < loop_space:212no_irregular = False213# print "node {} is too close to node {}".format(j, k)214break215if not no_irregular:216break217if not no_irregular:218continue # continue the while loop, keep trying new polygon219else: # if here, current interior angle guesses are good220int_final = int_guesses[:]221# although later check on the final node may still disqualify222# these guesses, the while loop will exit with a good int_final223# stage 2: use convex triangle for the rest, and deciding if polygon is possible224# solve the one last node225vect_temp = [nodes[i][0][0]-nodes[i][poly_n-2][0],226nodes[i][0][1]-nodes[i][poly_n-2][1]] # from n-2 to 0227dist_temp = math.sqrt(vect_temp[0]*vect_temp[0]+228vect_temp[1]*vect_temp[1])229# check distance between node n-2 and 0 to see if a convex triangle is possible230# the situation that whether node n-2 and 0 are too close has been excluded231if dist_temp > 2*loop_space:232# print("second last node is too far away from the starting node")233continue234else:235# calculate the position of the last node236midpoint = [(nodes[i][poly_n-2][0]+nodes[i][0][0])/2,237(nodes[i][poly_n-2][1]+nodes[i][0][1])/2]238perp_dist = math.sqrt(loop_space*loop_space - dist_temp*dist_temp/4)239perp_ori = math.atan2(vect_temp[1], vect_temp[0]) - math.pi/2240nodes[i][poly_n-1][0] = midpoint[0] + perp_dist*math.cos(perp_ori)241nodes[i][poly_n-1][1] = midpoint[1] + perp_dist*math.sin(perp_ori)242# also check any irregularity for the last node243no_irregular = True244for j in range(1, poly_n-2): # exclude starting node and second last node245vect_temp = [nodes[i][j][0]-nodes[i][poly_n-1][0],246nodes[i][j][1]-nodes[i][poly_n-1][1]] # from n-1 to j247dist_temp = math.sqrt(vect_temp[0]*vect_temp[0]+248vect_temp[1]*vect_temp[1])249if dist_temp < loop_space:250no_irregular = False251# print "last node is too close to node {}".format(j)252break253if no_irregular:254poly_success = True # reverse the flag255if i == 0: # for print message256print "initial formation generated at trial {}".format(trial_count)257else:258print "target formation generated at trial {}".format(trial_count)259# print("successful!")260# if here, a polygon has been successfully generated, save any new formation261if not save_opt: continue # skip following if option is not to save it262new_filename = get_date_time()263new_filepath = os.path.join(os.getcwd(), loop_folder, new_filename)264if os.path.isfile(new_filepath):265new_filename = new_filename + '-(1)' # add a suffix to avoid overwrite266new_filepath = new_filepath + '-(1)'267f = open(new_filepath, 'w')268f.write(str(poly_n) + '\n') # first line is the number of sides of the polygon269for j in int_final: # only recorded guessed interior angles270f.write(str(j) + '\n') # convert float to string271f.close()272# message for a file has been saved273if i == 0:274print('initial formation saved as "' + new_filename + '"')275else:276print('target formation saved as "' + new_filename + '"')277else: # option to read formation from file278new_filepath = os.path.join(os.getcwd(), loop_folder, form_files[i])279f = open(new_filepath, 'r') # read only280# check if the loop has the same number of side281if int(f.readline()) == poly_n:282# continue getting the interior angles283int_angles = []284new_line = f.readline()285while len(new_line) != 0: # not the end of the file yet286int_angles.append(float(new_line)) # add the new angle287new_line = f.readline()288# check if this file has the number of interior angles as it promised289if len(int_angles) != poly_n-3: # these many angles will determine the polygon290# the number of sides is not consistent inside the file291print 'file "{}" has incorrect number of interior angles'.format(form_files[i])292sys.exit()293# if here the data file is all fine, print message for this294if i == 0:295print 'initial formation read from file "{}"'.format(form_files[i])296else:297print 'target formation read from file "{}"'.format(form_files[i])298# construct the polygon from these interior angles299ori_current = 0 # orientation of current line segment300for j in range(2, poly_n-1):301int_angle_t = int_angles[j-2] # interior angle of previous node302ori_current = reset_radian(ori_current + (math.pi - int_angle_t))303nodes[i][j][0] = nodes[i][j-1][0] + loop_space*math.cos(ori_current)304nodes[i][j][1] = nodes[i][j-1][1] + loop_space*math.sin(ori_current)305# no need to check any irregularities306vect_temp = [nodes[i][0][0]-nodes[i][poly_n-2][0],307nodes[i][0][1]-nodes[i][poly_n-2][1]] # from node n-2 to 0308dist_temp = math.sqrt(vect_temp[0]*vect_temp[0]+309vect_temp[1]*vect_temp[1])310midpoint = [(nodes[i][poly_n-2][0]+nodes[i][0][0])/2,311(nodes[i][poly_n-2][1]+nodes[i][0][1])/2]312perp_dist = math.sqrt(loop_space*loop_space - dist_temp*dist_temp/4)313perp_ori = math.atan2(vect_temp[1], vect_temp[0]) - math.pi/2314nodes[i][poly_n-1][0] = midpoint[0] + perp_dist*math.cos(perp_ori)315nodes[i][poly_n-1][1] = midpoint[1] + perp_dist*math.sin(perp_ori)316else:317# the number of sides is not the same with poly_n specified here318print 'file "{}" has incorrect number of sides of polygon'.format(form_files[i])319sys.exit()320321# shift the two polygon to the top and bottom halves322for i in range(2):323# calculate the geometry center of current polygon324geometry_center = [0, 0]325for j in range(poly_n):326geometry_center[0] = geometry_center[0] + nodes[i][j][0]327geometry_center[1] = geometry_center[1] + nodes[i][j][1]328geometry_center[0] = geometry_center[0]/poly_n329geometry_center[1] = geometry_center[1]/poly_n330# shift the polygon to the middle of the screen331for j in range(poly_n):332nodes[i][j][0] = nodes[i][j][0] - geometry_center[0] + world_size[0]/2333if i == 0: # initial formation shift to top half334nodes[i][j][1] = nodes[i][j][1] - geometry_center[1] + 3*world_size[1]/4335else: # target formation shift to bottom half336nodes[i][j][1] = nodes[i][j][1] - geometry_center[1] + world_size[1]/4337338# draw the two polygons339screen.fill(background_color)340for i in range(2):341# draw the nodes and line segments342disp_pos = [[0,0] for j in range(poly_n)]343# pink color for the first robot344disp_pos[0] = world_to_display(nodes[i][0], world_size, screen_size)345pygame.draw.circle(screen, robot_color_s, disp_pos[0], robot_size, 0)346# red color for the rest robots and line segments347for j in range(1, poly_n):348disp_pos[j] = world_to_display(nodes[i][j], world_size, screen_size)349pygame.draw.circle(screen, robot_color, disp_pos[j], robot_size, 0)350for j in range(poly_n-1):351pygame.draw.line(screen, robot_color, disp_pos[j], disp_pos[j+1])352pygame.draw.line(screen, robot_color, disp_pos[poly_n-1], disp_pos[0])353pygame.display.update()354355356########################### start of section 2 ###########################357358# calculate the interior angles of the two formations359# It's not necessary to do the calculation again, but may have this part ready360# for the dynamic version of the program for the loop reshape simulation.361inter_ang = [[0 for j in range(poly_n)] for i in range(2)]362for i in range(2):363for j in range(poly_n):364# for the interior angles of initial setup formation365node_m = nodes[i][j] # node in the moddle366node_l = nodes[i][(j-1)%poly_n] # node on the left367node_r = nodes[i][(j+1)%poly_n] # node on the right368vect_l = [node_l[0]-node_m[0], node_l[1]-node_m[1]] # from middle to left369vect_r = [node_r[0]-node_m[0], node_r[1]-node_m[1]] # from middle to right370# get the angle rotating from vect_r to vect_l371inter_ang[i][j] = math.acos((vect_l[0]*vect_r[0] + vect_l[1]*vect_r[1])/372(loop_space*loop_space))373if (vect_r[0]*vect_l[1] - vect_r[1]*vect_l[0]) < 0:374inter_ang[i][j] = 2*math.pi - inter_ang[i][j]375# the result interior angles should be in range of [0, 2*pi)376377# rename the interior angle variables to be used in the second part378# use interior angle instead of deviation angle because they should be equivalent379inter_init = inter_ang[0][:] # interior angles of initial setup formation380inter_targ = inter_ang[1][:] # interior angles of target formation381# variable for the preferability distribution382pref_dist = np.zeros((poly_n, poly_n))383# modified standard deviation of each preferability distribution384# act as one way of measuring unipolarity of the distribution385std_dev = [0 for i in range(poly_n)]386# exponent of the power function in the preferability distribution evolution387exponent = 1.05388# largest probability in each distributions389# act as one way of measuring unipolarity of the distribution390larg_dist = [0 for i in range(poly_n)]391392# calculate the preferability distribution of the initial formation393for i in range(poly_n):394# the angle difference of inter_init[i] to all angles in inter_targ395ang_diff = [0 for j in range(poly_n)]396ang_diff_sum = 0397for j in range(poly_n):398# angle difference represents the effort of change between two angles399# the more effort, the less the preferability, so take reciprocal400ang_diff[j] = 1/abs(inter_init[i]-inter_targ[j])401# summation of ang_diff402ang_diff_sum = ang_diff_sum + ang_diff[j]403# convert to preferability distribution404for j in range(poly_n):405# linearize all probabilities such that sum(pref_dist[i])=1406pref_dist[i][j] = ang_diff[j]/ang_diff_sum407408### comment and uncomment following two chunks of code to choose graphics method409410# 1.matplotlib method of bar graph animation (preferred)411# adjust figure and grid size here412fig = plt.figure(figsize=(18,12), tight_layout=True)413fig.canvas.set_window_title('Evolution of Preferability Distribution')414gs = gridspec.GridSpec(5, 6)415ax = [fig.add_subplot(gs[i]) for i in range(poly_n)]416rects = [] # bar chart subplot rectangle handler417x_pos = range(poly_n)418for i in range(poly_n):419rects.append(ax[i].bar(x_pos, pref_dist[i], align='center'))420ax[i].set_xlim(-1, poly_n) # y limit depends on data set421422# # 2.matlab method of bar graph animation423# print("starting matlab engine ...")424# eng = matlab.engine.start_matlab()425# print("matlab engine is started")426# eng.figure('name', 'Evolution of Preferability Distribution', nargout=0)427# x_pos = eng.linspace(0.0, 29.0, 30.0)428429# the loop430sim_exit = False # simulation exit flag431sim_pause = True # simulation pause flag432print_pause = False # print message for paused simulation433iter_count = 0434graph_iters = 10 # draw the distribution graphs every these many iterations435while not sim_exit:436# exit the program by close window button, or Esc or Q on keyboard437for event in pygame.event.get():438if event.type == pygame.QUIT:439sim_exit = True # exit with the close window button440if event.type == pygame.KEYUP:441if event.key == pygame.K_SPACE:442sim_pause = not sim_pause # reverse the pause flag443if sim_pause: print_pause = True # need to print pause msg once444if (event.key == pygame.K_ESCAPE) or (event.key == pygame.K_q):445sim_exit = True # exit with ESC key or Q key446447# skip the rest of the loop if paused448if sim_pause:449if print_pause:450print('iteration paused')451print_pause = False452continue453454# method 1 for measuring unipolarity, the modified standard deviation455for i in range(poly_n):456std_dev_t = [0 for j in range(poly_n)] # temporary standard deviation457# the j-th value is the modified standard deviation that takes458# j-th value in pref_dist[i] as the middle459for j in range(poly_n):460vari_sum = 0 # variable for the summation of the variance461for k in range(poly_n):462# get the closer index distance of k to j on the loop463index_dist = min((j-k)%poly_n, (k-j)%poly_n)464vari_sum = vari_sum + pref_dist[i][k]*(index_dist*index_dist)465std_dev_t[j] = math.sqrt(vari_sum)466# find the minimum in the std_dev_t, as node i's best possible deviation467std_dev_min = std_dev_t[0] # initialize with first one468for j in range(1, poly_n):469if std_dev_t[j] < std_dev_min:470std_dev_min = std_dev_t[j]471# the minimum standard deviation is the desired one472std_dev[i] = std_dev_min473474# # method 2 of measuring unipolarity, simply the largest probability in the distribution475# for i in range(poly_n):476# larg_dist[i] = np.max(pref_dist[i])477478# method 1 of preferability distribution evolution479# combine three distributions linearly, with unipolarity as coefficient480pref_dist_t = np.copy(pref_dist) # deep copy the 'pref_dist', intermediate variable481for i in range(poly_n):482i_l = (i-1)%poly_n # index of neighbor on the left483i_r = (i+1)%poly_n # index of neighbor on the right484# shifted distribution from left neighbor485dist_l = [pref_dist_t[i_l][-1]]486for j in range(poly_n-1):487dist_l.append(pref_dist_t[i_l][j])488# shifted distribution from right neighbor489dist_r = []490for j in range(1, poly_n):491dist_r.append(pref_dist_t[i_r][j])492dist_r.append(pref_dist_t[i_r][0])493# combine the three distributions494dist_sum = 0 # summation of the distribution495for j in range(poly_n):496# the smaller the standard deviation, the more it should stand out497# so use the reciprocal of the standard deviation498pref_dist[i][j] = (dist_l[j]/std_dev[i_l]+499pref_dist[i][j]/std_dev[i]+500dist_r[j]/std_dev[i_r])501dist_sum = dist_sum + pref_dist[i][j]502# linearize the distribution here503pref_dist[i] = pref_dist[i]/dist_sum504505# # method 2 of preferability distribution evolution506# # combine distributions using power funciton, with coefficients507# pref_dist_t = np.copy(pref_dist)508# pref_dist_t = np.power(pref_dist_t, exponent)509# # for i in range(poly_n):510# # dist_sum = np.sum(pref_dist_t[i])511# # pref_dist_t[i] = pref_dist_t[i]/dist_sum512# for i in range(poly_n):513# i_l = (i-1)%poly_n # index of neighbor on the left514# i_r = (i+1)%poly_n # index of neighbor on the right515# # shifted distribution from left neighbor516# dist_l = [pref_dist_t[i_l][-1]]517# for j in range(poly_n-1):518# dist_l.append(pref_dist_t[i_l][j])519# # shifted distribution from right neighbor520# dist_r = []521# for j in range(1, poly_n):522# dist_r.append(pref_dist_t[i_r][j])523# dist_r.append(pref_dist_t[i_r][0])524# # combine three distributions using power method525# for j in range(poly_n):526# # the smaller the standard deviation, the more it should stand out527# # so use the reciprocal of the standard deviation528# pref_dist[i][j] = (larg_dist[i_l]*dist_l[j]+529# larg_dist[i]*pref_dist_t[i][j]+530# larg_dist[i_r]*dist_r[j])531# dist_sum = np.sum(pref_dist[i]) # summation of the distribution532# # linearize the distribution here533# pref_dist[i] = pref_dist[i]/dist_sum534535# the graphics536print("current iteration count {}".format(iter_count))537if iter_count%graph_iters == 0:538# find the largest y data in all distributions as up limit in graphs539y_lim = 0.0540for i in range(poly_n):541for j in range(poly_n):542if pref_dist[i][j] > y_lim:543y_lim = pref_dist[i][j]544y_lim = min(1.0, y_lim*1.1) # leave some gap545546### comment and uncomment following two chunks of code to choose graphics method547# be consistent with previous choice548549# 1.matplotlib method (preferred)550for i in range(poly_n):551for j in range(poly_n):552rects[i][j].set_height(pref_dist[i][j])553ax[i].set_title('{} -> {:.4f}'.format(i, std_dev[i]))554ax[i].set_ylim(0.0, y_lim)555fig.canvas.draw()556fig.show()557558# # 2.matlab method559# for i in range(poly_n):560# eng.subplot(5.0, 6.0, eng.double(i+1))561# eng.bar(x_pos, eng.cell2mat(pref_dist[i]))562# eng.xlim(eng.cell2mat([-1, poly_n]), nargout=0)563# eng.ylim(eng.cell2mat([0.0, y_lim]), nargout=0)564# eng.title("{} -> {:.4f}".format(i, std_dev[i]))565566iter_count = iter_count + 1 # update iteration count567568569570571572