def prop2part_test(): state_space = pc.Polytope.from_box(np.array([[0., 2.],[0., 2.]])) cont_props = [] A = [] b = [] A.append(np.array([[1., 0.], [-1., 0.], [0., 1.], [0., -1.]])) b.append(np.array([[.5, 0., .5, 0.]]).T) cont_props.append(pc.Polytope(A[0], b[0])) A.append(np.array([[1., 0.], [-1., 0.], [0., 1.], [0., -1.]])) b.append(np.array([[2., -1.5, 2., -1.5]]).T) cont_props.append(pc.Polytope(A[1], b[1])) cont_props_dict = {"C"+str(i) : pc.Polytope(A[i], b[i]) for i in range(2)} mypartition = prop2part(state_space, cont_props_dict) print(mypartition) ref_adjacency = np.array([[1,0,1],[0,1,1],[1,1,1]]) assert np.all(mypartition.adj.todense() == ref_adjacency) assert len(mypartition.regions) == 3 for reg in mypartition.regions[0:2]: assert len(reg.props) == 1 assert len(reg) == 1 assert cont_props_dict == mypartition.prop_regions assert len(mypartition.regions[2].props) == 0 assert len(mypartition.regions[2]) == 3 dum = state_space.copy() for reg in mypartition.regions[0:2]: dum = dum.diff(reg) assert pc.is_empty(dum.diff(mypartition.regions[2]) ) assert pc.is_empty(mypartition.regions[2].diff(dum) ) assert(mypartition.preserves_predicates()) # invalidate it mypartition.regions += [pc.Region([pc.Polytope(A[0], b[0])], {})] assert(not mypartition.preserves_predicates())
def region_empty_test(self): # Note that as of commit a037b555758ed9ee736fa7cb324d300b8d622fb4 # Region.__init__ deletes empty polytopes from # the given list of polytopes at instantiation. reg = pc.Region() reg.list_poly = [pc.Polytope(), pc.Polytope()] assert len(reg) > 0 assert pc.is_empty(reg)
def intersect(self, Z, m): """Check the intersection between 2 (probabilistic) zonotopes m is the scaling factor for the confidence set """ # These are deterministic zonotopes s1 = self.get_confidence_sets(m)[0] s2 = Z.get_confidence_sets(m)[0] return not pc.is_empty(s1.to_poly().intersect(s2.to_poly()))
def __init__(self, list_subsys=[], domain=None, time_semantics=None, timestep=None, overwrite_time=True): """ @type overwrite_time: bool @param overwrite_time: If true, then overwrites any time data in the objects in C{list_subsys} with the data in C{time_semantics} and C{timestep} variables. Otherwise checks that the time data of the objects in C{list_subsys} are consistent with C{time_semantics} and C{timestep}. """ if domain is None: warn("Domain not given to PwaSysDyn()") if ((domain is not None) and (not (isinstance(domain, pc.Polytope) or isinstance(domain, pc.Region)))): raise Exception( "PwaSysDyn: `domain` has to be a Polytope or Region") if len(list_subsys) > 0: uncovered_dom = domain.copy() n = list_subsys[0].A.shape[1] # State space dimension m = list_subsys[0].B.shape[1] # Input space dimension p = list_subsys[0].E.shape[1] # Disturbance space dimension for subsys in list_subsys: uncovered_dom = uncovered_dom.diff(subsys.domain) if (n != subsys.A.shape[1] or m != subsys.B.shape[1] or p != subsys.E.shape[1]): raise Exception("PwaSysDyn: state, input, disturbance " + "dimensions have to be the same for all " + "subsystems") if not pc.is_empty(uncovered_dom): raise Exception("PwaSysDyn: subdomains must cover the domain") for x in itertools.combinations(list_subsys, 2): if pc.is_fulldim(x[0].domain.intersect(x[1].domain)): raise Exception( "PwaSysDyn: subdomains have to be mutually" + " exclusive") self.list_subsys = list_subsys self.domain = domain # Input time semantics _check_time_data(time_semantics, timestep) if overwrite_time: _push_time_data(self.list_subsys, time_semantics, timestep) else: _check_time_consistency(list_subsys, time_semantics, timestep) self.timestep = timestep self.time_semantics = time_semantics
def __init__(self, list_subsys=[], domain=None, time_semantics=None, timestep=None, overwrite_time=True): """ @type overwrite_time: bool @param overwrite_time: If true, then overwrites any time data in the objects in C{list_subsys} with the data in C{time_semantics} and C{timestep} variables. Otherwise checks that the time data of the objects in C{list_subsys} are consistent with C{time_semantics} and C{timestep}. """ if domain is None: warn("Domain not given to PwaSysDyn()") if ((domain is not None) and (not (isinstance(domain, pc.Polytope) or isinstance(domain, pc.Region)) ) ): raise Exception("PwaSysDyn: `domain` has to be a Polytope or Region") if len(list_subsys) > 0: uncovered_dom = domain.copy() n = list_subsys[0].A.shape[1] # State space dimension m = list_subsys[0].B.shape[1] # Input space dimension p = list_subsys[0].E.shape[1] # Disturbance space dimension for subsys in list_subsys: uncovered_dom = uncovered_dom.diff(subsys.domain) if (n!=subsys.A.shape[1] or m!=subsys.B.shape[1] or p!=subsys.E.shape[1]): raise Exception("PwaSysDyn: state, input, disturbance " + "dimensions have to be the same for all " + "subsystems") if not pc.is_empty(uncovered_dom): raise Exception("PwaSysDyn: subdomains must cover the domain") for x in itertools.combinations(list_subsys, 2): if pc.is_fulldim(x[0].domain.intersect(x[1].domain) ): raise Exception("PwaSysDyn: subdomains have to be mutually"+ " exclusive") self.list_subsys = list_subsys self.domain = domain # Input time semantics _check_time_data(time_semantics, timestep) if overwrite_time: _push_time_data(self.list_subsys, time_semantics, timestep) else: _check_time_consistency(list_subsys, time_semantics, timestep) self.timestep = timestep self.time_semantics = time_semantics
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 is_empty(self, polytope): return (pc.is_empty(polytope) or (polytope.volume == 0.0))
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 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