Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

small performance improvement in QuadTree, code cleanup #3253

Merged
merged 3 commits into from
May 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,9 @@ void testRunDrtStopbasedExample() {
var expectedStats = Stats.newBuilder()
.rejectionRate(0.05)
.rejections(17)
.waitAverage(260.41)
.inVehicleTravelTimeMean(374.87)
.totalTravelTimeMean(635.28)
.waitAverage(260.24)
.inVehicleTravelTimeMean(375.14)
.totalTravelTimeMean(635.38)
.build();

verifyDrtCustomerStatsCloseToExpectedStats(utils.getOutputDirectory(), expectedStats);
Expand Down Expand Up @@ -271,9 +271,9 @@ public void install() {
var expectedStats = Stats.newBuilder()
.rejectionRate(0.05)
.rejections(17)
.waitAverage(261.88)
.inVehicleTravelTimeMean(376.04)
.totalTravelTimeMean(637.93)
.waitAverage(261.71)
.inVehicleTravelTimeMean(376.32)
.totalTravelTimeMean(638.03)
.build();

verifyDrtCustomerStatsCloseToExpectedStats(utils.getOutputDirectory(), expectedStats);
Expand All @@ -294,8 +294,8 @@ void testRunServiceAreabasedExampleWithSpeedUp() {
var expectedStats = Stats.newBuilder()
.rejectionRate(0.02)
.rejections(9)
.waitAverage(224.56)
.inVehicleTravelTimeMean(392.65)
.waitAverage(224.46)
.inVehicleTravelTimeMean(392.74)
.totalTravelTimeMean(617.21)
.build();

Expand Down
163 changes: 92 additions & 71 deletions matsim/src/main/java/org/matsim/core/utils/collections/QuadTree.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public class QuadTree<T> implements Serializable {
* @param maxY The largest y coordinate (northing, latitude) expected
*/
public QuadTree(final double minX, final double minY, final double maxX, final double maxY) {
this.top = new Node<T>(minX, minY, maxX, maxY);
this.top = new Node<>(minX, minY, maxX, maxY);
}

/**
Expand Down Expand Up @@ -140,7 +140,7 @@ public T getClosest(final double x, final double y) {
* @return the objects found within distance to x/y
*/
public Collection<T> getDisk(final double x, final double y, final double distance) {
return this.top.get(x, y, distance, new ArrayList<>());
return this.top.get(x, y, distance * distance, new ArrayList<>());
}

/**
Expand All @@ -152,12 +152,12 @@ public Collection<T> getDisk(final double x, final double y, final double distan
*
* @param x left-right location, longitude
* @param y up-down location, latitude
* @param r_min inner ring radius
* @param r_max outer rind radius
* @param rMin inner ring radius
* @param rMax outer rind radius
* @return objects within the ring
*/
public Collection<T> getRing(final double x, final double y, final double r_min, final double r_max) {
return this.top.get(x, y, r_min, r_max, new ArrayList<>());
public Collection<T> getRing(final double x, final double y, final double rMin, final double rMax) {
return this.top.get(x, y, rMin * rMin, rMax * rMax, new ArrayList<>());
}

/**
Expand Down Expand Up @@ -284,10 +284,10 @@ public double getMaxNorthing() {
*/
public Collection<T> values() {
if (this.values == null) {
this.values = new AbstractCollection<T>() {
this.values = new AbstractCollection<>() {
@Override
public Iterator<T> iterator() {
Iterator<T> iterator = new Iterator<T>() {
return new Iterator<>() {
private final int expectedModCount = QuadTree.this.modCount;
private Leaf<T> currentLeaf = firstLeaf();
private int nextIndex = 0;
Expand Down Expand Up @@ -346,7 +346,6 @@ public void remove() {
}

};
return iterator;
}

@Override
Expand Down Expand Up @@ -440,17 +439,44 @@ public double calcDistance(final double x, final double y) {
}

/**
* Calculates the distance from the given point to the furthest corner of the rectangle
*
* Calculates the square of the (minimum) distance of a given point to the border of the
* rectangle. If the point lies within the rectangle, the distance
* is zero.
*
* @param x left-right location
* @param y up-down location
* @return square of distance to border, 0 if inside rectangle or on border
*/
public double calcDistanceSqr(final double x, final double y) {
double distanceX;
double distanceY;

if (this.minX <= x && x <= this.maxX) {
distanceX = 0;
} else {
distanceX = Math.min(Math.abs(this.minX - x), Math.abs(this.maxX - x));
}
if (this.minY <= y && y <= this.maxY) {
distanceY = 0;
} else {
distanceY = Math.min(Math.abs(this.minY - y), Math.abs(this.maxY - y));
}

return distanceX * distanceX + distanceY * distanceY;
}

/**
* Calculates the square of the distance from the given point to the furthest corner of the rectangle
*
* @param x left-right location
* @param y up-down location
* @return distance to furthest corner of the rectangle
* @return square of distance to the furthest corner of the rectangle
*/
public double calcMaxDistance(final double x, final double y) {
public double calcMaxDistanceSqr(final double x, final double y) {
double distanceX = Math.max(Math.abs(this.minX - x), Math.abs(this.maxX - x));
double distanceY = Math.max(Math.abs(this.minY - y), Math.abs(this.maxY - y));

return Math.sqrt(distanceX * distanceX + distanceY * distanceY);
return distanceX * distanceX + distanceY * distanceY;
}


Expand Down Expand Up @@ -550,25 +576,25 @@ public Rect union(final Rect r) {

/**
* Increases the size of the rectangle by scaleX and scaleY.
*
*
* (and "increase by" means:
* 1.0: increase it by 100% (scale it by 2.0)
* -0.5: decrease it by 50% (scale it by 0.5)
* 0.0: do nothing) michaz
*
*
*/

public Rect scale(double scaleX, double scaleY) {
scaleY *= this.centerY - this.minY;
scaleX *= this.centerX - this.minX;
return new Rect(this.minX - scaleX, this.minY-scaleY, this.maxX + scaleX, this.maxY + scaleY);
}

@Override
public String toString() {
return "topLeft: ("+minX+","+minY+") bottomRight: ("+maxX+","+maxY+")";
}

}

protected static class Leaf<T> implements Serializable {
Expand Down Expand Up @@ -652,7 +678,7 @@ public boolean put(final Leaf<T> leaf) {
}

public boolean put(final double x, final double y, final T value) {
return put(new Leaf<T>(x, y, value));
return put(new Leaf<>(x, y, value));
}

public boolean remove(final double x, final double y, final T value) {
Expand All @@ -667,7 +693,7 @@ public boolean remove(final double x, final double y, final T value) {
}
if (leaf.values != null) {
if (leaf.values.remove(value)) {
if (leaf.values.size() == 0) {
if (leaf.values.isEmpty()) {
this.leaves.remove(leaf);
}
return true;
Expand Down Expand Up @@ -699,27 +725,27 @@ public void clear() {
}
}

/* default */ T get(final double x, final double y, final MutableDouble bestDistance) {
/* default */ T get(final double x, final double y, final MutableDouble bestDistanceSqr) {
if (this.hasChilds) {
T closest = null;
Node<T> bestChild = this.getChild(x, y);
if (bestChild != null) {
closest = bestChild.get(x, y, bestDistance);
closest = bestChild.get(x, y, bestDistanceSqr);
}
if (bestChild != this.northwest && this.northwest.bounds.calcDistance(x, y) < bestDistance.value) {
T value = this.northwest.get(x, y, bestDistance);
if (bestChild != this.northwest && this.northwest.bounds.calcDistanceSqr(x, y) < bestDistanceSqr.value) {
T value = this.northwest.get(x, y, bestDistanceSqr);
if (value != null) { closest = value; }
}
if (bestChild != this.northeast && this.northeast.bounds.calcDistance(x, y) < bestDistance.value) {
T value = this.northeast.get(x, y, bestDistance);
if (bestChild != this.northeast && this.northeast.bounds.calcDistanceSqr(x, y) < bestDistanceSqr.value) {
T value = this.northeast.get(x, y, bestDistanceSqr);
if (value != null) { closest = value; }
}
if (bestChild != this.southeast && this.southeast.bounds.calcDistance(x, y) < bestDistance.value) {
T value = this.southeast.get(x, y, bestDistance);
if (bestChild != this.southeast && this.southeast.bounds.calcDistanceSqr(x, y) < bestDistanceSqr.value) {
T value = this.southeast.get(x, y, bestDistanceSqr);
if (value != null) { closest = value; }
}
if (bestChild != this.southwest && this.southwest.bounds.calcDistance(x, y) < bestDistance.value) {
T value = this.southwest.get(x, y, bestDistance);
if (bestChild != this.southwest && this.southwest.bounds.calcDistanceSqr(x, y) < bestDistanceSqr.value) {
T value = this.southwest.get(x, y, bestDistanceSqr);
if (value != null) { closest = value; }
}
return closest;
Expand All @@ -728,12 +754,12 @@ public void clear() {
T closest = null;
if (this.leaves != null) {
for (Leaf<T> leaf : this.leaves) {
double distance = Math.sqrt(
(leaf.x - x) * (leaf.x - x)
+ (leaf.y - y) * (leaf.y - y));
if (distance < bestDistance.value) {
bestDistance.value = distance;
closest = leaf.value != null ? leaf.value : leaf.values.get(0);
double deltaX = leaf.x - x;
double deltaY = leaf.y - y;
double distanceSqr = deltaX * deltaX + deltaY * deltaY;
if (distanceSqr < bestDistanceSqr.value) {
bestDistanceSqr.value = distanceSqr;
closest = leaf.value != null ? leaf.value : leaf.values.getFirst();
}
}
}
Expand All @@ -751,7 +777,7 @@ public void clear() {
if (this.hasChilds) {
// note: this could probably be improved. The idea here
// is NOT to dive in quadrants which we know do not contain points
// in the ellipse.
// in the ellipse.
// This is done, but we will also dive in some quadrants not intersecting
// the ellipse, which is just a loss of time (the sum of the minimum distances
// to an area is always lower than the munimum of the sum of the distances,
Expand Down Expand Up @@ -807,29 +833,27 @@ public void clear() {
return values;
}

/* default */ Collection<T> get(final double x, final double y, final double maxDistance, final Collection<T> values) {
/* default */ Collection<T> get(final double x, final double y, final double maxDistanceSqr, final Collection<T> values) {
if (this.hasChilds) {
if (this.northwest.bounds.calcDistance(x, y) <= maxDistance) {
this.northwest.get(x, y, maxDistance, values);
if (this.northwest.bounds.calcDistanceSqr(x, y) <= maxDistanceSqr) {
this.northwest.get(x, y, maxDistanceSqr, values);
}
if (this.northeast.bounds.calcDistance(x, y) <= maxDistance) {
this.northeast.get(x, y, maxDistance, values);
if (this.northeast.bounds.calcDistanceSqr(x, y) <= maxDistanceSqr) {
this.northeast.get(x, y, maxDistanceSqr, values);
}
if (this.southeast.bounds.calcDistance(x, y) <= maxDistance) {
this.southeast.get(x, y, maxDistance, values);
if (this.southeast.bounds.calcDistanceSqr(x, y) <= maxDistanceSqr) {
this.southeast.get(x, y, maxDistanceSqr, values);
}
if (this.southwest.bounds.calcDistance(x, y) <= maxDistance) {
this.southwest.get(x, y, maxDistance, values);
if (this.southwest.bounds.calcDistanceSqr(x, y) <= maxDistanceSqr) {
this.southwest.get(x, y, maxDistanceSqr, values);
}
return values;
}
// no more childs, so we must contain the closest object
if (this.leaves != null) {
for (Leaf<T> leaf : this.leaves) {
double distance = Math.sqrt(
(leaf.x - x) * (leaf.x - x)
+ (leaf.y - y) * (leaf.y - y));
if (distance <= maxDistance) {
double distanceSqr = (leaf.x - x) * (leaf.x - x) + (leaf.y - y) * (leaf.y - y);
if (distanceSqr <= maxDistanceSqr) {
if (leaf.value != null) {
values.add(leaf.value);
} else {
Expand All @@ -841,22 +865,20 @@ public void clear() {
return values;
}

/* default */ Collection<T> get(final double x, final double y, final double r_min, final double r_max,
Collection<T> values) {
/* default */ Collection<T> get(final double x, final double y, final double rMinSqr, final double rMaxSqr,
Collection<T> values) {
if (this.hasChilds) {
stepInto(this.northwest, x, y, r_min, r_max, values);
stepInto(this.northeast, x, y, r_min, r_max, values);
stepInto(this.southeast, x, y, r_min, r_max, values);
stepInto(this.southwest, x, y, r_min, r_max, values);
stepIntoSqr(this.northwest, x, y, rMinSqr, rMaxSqr, values);
stepIntoSqr(this.northeast, x, y, rMinSqr, rMaxSqr, values);
stepIntoSqr(this.southeast, x, y, rMinSqr, rMaxSqr, values);
stepIntoSqr(this.southwest, x, y, rMinSqr, rMaxSqr, values);
return values;
}
// no more childs, so we must contain the closest object
if (this.leaves != null) {
for (Leaf<T> leaf : this.leaves) {
double distance = Math.sqrt(
(leaf.x - x) * (leaf.x - x)
+ (leaf.y - y) * (leaf.y - y));
if (distance <= r_max && distance >= r_min) {
double distanceSqr = (leaf.x - x) * (leaf.x - x) + (leaf.y - y) * (leaf.y - y);
if (distanceSqr <= rMaxSqr && distanceSqr >= rMinSqr) {
if (leaf.value != null) {
values.add(leaf.value);
} else {
Expand All @@ -868,12 +890,12 @@ public void clear() {
return values;
}

private void stepInto(Node node, double x, double y, double r_min, double r_max, Collection<T> values) {
double minDistance = node.bounds.calcDistance(x, y);
double maxDistance = node.bounds.calcMaxDistance(x, y);
private void stepIntoSqr(Node<T> node, double x, double y, double rMinSqr, double rMaxSqr, Collection<T> values) {
double minDistanceSqr = node.bounds.calcDistanceSqr(x, y);
double maxDistanceSqr = node.bounds.calcMaxDistanceSqr(x, y);

if(minDistance <= r_max && maxDistance >= r_min) {
node.get(x, y, r_min, r_max, values);
if (minDistanceSqr <= rMaxSqr && maxDistanceSqr >= rMinSqr) {
node.get(x, y, rMinSqr, rMaxSqr, values);
}
}

Expand Down Expand Up @@ -978,14 +1000,13 @@ private Node<T> getChild(final double x, final double y) {
if (leaf == null) { leaf = this.northeast.firstLeaf(); }
return leaf;
}
return (this.leaves == null || this.leaves.isEmpty()) ? null : this.leaves.get(0);
return (this.leaves == null || this.leaves.isEmpty()) ? null : this.leaves.getFirst();
}

/* default */ boolean nextLeaf(final Leaf<T> currentLeaf, final MutableLeaf<T> nextLeaf) {
if (this.hasChilds) {
boolean found = false;
if (currentLeaf.x <= this.bounds.centerX && currentLeaf.y <= this.bounds.centerY) {
found = this.southwest.nextLeaf(currentLeaf, nextLeaf);
boolean found = this.southwest.nextLeaf(currentLeaf, nextLeaf);
if (found) {
if (nextLeaf.value == null) { nextLeaf.value = this.northwest.firstLeaf(); }
if (nextLeaf.value == null) { nextLeaf.value = this.southeast.firstLeaf(); }
Expand All @@ -994,15 +1015,15 @@ private Node<T> getChild(final double x, final double y) {
}
}
if (currentLeaf.x <= this.bounds.centerX && currentLeaf.y >= this.bounds.centerY) {
found = this.northwest.nextLeaf(currentLeaf, nextLeaf);
boolean found = this.northwest.nextLeaf(currentLeaf, nextLeaf);
if (found) {
if (nextLeaf.value == null) { nextLeaf.value = this.southeast.firstLeaf(); }
if (nextLeaf.value == null) { nextLeaf.value = this.northeast.firstLeaf(); }
return true;
}
}
if (currentLeaf.x >= this.bounds.centerX && currentLeaf.y <= this.bounds.centerY) {
found = this.southeast.nextLeaf(currentLeaf, nextLeaf);
boolean found = this.southeast.nextLeaf(currentLeaf, nextLeaf);
if (found) {
if (nextLeaf.value == null) { nextLeaf.value = this.northeast.firstLeaf(); }
return true;
Expand Down
Loading
Loading