-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcol_set_addition.py
184 lines (150 loc) · 6.19 KB
/
col_set_addition.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
"""Encapsulates the basic collision set addition functions, so they can
be accessible to any code that uses it
Also provides exceptions for indicating no solution or out of time
"""
def add_col_set_recursive(c1, c2):
"""Returns a new collision set resulting from adding c1 to c2. No
side effecting
collision set is done for the recursive case, where
({1, 2}, ) + ({3, 4}, ) = ({1, 2}, {3, 4})
c1, c2 - tuples of (immutable) sets
returns:
recursive collision set containing c1 and c2
"""
# Make shallow copies
c1 = list(c1)
c2 = list(c2)
while len(c1) > 0:
i = 0
# Whether c1[-1] overlaps with any element of c2
found_overlap = False
while i < len(c2):
if not c2[i].isdisjoint(c1[-1]):
# Found overlap
if c2[i].issuperset(c1[-1]):
# No change in c2
c1.pop()
found_overlap = True
break
# Have found a non-trivial overlap. Need to add the
# union to c1 so that we can check if the union has any
# further overlap with elements of c2
temp = c2.pop(i)
# replace c2[i] with the union of c2[i] and c1[-1]
c1.append(temp.union(c1.pop()))
found_overlap = True
break
else:
# No overlap between c1[-1] and c2[i], so check next
# element of c2
i += 1
if not found_overlap:
# c1[-1] has no overlap with any element of c2, so it can be
# added as is to c2
c2.append(c1.pop())
return tuple(c2)
def add_col_set(c1, c2):
"""Adds the collision sets c1 to c2. c2 is assumed to contain a
single,
possibly empty, set
c1, c2 - input collision sets
returns:
combined collision set containing c1 and c2
"""
temp = frozenset([])
if len(c2) >= 1:
temp = c2[0]
assert len(c2) == 1
for i in c1:
temp = temp.union(i)
if len(temp) == 0:
return ()
return (temp, )
def col_set_add(c1, c2, recursive):
"""Adds two collision sets
c1, c2 - input collision sets
recursive - boolean, whether to perform recursive M* style addition
returns:
collision set containing c1 and c2
"""
if recursive:
return add_col_set_recursive(c1, c2)
else:
return add_col_set(c1, c2)
def effective_col_set(col_set, prev_col_set):
"""Computes the effective collision set to use given the current
collision set and the collision set used to get to the current node
Only makes sense when used with recursive M*
The purpose of this code is that in recursive M*, you invoke a
subplanner to figure out how to get to the goal, which caches the
entire path to the goal . The next step, you have an empty
collision set, so you don't query the subplanner with the cached
path, and have to find a bunch of collisions before using the cached
solution. This is intended for use with a memory of what the
collision set was when you reached a given node.
Computes the "effecitve collision set". Elements of the memorized
collision set are used if they have no non-empty intersections with
elements of the current collision set that are not subsets of the
memorized component.
elements of col_set are NOT used if they are contained within some
element of prev_col_set that is used. Elements of prev_col_set are
used if they completely contain all elements of col_set with which
they intersect
col_set - current collision set
prev_col_set - "memorized" collision set, i.e. the collision set of
the optimal predecessor at the time the path from the
optimal predecessor was first found
returns:
effective collision set. Consists of the elements of the previous
collision set, which should index subplanners which have cached
paths available, and elements of the current collision set which
are not contained within prev_col_set
"""
effective_set = []
prev_col_set = list(prev_col_set)
col_set = list(col_set)
while(len(prev_col_set) > 0):
# Need to keep around the elements of col_set that won't be
# used, because the containing element of prev_col_set may be
# invalidated by a later element of col_set
col_set_to_remove = []
j = 0
while (j < len(col_set)):
if col_set[j].issubset(prev_col_set[-1]):
# this element is contained in prev_col_set, so can be
# skipped unless prev_col_set-1] is invalidated by some
# later element of col_set
col_set_to_remove.append(col_set.pop(j))
elif not col_set[j].isdisjoint(prev_col_set[-1]):
# this element partially overlaps prev_col_set,
# invalidating it, so cannot use this element of
# prev_col_set
prev_col_set.pop()
# return the elements of col_set we were going to remove
col_set.extend(col_set_to_remove)
break
else:
j += 1
else:
# Never broke, so prev_col_set can be used as part of the
# effective collision set
effective_set.append(prev_col_set.pop())
# Just copy over any elements of col_set that survived
effective_set.extend(col_set)
return tuple(effective_set)
class OutOfTimeError(Exception):
def __init__(self, value=None):
self.value = value
def __str__(self):
return repr(self.value)
class NoSolutionError(Exception):
def __init__(self, value=None):
self.value = value
def __str__(self):
return repr(self.value)
class OutOfScopeError(NoSolutionError):
def __init__(self, value=None, col_set=()):
self.value = value
self.col_set = col_set
def __str__(self):
return repr(self.value)