LUCENE-7942: Explicitly require conversion to 'aggregation' form before aggregating distances, plus require a conversion back. This is more efficient than my initial commit for this ticket, since sqrt values will be cached for path segments, and will not need to be recomputed.

This commit is contained in:
Karl Wright 2017-08-29 01:16:11 -04:00
parent db87e55750
commit c01d692bac
4 changed files with 51 additions and 18 deletions

View File

@ -78,16 +78,40 @@ public interface DistanceStyle {
*/ */
public double computeDistance(final PlanetModel planetModel, final Plane plane, final double x, final double y, final double z, final Membership... bounds); public double computeDistance(final PlanetModel planetModel, final Plane plane, final double x, final double y, final double z, final Membership... bounds);
/** Convert a distance to a form meant for aggregation.
* This is meant to be used in conjunction with aggregateDistances() and fromAggregationForm().
* Distances should be converted to aggregation form before aggregation is attempted,
* and they should be converted back from aggregation form to yield a final result.
* @param distance is an output of computeDistance().
* @return the distance, converted to aggregation form.
*/
public default double toAggregationForm(final double distance) {
return distance;
}
/** Aggregate two distances together to produce a "sum". /** Aggregate two distances together to produce a "sum".
* This is usually just an addition operation, but in the case of squared distances it is more complex. * This is usually just an addition operation, but in the case of squared distances it is more complex.
* @param distance1 is the first distance. * Distances should be converted to aggregation form before aggregation is attempted,
* @param distance2 is the second distance. * and they should be converted back from aggregation form to yield a final result.
* @return the combined distance. * @param distance1 is the first aggregation form distance.
* @param distance2 is the second aggregation form distance.
* @return the combined aggregation form distance.
*/ */
public default double aggregateDistances(final double distance1, final double distance2) { public default double aggregateDistances(final double distance1, final double distance2) {
return distance1 + distance2; return distance1 + distance2;
} }
/** Convert an aggregation form distance value back to an actual distance.
* This is meant to be used in conjunctiion with toAggregationForm() and aggregateDistances().
* Distances should be converted to aggregation form before aggregation is attempted,
* and they should be converted back from aggregation form to yield a final result.
* @param aggregateDistance is the aggregate form of the distance.
* @return the combined distance.
*/
public default double fromAggregationForm(final double aggregateDistance) {
return aggregateDistance;
}
// The following methods are used to go from a distance value back to something // The following methods are used to go from a distance value back to something
// that can be used to construct a constrained shape. // that can be used to construct a constrained shape.

View File

@ -223,7 +223,7 @@ class GeoStandardPath extends GeoBasePath {
for (PathSegment segment : segments) { for (PathSegment segment : segments) {
double distance = segment.pathDistance(planetModel, distanceStyle, x,y,z); double distance = segment.pathDistance(planetModel, distanceStyle, x,y,z);
if (distance != Double.POSITIVE_INFINITY) if (distance != Double.POSITIVE_INFINITY)
return distanceStyle.aggregateDistances(currentDistance, distance); return distanceStyle.fromAggregationForm(distanceStyle.aggregateDistances(currentDistance, distance));
currentDistance = distanceStyle.aggregateDistances(currentDistance, segment.fullPathDistance(distanceStyle)); currentDistance = distanceStyle.aggregateDistances(currentDistance, segment.fullPathDistance(distanceStyle));
} }
@ -232,7 +232,7 @@ class GeoStandardPath extends GeoBasePath {
for (SegmentEndpoint endpoint : endPoints) { for (SegmentEndpoint endpoint : endPoints) {
double distance = endpoint.pathDistance(distanceStyle, x, y, z); double distance = endpoint.pathDistance(distanceStyle, x, y, z);
if (distance != Double.POSITIVE_INFINITY) if (distance != Double.POSITIVE_INFINITY)
return distanceStyle.aggregateDistances(currentDistance, distance); return distanceStyle.fromAggregationForm(distanceStyle.aggregateDistances(currentDistance, distance));
if (segmentIndex < segments.size()) if (segmentIndex < segments.size())
currentDistance = distanceStyle.aggregateDistances(currentDistance, segments.get(segmentIndex++).fullPathDistance(distanceStyle)); currentDistance = distanceStyle.aggregateDistances(currentDistance, segments.get(segmentIndex++).fullPathDistance(distanceStyle));
} }
@ -554,12 +554,12 @@ class GeoStandardPath extends GeoBasePath {
*@param x is the point x. *@param x is the point x.
*@param y is the point y. *@param y is the point y.
*@param z is the point z. *@param z is the point z.
*@return the distance metric. *@return the distance metric, in aggregation form.
*/ */
public double pathDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) { public double pathDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
if (!isWithin(x,y,z)) if (!isWithin(x,y,z))
return Double.POSITIVE_INFINITY; return Double.POSITIVE_INFINITY;
return distanceStyle.computeDistance(this.point, x, y, z); return distanceStyle.toAggregationForm(distanceStyle.computeDistance(this.point, x, y, z));
} }
/** Compute external distance. /** Compute external distance.
@ -729,13 +729,13 @@ class GeoStandardPath extends GeoBasePath {
/** Compute the full distance along this path segment. /** Compute the full distance along this path segment.
*@param distanceStyle is the distance style. *@param distanceStyle is the distance style.
*@return the distance metric. *@return the distance metric, in aggregation form.
*/ */
public double fullPathDistance(final DistanceStyle distanceStyle) { public double fullPathDistance(final DistanceStyle distanceStyle) {
synchronized (fullDistanceCache) { synchronized (fullDistanceCache) {
Double dist = fullDistanceCache.get(distanceStyle); Double dist = fullDistanceCache.get(distanceStyle);
if (dist == null) { if (dist == null) {
dist = new Double(distanceStyle.computeDistance(start, end.x, end.y, end.z)); dist = new Double(distanceStyle.toAggregationForm(distanceStyle.computeDistance(start, end.x, end.y, end.z)));
fullDistanceCache.put(distanceStyle, dist); fullDistanceCache.put(distanceStyle, dist);
} }
return dist.doubleValue(); return dist.doubleValue();
@ -772,7 +772,7 @@ class GeoStandardPath extends GeoBasePath {
*@param x is the point x. *@param x is the point x.
*@param y is the point y. *@param y is the point y.
*@param z is the point z. *@param z is the point z.
*@return the distance metric. *@return the distance metric, in aggregation form.
*/ */
public double pathDistance(final PlanetModel planetModel, final DistanceStyle distanceStyle, final double x, final double y, final double z) { public double pathDistance(final PlanetModel planetModel, final DistanceStyle distanceStyle, final double x, final double y, final double z) {
if (!isWithin(x,y,z)) if (!isWithin(x,y,z))
@ -785,7 +785,7 @@ class GeoStandardPath extends GeoBasePath {
final double perpZ = normalizedConnectingPlane.x * y - normalizedConnectingPlane.y * x; final double perpZ = normalizedConnectingPlane.x * y - normalizedConnectingPlane.y * x;
final double magnitude = Math.sqrt(perpX * perpX + perpY * perpY + perpZ * perpZ); final double magnitude = Math.sqrt(perpX * perpX + perpY * perpY + perpZ * perpZ);
if (Math.abs(magnitude) < Vector.MINIMUM_RESOLUTION) if (Math.abs(magnitude) < Vector.MINIMUM_RESOLUTION)
return distanceStyle.computeDistance(start, x,y,z); return distanceStyle.toAggregationForm(distanceStyle.computeDistance(start, x,y,z));
final double normFactor = 1.0/magnitude; final double normFactor = 1.0/magnitude;
final Plane normalizedPerpPlane = new Plane(perpX * normFactor, perpY * normFactor, perpZ * normFactor, 0.0); final Plane normalizedPerpPlane = new Plane(perpX * normFactor, perpY * normFactor, perpZ * normFactor, 0.0);
@ -807,7 +807,8 @@ class GeoStandardPath extends GeoBasePath {
else else
throw new RuntimeException("Can't find world intersection for point x="+x+" y="+y+" z="+z); throw new RuntimeException("Can't find world intersection for point x="+x+" y="+y+" z="+z);
} }
return distanceStyle.aggregateDistances(distanceStyle.computeDistance(thePoint, x, y, z), distanceStyle.computeDistance(start, thePoint.x, thePoint.y, thePoint.z)); return distanceStyle.aggregateDistances(distanceStyle.toAggregationForm(distanceStyle.computeDistance(thePoint, x, y, z)),
distanceStyle.toAggregationForm(distanceStyle.computeDistance(start, thePoint.x, thePoint.y, thePoint.z)));
} }
/** Compute external distance. /** Compute external distance.

View File

@ -52,9 +52,13 @@ public class LinearSquaredDistance implements DistanceStyle {
} }
@Override @Override
public double aggregateDistances(final double distance1, final double distance2) { public double toAggregationForm(final double distance) {
final double linearDistance = Math.sqrt(distance1) + Math.sqrt(distance2); return Math.sqrt(distance);
return linearDistance * linearDistance; }
@Override
public double fromAggregationForm(final double aggregateDistance) {
return aggregateDistance * aggregateDistance;
} }
@Override @Override

View File

@ -52,9 +52,13 @@ public class NormalSquaredDistance implements DistanceStyle {
} }
@Override @Override
public double aggregateDistances(final double distance1, final double distance2) { public double toAggregationForm(final double distance) {
final double normalDistance = Math.sqrt(distance1) + Math.sqrt(distance2); return Math.sqrt(distance);
return normalDistance * normalDistance; }
@Override
public double fromAggregationForm(final double aggregateDistance) {
return aggregateDistance * aggregateDistance;
} }
@Override @Override