def find_equilibria(ssd, eps=0): """ Finds the polytope that contains the equilibrium points @param ssd: The dynamics of the switched system @type ssd: L{SwitchedSysDyn} @param eps: The value by which the width of all polytopes containing equilibrium points is increased. @type eps: float @return param cont_props: The polytope representations of the atomic propositions of the state space to be used in partitiong @type cont_props: dict of polytope.Polytope Warning: Currently, there is no condition for points where the polytope is critically stable. Warning: if there is a region outside the domain, then it is unstable. It seems to ignore the regions outside the domain. """ def normalize(A, B): """ Normalizes set of equations of the form Ax<=B """ if A.size > 0: Anorm = np.sqrt(np.sum(A * A, 1)).flatten() pos = np.nonzero(Anorm > 1e-10)[0] A = A[pos, :] B = B[pos] Anorm = Anorm[pos] mult = 1 / Anorm for i in xrange(A.shape[0]): A[i, :] = A[i, :] * mult[i] B = B.flatten() * mult return A, B cont_ss = ssd.cts_ss min_outx = cont_ss.b[0] min_outy = cont_ss.b[1] max_outx = min_outx + 1 max_outy = min_outy + 1 abs_tol = 1e-7 cont_props = dict() for mode in ssd.modes: cont_dyn = ssd.dynamics[mode].list_subsys[0] A = cont_dyn.A K = cont_dyn.K.T[0] I = np.eye(len(A), dtype=float) rank_IA = np.linalg.matrix_rank(I - A) concat = np.hstack((I - A, K.reshape(len(A), 1))) rank_concat = np.linalg.matrix_rank(concat) soln = pc.Polytope() props_sym = "eqpnt_" + str(mode[1]) if rank_IA == rank_concat: if rank_IA == len(A): equil = np.dot(np.linalg.inv(I - A), K) if ( equil[0] >= (-cont_ss.b[2]) and equil[0] <= cont_ss.b[0] and equil[1] >= (-cont_ss.b[3]) and equil[1] <= cont_ss.b[1] ): delta = eps + equil / 100 soln = pc.box2poly( [[equil[0] - delta[0], equil[0] + delta[0]], [equil[1] - delta[1], equil[1] + delta[1]]] ) else: soln = pc.box2poly([[min_outx, max_outx], [min_outy, max_outy]]) elif rank_IA < len(A): if eps == 0: eps = abs(min(np.amin(-K), np.amin(A - I))) IAn, Kn = normalize(I - A, K) soln = pc.Polytope(np.vstack((IAn, -IAn)), np.hstack((Kn + eps, -Kn + eps))) relevantsoln = pc.intersect(soln, cont_ss, abs_tol) if pc.is_empty(relevantsoln) & ~pc.is_empty(soln): soln = pc.box2poly([[min_outx, max_outx], [min_outy, max_outy]]) else: soln = relevantsoln else: # Assuming trajectories go to infinity as there are no # equilibrium points soln = pc.box2poly([[min_outx, max_outx], [min_outy, max_outy]]) print str(mode) + " trajectories go to infinity! No solution" cont_props[props_sym] = soln return cont_props
def infer_srtesseler_density(cells, volume_weighted=True, **kwargs): """ adapted from: SR-Tesseler: a method to segment and quantify localization-based super-resolution microscopy data. Levet, F., Hosy, E., Kechkar, A., Butler, C., Beghin, A., Choquet, C., Sibarita, J.B. Nature Methods 2015; 12 (11); 1065-1071. """ points = cells.locations is_densest_tessellation = isinstance(cells.tessellation, Voronoi) and cells.number_of_cells == len(points) if is_densest_tessellation: tessellation = cells.tessellation partition = cells else: tessellation = Voronoi() tessellation.tessellate(points[['x','y']]) partition = Partition(points, tessellation) #areas = tessellation.cell_volume # the default implementation for cell_volume # generates a non-infinite estimation for opened cells areas = np.full(tessellation.number_of_cells, np.nan) indices, densities, unit_polygons = [], [], [] for i, surface_area in enumerate(areas): unknown_area = np.isnan(surface_area) or np.isinf(surface_area) or surface_area == 0 polygons_required = not is_densest_tessellation and volume_weighted if unknown_area or polygons_required: hull = convex_hull(partition=partition, cell_index=i) if unknown_area: if hull is None: continue density = 1./ hull.volume # should also divide by the number of frames else: density = 1./ surface_area indices.append(i) densities.append(density) if polygons_required: unit_polygons.append(p.Polytope(hull.equations[:,[0,1]], -hull.equations[:,2])) unit_density = pd.Series(index=indices, data=densities) if is_densest_tessellation: density = unit_density else: indices, densities = [], [] for i in range(cells.number_of_cells): assigned = cells.cell_index == i if not np.any(assigned): indices.append(i) densities.append(0.) continue local_polygons, = np.nonzero(assigned) assert 0 < local_polygons.size if volume_weighted: hull = convex_hull(partition=cells, cell_index=i, point_assigned=assigned) if hull is None: continue polygon = p.Polytope(hull.equations[:,[0,1]], -hull.equations[:,2]) weighted_sum = 0. total_area = 0. for j in local_polygons: intersection = p.intersect(unit_polygons[j], polygon) total_area += intersection.volume weighted_sum += intersection.volume * unit_density[j] average_density = weighted_sum / total_area else: average_density = unit_density[local_polygons].mean() indices.append(i) densities.append(average_density) density = pd.Series(index=indices, data=densities) return pd.DataFrame(dict(density=density))
def compute_calP_in_probs(self, c, d, calP, t, cp1, cp2, actions_taken): A_upper = np.array([ [-1.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, -1.0], [0.0, 0.0, 1.0], #surrounding box [-cp1[0], -cp1[1], -cp1[2]] ]) b_upper = np.array( [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -c * self.agents_lst[t].delta]) p_upper = pc.Polytope(A_upper, b_upper) #large upper polytope A_lower = np.array([ [-1.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, -1.0], [0.0, 0.0, 1.0], #surrounding box [cp2[0], cp2[1], cp2[2]] ]) b_lower = np.array( [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -c * self.agents_lst[t].delta]) p_lower = pc.Polytope(A_lower, b_lower) #large lower polytope calP_u = [] calP_l = [] calP_m = [] print("actual number of polytopes:") print(len(calP)) for pol in calP: p = pol.pol_repr if p.volume > 0.01: # intersections of the polytope with upper and lower of curr round intersect_up = pc.intersect(p, p_upper) intersect_lo = pc.intersect(p, p_lower) # check whether the intersection is empty-ish (very small vol) emptyish_up = self.is_empty(intersect_up) or self.has_tiny_vol( intersect_up) emptyish_lo = self.is_empty(intersect_lo) or self.has_tiny_vol( intersect_lo) if (emptyish_up and emptyish_lo): calP_m.append(pol) elif (emptyish_up): diff_lo = p.diff( p_lower ) # what is left in the diff should go in the middle space unless emptyish if (self.is_empty(diff_lo) or self.has_tiny_vol(diff_lo) ): # intersection is exactly the polytope # if emptyish, no need to create new polytope pol.updated[t] = 1 calP_l.append(pol) else: # otherwise we need to create new polytope upd_lst = pol.updated #store the history of updates for curr pol upd_lst[ t] = 0 # by default assume that you won't update it ratio = 1.0 * diff_lo.volume / p.volume # how big is the prob that you keep for new pol g_pol1 = Grind_Polytope(diff_lo, ratio * pol.pi, ratio * pol.weight, d, self.T, pol.est_loss, pol.loss, upd_lst) calP_m.append(g_pol1) upd_lst2 = pol.updated upd_lst2[ t] = 1 # this part of the pol will be part of the calP_lower g_pol2 = Grind_Polytope(intersect_lo, (1.0 - ratio) * pol.pi, (1.0 - ratio) * pol.weight, d, self.T, pol.est_loss, pol.loss, upd_lst2) calP_l.append(g_pol2) elif (emptyish_lo): diff_up = p.diff(p_upper) if (self.is_empty(diff_up) or self.has_tiny_vol(diff_up)): pol.updated[t] = 1 calP_u.append(pol) else: upd_lst = pol.updated upd_lst[t] = 0 ratio = 1.0 * diff_up.volume / p.volume g_pol1 = Grind_Polytope(diff_up, ratio * pol.pi, ratio * pol.weight, d, self.T, pol.est_loss, pol.loss, upd_lst) calP_m.append(g_pol1) upd_lst2 = pol.updated upd_lst2[t] = 1 g_pol2 = Grind_Polytope(intersect_up, (1.0 - ratio) * pol.pi, (1.0 - ratio) * pol.weight, d, self.T, pol.est_loss, pol.loss, upd_lst2) calP_u.append(g_pol2) else: diff_up = p.diff(p_upper) diff_lo = p.diff(p_lower) ratio1 = 1.0 * intersect_up.volume / p.volume upd_lst = pol.updated upd_lst[t] = 1 g_pol1 = Grind_Polytope(intersect_up, ratio1 * pol.pi, ratio1 * pol.weight, d, self.T, pol.est_loss, pol.loss, upd_lst) calP_u.append(g_pol1) ratio2 = 1.0 * intersect_lo.volume / p.volume g_pol2 = Grind_Polytope(intersect_lo, ratio2 * pol.pi, ratio2 * pol.weight, d, self.T, pol.est_loss, pol.loss, upd_lst) calP_l.append(g_pol2) #if ratio1 > 0 or ratio2 > 0: diff_uplo = pc.intersect(diff_up, diff_lo) if (not self.is_empty(diff_uplo) and not self.has_tiny_vol(diff_uplo)): upd_lst[t] = 0 g_pol3 = Grind_Polytope( diff_uplo, (1.0 - ratio1 - ratio2) * pol.pi, (1.0 - ratio1 - ratio2) * pol.weight, d, self.T, pol.est_loss, pol.loss, upd_lst) calP_m.append(g_pol3) elif (p.volume == 0): # discard point-polytopes print("Timestep t=%d polytope w zero vol" % t) print(p) print("Was it really empty?") print pc.is_empty(p) else: intersect_up = pc.intersect(p, p_upper) intersect_lo = pc.intersect(p, p_lower) if (self.is_empty(intersect_up) and self.is_empty(intersect_lo)): pol.updated[t] = 0 calP_m.append(pol) elif (self.is_empty(intersect_lo) or intersect_lo.volume < intersect_up.volume): # should go with upper polytopes set pol.updated[t] = 1 calP_u.append(pol) elif (self.is_empty(intersect_up) or intersect_up.volume < intersect_lo.volume): # should go with lower polytopes set pol.updated[t] = 1 calP_l.append(pol) for pol in calP_u: if pol.pi <= 0.000001: pol.pi = 0.000001 for pol in calP_m: if pol.pi <= 0.000001: pol.pi = 0.000001 for pol in calP_l: if pol.pi <= 0.000001: pol.pi = 0.000001 actions_set = [] upd = [] pi_lst = [] tot_up = 0.0 tot_lo = 0.0 incl = [ ] # indicator function whether the current action belongs in an upper or lower polytope set for pol in calP_u: actions_set.append(pol.action) upd.append(pol.updated) #size |\calA| x T pi_lst.append(pol.pi) incl.append(1) tot_up += pol.pi for pol in calP_m: actions_set.append(pol.action) upd.append(pol.updated) incl.append(0) pi_lst.append(pol.pi) for pol in calP_l: actions_set.append(pol.action) upd.append(pol.updated) pi_lst.append(pol.pi) incl.append(1) tot_lo += pol.pi # a lower bound in the in probabilities of the actions that are to be updated is # the tot prob of the upper and the lower polytopes sets in_probs_est = self.compute_in_probs_regr(pi_lst, t, upd, actions_taken, actions_set, tot_up + tot_lo, incl) spammer = 1 if self.agents_lst[t].type == 0 else 0 j = 0 for pol in calP_u: pol.est_loss += (1.0 * spammer) / in_probs_est[j] j += 1 for pol in calP_m: j += 1 for pol in calP_l: pol.est_loss += (1.0 - spammer) / in_probs_est[j] j += 1 return (calP_u, calP_m, calP_l)
def merge_partition_pair( old_regions, ab2, cur_mode, prev_modes, old_parents, old_ap_labeling ): """Merge an Abstraction with the current partition iterate. @param old_regions: A list of C{Region} that is from either: 1. The ppp of the first (initial) L{AbstractPwa} to be merged. 2. A list of already-merged regions @type old_regions: list of C{Region} @param ab2: Abstracted piecewise affine dynamics to be merged into the @type ab2: L{AbstractPwa} @param cur_mode: mode to be merged @type cur_mode: tuple @param prev_modes: list of modes that have already been merged together @type prev_modes: list of tuple @param old_parents: dict of modes that have already been merged to dict of indices of new regions to indices of regions @type old_parents: dict of modes to list of region indices in list C{old_regions} or dict of region indices to regions in original ppp for that mode @param old_ap_labeling: dict of states of already-merged modes to sets of propositions for each state @type old_ap_labeling: dict of tuples to sets @return: the following: - C{new_list}, list of new regions - C{parents}, same as input param C{old_parents}, except that it includes the mode that was just merged and for list of regions in return value C{new_list} - C{ap_labeling}, same as input param C{old_ap_labeling}, except that it includes the mode that was just merged. """ logger.info('merging partitions') part2 = ab2.ppp modes = prev_modes + [cur_mode] new_list = [] parents = {mode:dict() for mode in modes} ap_labeling = dict() for i in range(len(old_regions)): for j in range(len(part2)): isect = pc.intersect(old_regions[i], part2[j]) rc, xc = pc.cheby_ball(isect) # no intersection ? if rc < 1e-5: continue logger.info('merging region: A' + str(i) + ', with: B' + str(j)) # if Polytope, make it Region if len(isect) == 0: isect = pc.Region([isect]) # label the Region with propositions isect.props = old_regions[i].props.copy() new_list.append(isect) idx = new_list.index(isect) # keep track of parents for mode in prev_modes: parents[mode][idx] = old_parents[mode][i] parents[cur_mode][idx] = j # union of AP labels from parent states ap_label_1 = old_ap_labeling[i] ap_label_2 = ab2.ts.states[j]['ap'] logger.debug('AP label 1: ' + str(ap_label_1)) logger.debug('AP label 2: ' + str(ap_label_2)) # original partitions may be different if pwa_partition used # but must originate from same initial partition, # i.e., have same continuous propositions, checked above # # so no two intersecting regions can have different AP labels, # checked here if ap_label_1 != ap_label_2: msg = 'Inconsistent AP labels between intersecting regions\n' msg += 'of partitions of switched system.' raise Exception(msg) ap_labeling[idx] = ap_label_1 return new_list, parents, ap_labeling
def find_equilibria(ssd, eps=0): """ Finds the polytope that contains the equilibrium points @param ssd: The dynamics of the switched system @type ssd: L{SwitchedSysDyn} @param eps: The value by which the width of all polytopes containing equilibrium points is increased. @type eps: float @return param cont_props: The polytope representations of the atomic propositions of the state space to be used in partitiong @type cont_props: dict of polytope.Polytope Warning: Currently, there is no condition for points where the polytope is critically stable. Warning: if there is a region outside the domain, then it is unstable. It seems to ignore the regions outside the domain. """ def normalize(A, B): """ Normalizes set of equations of the form Ax<=B """ if A.size > 0: Anorm = np.sqrt(np.sum(A * A, 1)).flatten() pos = np.nonzero(Anorm > 1e-10)[0] A = A[pos, :] B = B[pos] Anorm = Anorm[pos] mult = 1 / Anorm for i in xrange(A.shape[0]): A[i, :] = A[i, :] * mult[i] B = B.flatten() * mult return A, B cont_ss = ssd.cts_ss min_outx = cont_ss.b[0] min_outy = cont_ss.b[1] max_outx = min_outx + 1 max_outy = min_outy + 1 abs_tol = 1e-7 cont_props = dict() for mode in ssd.modes: cont_dyn = ssd.dynamics[mode].list_subsys[0] A = cont_dyn.A K = cont_dyn.K.T[0] I = np.eye(len(A), dtype=float) rank_IA = np.linalg.matrix_rank(I - A) concat = np.hstack((I - A, K.reshape(len(A), 1))) rank_concat = np.linalg.matrix_rank(concat) soln = pc.Polytope() props_sym = 'eqpnt_' + str(mode[1]) if (rank_IA == rank_concat): if (rank_IA == len(A)): equil = np.dot(np.linalg.inv(I - A), K) if (equil[0] >= (-cont_ss.b[2]) and equil[0] <= cont_ss.b[0] and equil[1] >= (-cont_ss.b[3]) and equil[1] <= cont_ss.b[1]): delta = eps + equil / 100 soln = pc.box2poly( [[equil[0] - delta[0], equil[0] + delta[0]], [equil[1] - delta[1], equil[1] + delta[1]]]) else: soln = pc.box2poly([[min_outx, max_outx], [min_outy, max_outy]]) elif (rank_IA < len(A)): if eps == 0: eps = abs(min(np.amin(-K), np.amin(A - I))) IAn, Kn = normalize(I - A, K) soln = pc.Polytope(np.vstack((IAn, -IAn)), np.hstack((Kn + eps, -Kn + eps))) relevantsoln = pc.intersect(soln, cont_ss, abs_tol) if (pc.is_empty(relevantsoln) & ~pc.is_empty(soln)): soln = pc.box2poly([[min_outx, max_outx], [min_outy, max_outy]]) else: soln = relevantsoln else: #Assuming trajectories go to infinity as there are no #equilibrium points soln = pc.box2poly([[min_outx, max_outx], [min_outy, max_outy]]) print str(mode) + " trajectories go to infinity! No solution" cont_props[props_sym] = soln return cont_props