|
|
home |
Bug in Location.Location2D |
Noam Mori
has found a bug in the spatial message propagation mechanism (Grid, HierGrid and
TiledWraparound). The bug effects the handling of node location if it is exactly
on the border of two bins. The symptoms of the bug is that after running the
simulation for a while some nodes do not receive some of the messages sent to
them, even by a physically close neighbor and even when there is no interference
at all.
This bug is originated from the following method (this is the original code). File: Location.java, class: Location2D (same goes for Location3D):
public
boolean
inside(Location min, Location max)
{
Location2D min2d = (Location2D)min,
max2d = (Location2D)max;
if(Main.ASSERT)
Util.assertion(min2d.x<=max2d.x
&& min2d.y<=max2d.y);
return
x<=max2d.x
&&
y<=max2d.y
&&
x>=min2d.x
&&
y>=min2d.y;
}
The fix is:
public
boolean
inside(Location min, Location max)
{
Location2D min2d = (Location2D)min,
max2d = (Location2D)max;
if(Main.ASSERT)
Util.assertion(min2d.x<=max2d.x
&& min2d.y<=max2d.y);
return
x<max2d.x
&&
y<max2d.y
&&
x>=min2d.x
&&
y>=min2d.y;
//Changed by Noam Mori (= removed)
}
public
boolean
inside(Location bounds)
{
Location2D l2d = (Location2D)bounds;
return
x<l2d.x
&& y<l2d.y
&& x>=0
&& y>=0;
//Changed by
Noam Mori (= removed)
}
Explanation:
When deciding if a node is inside a squared area (a bin) the right and the
bottom borders should not be considered part of the bin. This is because when
trying to determine to which bin the node belongs the following methods are
used:
private
LinearList
getBin(Location l)
{
return
bins[getBinI(l)][getBinJ(l)];
}
private
int
getBinI(Location l)
{
return
(int)((l.getX()-bl.getX())/di);
}
private
int
getBinJ(Location l)
{
return
(int)((l.getY()-bl.getY())/dj);
}
The code states that nodes on the border of two adjacent bins belong to the top and/or left bin of the two and not both bins as the original inside(Location bounds) method implies. This inconsistency latter effects the handling of nodes movement if they are exactly on the border of two bins – it is removed from the liked list of the correct bin while the “size” field of a separate bin is wrongly updated or sometimes it does not change bins at all (depending on what is the node’s next step after stopping exactly on the border).
For example: say that each bin is 10X10 and there is a node in position <15,15>. This node moves to the left and stops exactly at position <10,15> (on the border of 2 bins). Say that it’s next step (step s) is to the right to position <10.2,15>. Before step s the node was part of the right bin but when it makes step s the original algorithm thinks that it is in the left bin (this is the result of the method getBin) and so the node will be wrongly transferred from the left bin to the right bin. This is done by removing the node from the bin’s linked list which is done on the correct list via the next and prev fields of the node (RadioData), but the “size” of the left bin is wrongly decremented, as seen in the following code:
public
void
del(Field.RadioData data)
{
if(Main.ASSERT)
Util.assertion(data.loc.inside(bl,
tr));
if(data.prev!=null)
data.prev.next
= data.next;
if(data.next!=null)
data.next.prev
= data.prev;
if(radioList==data)
radioList
=
radioList.next;
size--;
data.next =
null;
data.prev =
null;
if(CHECK_CYCLE)
{
if(hasCycle())
{
throw
new
RuntimeException("cycle detected");
}
}
}
When the “size” of a bin is smaller than the length of it’s list of nodes it can only transfer messages to the first “size” nodes in its linked list, as seen in the following code:
public
int
visitTransmit(SpatialTransmitVisitor visitor,
RadioInfo srcInfo,
Location srcLoc, Message msg, Long durationObj,
double
limit)
{
int visited=0;
for(Field.RadioData dst=radioList;
dst!=null
&& visited<size;
dst=dst.next,
visited++)
{
visitor.visitTransmit(srcInfo, srcLoc, dst.info,
dst.entity,
dst.loc,
msg, durationObj);
}
return
visited;
}
Fixing the method “inside” as described above solves the described bug in all its forms.
Gabriel Kliot
07/24/2008