Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
CityDoctor
CityDoctor2
Commits
2c9eea60
Commit
2c9eea60
authored
Jun 23, 2026
by
Numanoglu
Browse files
Refine AABB BVH heuristic exploration
parent
35a777b9
Changes
52
Hide whitespace changes
Inline
Side-by-side
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
bht
/RingSelfIntCheckBvhPerformanceTest.java
→
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
aabb/performance
/RingSelfIntCheckBvhPerformanceTest.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.checks.
bht
;
package
de.hft.stuttgart.citydoctor2.checks.
aabb.performance
;
import
de.hft.stuttgart.citydoctor2.checks.aabb.fixtures.SyntheticRingGeometryFactory
;
import
de.hft.stuttgart.citydoctor2.checks.aabb.support.BvhInputMetricsCollector
;
import
de.hft.stuttgart.citydoctor2.checks.aabb.support.BvhPerformanceTestSupport
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
...
@@ -15,8 +19,16 @@ import de.hft.stuttgart.citydoctor2.checks.geometry.RingSelfIntCheck;
...
@@ -15,8 +19,16 @@ import de.hft.stuttgart.citydoctor2.checks.geometry.RingSelfIntCheck;
import
de.hft.stuttgart.citydoctor2.datastructure.Geometry
;
import
de.hft.stuttgart.citydoctor2.datastructure.Geometry
;
import
de.hft.stuttgart.citydoctor2.datastructure.LinearRing
;
import
de.hft.stuttgart.citydoctor2.datastructure.LinearRing
;
import
de.hft.stuttgart.citydoctor2.datastructure.Polygon
;
import
de.hft.stuttgart.citydoctor2.datastructure.Polygon
;
import
de.hft.stuttgart.citydoctor2.datastructure.
bht
.SplitStrategy
;
import
de.hft.stuttgart.citydoctor2.datastructure.
aabb
.SplitStrategy
;
/**
* Manual performance probe for ring self-intersection checks.
*
* The BVH indexes padded edge boxes. The exact ring-intersection logic remains
* in RingSelfIntCheck; this test only compares how cheaply candidates are found.
*
* @author Numanoglu
*/
@Tag
(
"performance"
)
@Tag
(
"performance"
)
public
class
RingSelfIntCheckBvhPerformanceTest
{
public
class
RingSelfIntCheckBvhPerformanceTest
{
...
@@ -30,6 +42,7 @@ public class RingSelfIntCheckBvhPerformanceTest {
...
@@ -30,6 +42,7 @@ public class RingSelfIntCheckBvhPerformanceTest {
}
}
private
static
void
measureScenario
(
String
scenario
,
Geometry
geometry
)
{
private
static
void
measureScenario
(
String
scenario
,
Geometry
geometry
)
{
// Edge count is the natural input size because every segment can become a BVH element.
int
edgeCount
=
countEdges
(
geometry
);
int
edgeCount
=
countEdges
(
geometry
);
BvhInputMetricsCollector
.
print
(
BvhInputMetricsCollector
.
forRings
(
scenario
,
collectExteriorRings
(
geometry
)));
BvhInputMetricsCollector
.
print
(
BvhInputMetricsCollector
.
forRings
(
scenario
,
collectExteriorRings
(
geometry
)));
List
<
BvhPerformanceTestSupport
.
Measurement
>
measurements
=
new
ArrayList
<>();
List
<
BvhPerformanceTestSupport
.
Measurement
>
measurements
=
new
ArrayList
<>();
...
@@ -55,6 +68,7 @@ public class RingSelfIntCheckBvhPerformanceTest {
...
@@ -55,6 +68,7 @@ public class RingSelfIntCheckBvhPerformanceTest {
}
}
private
static
int
runCheck
(
Geometry
geometry
,
RingSelfIntCheck
.
Variant
variant
)
{
private
static
int
runCheck
(
Geometry
geometry
,
RingSelfIntCheck
.
Variant
variant
)
{
// Run each polygon ring independently, as the check normally receives one ring at a time.
int
errorCount
=
0
;
int
errorCount
=
0
;
for
(
Polygon
polygon
:
geometry
.
getPolygons
())
{
for
(
Polygon
polygon
:
geometry
.
getPolygons
())
{
RingSelfIntCheck
check
=
new
RingSelfIntCheck
(
variant
);
RingSelfIntCheck
check
=
new
RingSelfIntCheck
(
variant
);
...
@@ -87,6 +101,7 @@ public class RingSelfIntCheckBvhPerformanceTest {
...
@@ -87,6 +101,7 @@ public class RingSelfIntCheckBvhPerformanceTest {
}
}
private
static
RingSelfIntCheck
.
Variant
variantFor
(
SplitStrategy
strategy
)
{
private
static
RingSelfIntCheck
.
Variant
variantFor
(
SplitStrategy
strategy
)
{
// Bridge the shared BVH strategy enum to the check-specific mode enum.
switch
(
strategy
)
{
switch
(
strategy
)
{
case
BINARY_OBJECT_MEDIAN:
case
BINARY_OBJECT_MEDIAN:
return
RingSelfIntCheck
.
Variant
.
BVH_BINARY_OBJECT_MEDIAN
;
return
RingSelfIntCheck
.
Variant
.
BVH_BINARY_OBJECT_MEDIAN
;
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
bht
/SolidSelfIntersectionBvhPerformanceTest.java
→
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
aabb/performance
/SolidSelfIntersectionBvhPerformanceTest.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.checks.
bht
;
package
de.hft.stuttgart.citydoctor2.checks.
aabb.performance
;
import
de.hft.stuttgart.citydoctor2.checks.aabb.fixtures.SyntheticCityGmlLikeGeometryFactory
;
import
de.hft.stuttgart.citydoctor2.checks.aabb.fixtures.SyntheticSolidGeometryFactory
;
import
de.hft.stuttgart.citydoctor2.checks.aabb.support.BvhInputMetricsCollector
;
import
de.hft.stuttgart.citydoctor2.checks.aabb.support.BvhPerformanceTestSupport
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
...
@@ -12,10 +17,18 @@ import de.hft.stuttgart.citydoctor2.checks.util.SelfIntersectionUtil;
...
@@ -12,10 +17,18 @@ import de.hft.stuttgart.citydoctor2.checks.util.SelfIntersectionUtil;
import
de.hft.stuttgart.citydoctor2.datastructure.Geometry
;
import
de.hft.stuttgart.citydoctor2.datastructure.Geometry
;
import
de.hft.stuttgart.citydoctor2.datastructure.Lod
;
import
de.hft.stuttgart.citydoctor2.datastructure.Lod
;
import
de.hft.stuttgart.citydoctor2.datastructure.Polygon
;
import
de.hft.stuttgart.citydoctor2.datastructure.Polygon
;
import
de.hft.stuttgart.citydoctor2.datastructure.
bht
.AABB
;
import
de.hft.stuttgart.citydoctor2.datastructure.
aabb
.AABB
;
import
de.hft.stuttgart.citydoctor2.datastructure.
bht
.BoundingVolumeHierarchyTree
;
import
de.hft.stuttgart.citydoctor2.datastructure.
aabb
.BoundingVolumeHierarchyTree
;
import
de.hft.stuttgart.citydoctor2.datastructure.
bht
.SplitStrategy
;
import
de.hft.stuttgart.citydoctor2.datastructure.
aabb
.SplitStrategy
;
/**
* Manual performance probe for solid self-intersection candidate search.
*
* It compares the brute-force reference with all six BVH split strategies on
* synthetic solids whose polygon distributions are deliberately different.
*
* @author Numanoglu
*/
@Tag
(
"performance"
)
@Tag
(
"performance"
)
public
class
SolidSelfIntersectionBvhPerformanceTest
{
public
class
SolidSelfIntersectionBvhPerformanceTest
{
...
@@ -34,6 +47,7 @@ public class SolidSelfIntersectionBvhPerformanceTest {
...
@@ -34,6 +47,7 @@ public class SolidSelfIntersectionBvhPerformanceTest {
}
}
private
static
void
measureScenario
(
String
scenario
,
Geometry
geometry
)
{
private
static
void
measureScenario
(
String
scenario
,
Geometry
geometry
)
{
// Result counts must match the brute-force run; only the broad-phase cost should change.
int
polygonCount
=
geometry
.
getPolygons
().
size
();
int
polygonCount
=
geometry
.
getPolygons
().
size
();
BvhInputMetricsCollector
.
print
(
BvhInputMetricsCollector
.
forPolygons
(
scenario
,
geometry
.
getPolygons
()));
BvhInputMetricsCollector
.
print
(
BvhInputMetricsCollector
.
forPolygons
(
scenario
,
geometry
.
getPolygons
()));
List
<
BvhPerformanceTestSupport
.
Measurement
>
measurements
=
new
ArrayList
<>();
List
<
BvhPerformanceTestSupport
.
Measurement
>
measurements
=
new
ArrayList
<>();
...
@@ -58,6 +72,7 @@ public class SolidSelfIntersectionBvhPerformanceTest {
...
@@ -58,6 +72,7 @@ public class SolidSelfIntersectionBvhPerformanceTest {
}
}
private
static
int
calculateWithTree
(
Geometry
geometry
,
SplitStrategy
strategy
)
{
private
static
int
calculateWithTree
(
Geometry
geometry
,
SplitStrategy
strategy
)
{
// Build a fresh tree per measurement so construction cost stays part of the comparison.
BoundingVolumeHierarchyTree
<
Polygon
>
tree
=
BoundingVolumeHierarchyTree
<
Polygon
>
tree
=
BoundingVolumeHierarchyTree
.
newWithStrategy
(
BoundingVolumeHierarchyTree
.
newWithStrategy
(
geometry
.
getPolygons
(),
geometry
.
getPolygons
(),
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/aabb/support/BvhExplorationCsvWriter.java
0 → 100644
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.checks.aabb.support
;
import
java.io.BufferedWriter
;
import
java.io.IOException
;
import
java.nio.charset.StandardCharsets
;
import
java.nio.file.Files
;
import
java.nio.file.Path
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.Locale
;
import
de.hft.stuttgart.citydoctor2.datastructure.aabb.SplitStrategy
;
/**
* Writes structured BVH exploration data for reproducible downstream analysis.
*
* Console tables are useful while running the tests, but the CSV exports are
* the stable handoff to spreadsheet tools, pandas, and the written evaluation.
*
* @author Numanoglu
*/
public
final
class
BvhExplorationCsvWriter
{
public
static
final
String
CANDIDATE_POLICY_VERSION
=
"candidate-rules-v1"
;
private
static
final
List
<
String
>
OBSERVATION_HEADER
=
List
.
of
(
"source"
,
"dataSplit"
,
"dataset"
,
"datasetLabel"
,
"check"
,
"scenario"
,
"n"
,
"thin"
,
"relVol"
,
"spread"
,
"aspect"
,
"currentPolicy"
,
"currentPolicyMs"
,
"currentPolicyRegretRatio"
,
"candidatePolicy"
,
"winnerBvh"
,
"match"
,
"baselineVariant"
,
"baselineMs"
,
"bestBvhMs"
,
"candidatePolicyMs"
,
"regretMs"
,
"regretRatio"
,
"slowdownFactor"
,
"resultCount"
,
"policyVersion"
);
private
static
final
List
<
String
>
BUCKET_HEADER
=
List
.
of
(
"check"
,
"metric"
,
"bucket"
,
"cases"
,
"binaryObjectMedianWins"
,
"binaryObjectMeanWins"
,
"binarySpatialMedianWins"
,
"octonaryObjectMedianWins"
,
"octonaryObjectMeanWins"
,
"octonarySpatialMedianWins"
,
"mostCommonBvh"
,
"winnerCount"
,
"winnerShare"
,
"secondBestCount"
,
"winnerMargin"
,
"candidateRule"
);
private
BvhExplorationCsvWriter
()
{
}
public
static
void
writeObservations
(
Path
outputFile
,
List
<
ObservationRecord
>
records
)
throws
IOException
{
// One row corresponds to one measured check/scenario combination.
List
<
List
<
String
>>
rows
=
new
ArrayList
<>(
records
.
size
());
for
(
ObservationRecord
record
:
records
)
{
BvhHeuristicTimingSupport
.
Observation
observation
=
record
.
observation
;
BvhInputMetricsCollector
.
Metrics
metrics
=
observation
.
metrics
;
rows
.
add
(
List
.
of
(
record
.
source
,
record
.
dataSplit
,
record
.
dataset
,
record
.
datasetLabel
,
observation
.
checkName
,
metrics
.
scenario
,
Integer
.
toString
(
metrics
.
elementCount
),
decimal
(
metrics
.
thinBoxRate
),
decimal
(
metrics
.
averageRelativeBoxVolume
),
decimal
(
metrics
.
centerSpreadRatio
),
decimal
(
metrics
.
averageAspectRatio
),
observation
.
currentPolicyPrediction
,
measurementMillis
(
observation
.
currentPolicyMeasurement
),
decimalOrEmpty
(
observation
.
currentPolicyRegretRatio
()),
observation
.
candidateRulePrediction
,
observation
.
fastestBvh
.
variant
,
observation
.
candidateRuleMatchLabel
(),
observation
.
baseline
==
null
?
""
:
observation
.
baseline
.
variant
,
measurementMillis
(
observation
.
baseline
),
measurementMillis
(
observation
.
fastestBvh
),
measurementMillis
(
observation
.
candidateRuleMeasurement
),
decimalOrEmpty
(
observation
.
candidateRegretMillis
()),
decimalOrEmpty
(
observation
.
candidateRegretRatio
()),
decimalOrEmpty
(
observation
.
candidateSlowdownFactor
()),
Integer
.
toString
(
observation
.
fastestBvh
.
resultCount
),
record
.
policyVersion
));
}
write
(
outputFile
,
OBSERVATION_HEADER
,
rows
);
}
public
static
void
writeBucketSummary
(
Path
outputFile
,
List
<
BucketSummaryRecord
>
records
)
throws
IOException
{
// Bucket rows summarize how often each split strategy wins inside one metric interval.
List
<
List
<
String
>>
rows
=
new
ArrayList
<>(
records
.
size
());
for
(
BucketSummaryRecord
record
:
records
)
{
rows
.
add
(
List
.
of
(
record
.
checkName
,
record
.
metricName
,
record
.
bucketName
,
Integer
.
toString
(
record
.
cases
),
Integer
.
toString
(
record
.
winCount
(
SplitStrategy
.
BINARY_OBJECT_MEDIAN
)),
Integer
.
toString
(
record
.
winCount
(
SplitStrategy
.
BINARY_OBJECT_MEAN
)),
Integer
.
toString
(
record
.
winCount
(
SplitStrategy
.
BINARY_SPATIAL_MEDIAN
)),
Integer
.
toString
(
record
.
winCount
(
SplitStrategy
.
OCTONARY_OBJECT_MEDIAN
)),
Integer
.
toString
(
record
.
winCount
(
SplitStrategy
.
OCTONARY_OBJECT_MEAN
)),
Integer
.
toString
(
record
.
winCount
(
SplitStrategy
.
OCTONARY_SPATIAL_MEDIAN
)),
record
.
mostCommonBvh
,
Integer
.
toString
(
record
.
winnerCount
),
decimal
(
record
.
winnerShare
),
Integer
.
toString
(
record
.
secondBestCount
),
decimal
(
record
.
winnerMargin
),
Boolean
.
toString
(
record
.
candidateRule
)));
}
write
(
outputFile
,
BUCKET_HEADER
,
rows
);
}
private
static
void
write
(
Path
outputFile
,
List
<
String
>
header
,
List
<
List
<
String
>>
rows
)
throws
IOException
{
Files
.
createDirectories
(
outputFile
.
getParent
());
try
(
BufferedWriter
writer
=
Files
.
newBufferedWriter
(
outputFile
,
StandardCharsets
.
UTF_8
))
{
writeRow
(
writer
,
header
);
for
(
List
<
String
>
row
:
rows
)
{
writeRow
(
writer
,
row
);
}
}
}
private
static
void
writeRow
(
BufferedWriter
writer
,
List
<
String
>
values
)
throws
IOException
{
for
(
int
i
=
0
;
i
<
values
.
size
();
i
++)
{
if
(
i
>
0
)
{
writer
.
write
(
','
);
}
writer
.
write
(
escape
(
values
.
get
(
i
)));
}
writer
.
newLine
();
}
private
static
String
escape
(
String
value
)
{
if
(
value
.
indexOf
(
','
)
<
0
&&
value
.
indexOf
(
'"'
)
<
0
&&
value
.
indexOf
(
'\n'
)
<
0
)
{
return
value
;
}
return
'"'
+
value
.
replace
(
"\""
,
"\"\""
)
+
'"'
;
}
private
static
String
measurementMillis
(
BvhPerformanceTestSupport
.
Measurement
measurement
)
{
return
measurement
==
null
?
""
:
decimal
(
measurement
.
averageMillis
());
}
private
static
String
decimalOrEmpty
(
double
value
)
{
return
Double
.
isFinite
(
value
)
?
decimal
(
value
)
:
""
;
}
private
static
String
decimal
(
double
value
)
{
return
String
.
format
(
Locale
.
ROOT
,
"%.9f"
,
value
);
}
public
static
final
class
ObservationRecord
{
public
final
String
source
;
public
final
String
dataSplit
;
public
final
String
dataset
;
public
final
String
datasetLabel
;
public
final
String
policyVersion
;
public
final
BvhHeuristicTimingSupport
.
Observation
observation
;
/**
* Adds dataset labels around a measured observation before it is written.
*/
public
ObservationRecord
(
String
source
,
String
dataSplit
,
String
dataset
,
String
datasetLabel
,
String
policyVersion
,
BvhHeuristicTimingSupport
.
Observation
observation
)
{
this
.
source
=
source
;
this
.
dataSplit
=
dataSplit
;
this
.
dataset
=
dataset
;
this
.
datasetLabel
=
datasetLabel
;
this
.
policyVersion
=
policyVersion
;
this
.
observation
=
observation
;
}
}
public
static
final
class
BucketSummaryRecord
{
public
final
String
checkName
;
public
final
String
metricName
;
public
final
String
bucketName
;
public
final
int
cases
;
public
final
java
.
util
.
Map
<
String
,
Integer
>
winnerCounts
;
public
final
String
mostCommonBvh
;
public
final
int
winnerCount
;
public
final
double
winnerShare
;
public
final
int
secondBestCount
;
public
final
double
winnerMargin
;
public
final
boolean
candidateRule
;
/**
* Stores the winner distribution for one check, metric, and bucket.
*/
public
BucketSummaryRecord
(
String
checkName
,
String
metricName
,
String
bucketName
,
int
cases
,
java
.
util
.
Map
<
String
,
Integer
>
winnerCounts
,
String
mostCommonBvh
,
int
winnerCount
,
double
winnerShare
,
int
secondBestCount
,
double
winnerMargin
,
boolean
candidateRule
)
{
this
.
checkName
=
checkName
;
this
.
metricName
=
metricName
;
this
.
bucketName
=
bucketName
;
this
.
cases
=
cases
;
this
.
winnerCounts
=
java
.
util
.
Map
.
copyOf
(
winnerCounts
);
this
.
mostCommonBvh
=
mostCommonBvh
;
this
.
winnerCount
=
winnerCount
;
this
.
winnerShare
=
winnerShare
;
this
.
secondBestCount
=
secondBestCount
;
this
.
winnerMargin
=
winnerMargin
;
this
.
candidateRule
=
candidateRule
;
}
private
int
winCount
(
SplitStrategy
strategy
)
{
return
winnerCounts
.
getOrDefault
(
strategy
.
name
(),
0
);
}
}
}
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
bh
t/BvhHeuristicTimingSupport.java
→
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
aabb/suppor
t/BvhHeuristicTimingSupport.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.checks.
bh
t
;
package
de.hft.stuttgart.citydoctor2.checks.
aabb.suppor
t
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
...
@@ -8,8 +8,18 @@ import java.util.List;
...
@@ -8,8 +8,18 @@ import java.util.List;
import
de.hft.stuttgart.citydoctor2.checks.geometry.NestedRingsCheck
;
import
de.hft.stuttgart.citydoctor2.checks.geometry.NestedRingsCheck
;
import
de.hft.stuttgart.citydoctor2.checks.geometry.RingSelfIntCheck
;
import
de.hft.stuttgart.citydoctor2.checks.geometry.RingSelfIntCheck
;
import
de.hft.stuttgart.citydoctor2.checks.util.BvhUsagePolicy
;
import
de.hft.stuttgart.citydoctor2.checks.util.BvhUsagePolicy
;
import
de.hft.stuttgart.citydoctor2.datastructure.
bht
.SplitStrategy
;
import
de.hft.stuttgart.citydoctor2.datastructure.
aabb
.SplitStrategy
;
/**
* Centralizes timing and policy comparison code for the BVH heuristic tests.
*
* The class keeps the exploratory tests small: they provide the geometry and
* the operation to run, while this support class measures all split strategies,
* checks that result counts stay stable, and records both the current production
* policy and the candidate rules derived from the synthetic study.
*
* @author Numanoglu
*/
public
final
class
BvhHeuristicTimingSupport
{
public
final
class
BvhHeuristicTimingSupport
{
private
BvhHeuristicTimingSupport
()
{
private
BvhHeuristicTimingSupport
()
{
...
@@ -23,6 +33,7 @@ public final class BvhHeuristicTimingSupport {
...
@@ -23,6 +33,7 @@ public final class BvhHeuristicTimingSupport {
String
baselineName
,
String
baselineName
,
MeasuredOperation
baselineOperation
,
MeasuredOperation
baselineOperation
,
StrategyOperation
strategyOperation
)
{
StrategyOperation
strategyOperation
)
{
// Full checks compare each BVH variant against the old or brute-force implementation.
List
<
BvhPerformanceTestSupport
.
Measurement
>
measurements
=
new
ArrayList
<>();
List
<
BvhPerformanceTestSupport
.
Measurement
>
measurements
=
new
ArrayList
<>();
BvhPerformanceTestSupport
.
Measurement
baseline
=
BvhPerformanceTestSupport
.
measure
(
BvhPerformanceTestSupport
.
Measurement
baseline
=
BvhPerformanceTestSupport
.
measure
(
scenarioName
,
scenarioName
,
...
@@ -42,17 +53,18 @@ public final class BvhHeuristicTimingSupport {
...
@@ -42,17 +53,18 @@ public final class BvhHeuristicTimingSupport {
baseline
.
resultCount
,
bvhMeasurement
.
resultCount
);
baseline
.
resultCount
,
bvhMeasurement
.
resultCount
);
}
}
String
synthet
icPrediction
=
predictFrom
Synthetic
Policy
(
checkName
,
metrics
);
String
currentPol
ic
y
Prediction
=
predictFrom
Current
Policy
(
checkName
,
metrics
);
String
candidatePrediction
=
predictCandidate
Strategy
(
checkName
,
metrics
);
String
candidate
Rule
Prediction
=
predict
From
Candidate
Rules
(
checkName
,
metrics
);
return
new
Observation
(
return
new
Observation
(
checkName
,
checkName
,
metrics
,
metrics
,
fastest
(
measurements
),
fastest
(
measurements
),
fastestBvh
(
measurements
),
fastestBvh
(
measurements
),
baseline
,
baseline
,
syntheticPrediction
,
currentPolicyPrediction
,
candidatePrediction
,
candidateRulePrediction
,
measurementForPrediction
(
measurements
,
candidatePrediction
));
measurementForPrediction
(
measurements
,
currentPolicyPrediction
),
measurementForPrediction
(
measurements
,
candidateRulePrediction
));
}
}
public
static
Observation
measureBvhStrategies
(
public
static
Observation
measureBvhStrategies
(
...
@@ -61,6 +73,7 @@ public final class BvhHeuristicTimingSupport {
...
@@ -61,6 +73,7 @@ public final class BvhHeuristicTimingSupport {
BvhInputMetricsCollector
.
Metrics
metrics
,
BvhInputMetricsCollector
.
Metrics
metrics
,
int
inputSize
,
int
inputSize
,
StrategyOperation
strategyOperation
)
{
StrategyOperation
strategyOperation
)
{
// Real CityGML runs measure only broad-phase candidate search, so the first BVH variant is the count baseline.
List
<
BvhPerformanceTestSupport
.
Measurement
>
measurements
=
new
ArrayList
<>();
List
<
BvhPerformanceTestSupport
.
Measurement
>
measurements
=
new
ArrayList
<>();
BvhPerformanceTestSupport
.
Measurement
baseline
=
null
;
BvhPerformanceTestSupport
.
Measurement
baseline
=
null
;
for
(
SplitStrategy
strategy
:
BvhPerformanceTestSupport
.
concreteStrategies
())
{
for
(
SplitStrategy
strategy
:
BvhPerformanceTestSupport
.
concreteStrategies
())
{
...
@@ -78,20 +91,22 @@ public final class BvhHeuristicTimingSupport {
...
@@ -78,20 +91,22 @@ public final class BvhHeuristicTimingSupport {
measurements
.
add
(
bvhMeasurement
);
measurements
.
add
(
bvhMeasurement
);
}
}
String
synthet
icPrediction
=
predictFrom
Synthetic
Policy
(
checkName
,
metrics
);
String
currentPol
ic
y
Prediction
=
predictFrom
Current
Policy
(
checkName
,
metrics
);
String
candidatePrediction
=
predictCandidate
Strategy
(
checkName
,
metrics
);
String
candidate
Rule
Prediction
=
predict
From
Candidate
Rules
(
checkName
,
metrics
);
return
new
Observation
(
return
new
Observation
(
checkName
,
checkName
,
metrics
,
metrics
,
fastest
(
measurements
),
fastest
(
measurements
),
fastestBvh
(
measurements
),
fastestBvh
(
measurements
),
baseline
,
baseline
,
syntheticPrediction
,
currentPolicyPrediction
,
candidatePrediction
,
candidateRulePrediction
,
measurementForPrediction
(
measurements
,
candidatePrediction
));
measurementForPrediction
(
measurements
,
currentPolicyPrediction
),
measurementForPrediction
(
measurements
,
candidateRulePrediction
));
}
}
public
static
String
predictFromSyntheticPolicy
(
String
checkName
,
BvhInputMetricsCollector
.
Metrics
metrics
)
{
public
static
String
predictFromCurrentPolicy
(
String
checkName
,
BvhInputMetricsCollector
.
Metrics
metrics
)
{
// This is the currently implemented production policy, not a learned rule.
BvhUsagePolicy
.
BvhCheckType
checkType
=
checkTypeFor
(
checkName
);
BvhUsagePolicy
.
BvhCheckType
checkType
=
checkTypeFor
(
checkName
);
if
(!
BvhUsagePolicy
.
shouldUseTree
(
checkType
,
metrics
.
summary
))
{
if
(!
BvhUsagePolicy
.
shouldUseTree
(
checkType
,
metrics
.
summary
))
{
return
"OLD"
;
return
"OLD"
;
...
@@ -99,7 +114,8 @@ public final class BvhHeuristicTimingSupport {
...
@@ -99,7 +114,8 @@ public final class BvhHeuristicTimingSupport {
return
BvhUsagePolicy
.
chooseSplitStrategy
(
checkType
,
metrics
.
summary
).
name
();
return
BvhUsagePolicy
.
chooseSplitStrategy
(
checkType
,
metrics
.
summary
).
name
();
}
}
public
static
String
predictCandidateStrategy
(
String
checkName
,
BvhInputMetricsCollector
.
Metrics
metrics
)
{
public
static
String
predictFromCandidateRules
(
String
checkName
,
BvhInputMetricsCollector
.
Metrics
metrics
)
{
// Candidate rules are intentionally explicit so they can be reviewed before being moved into production code.
BvhUsagePolicy
.
BvhCheckType
checkType
=
checkTypeFor
(
checkName
);
BvhUsagePolicy
.
BvhCheckType
checkType
=
checkTypeFor
(
checkName
);
if
(!
BvhUsagePolicy
.
shouldUseTree
(
checkType
,
metrics
.
summary
))
{
if
(!
BvhUsagePolicy
.
shouldUseTree
(
checkType
,
metrics
.
summary
))
{
return
"OLD"
;
return
"OLD"
;
...
@@ -129,6 +145,7 @@ public final class BvhHeuristicTimingSupport {
...
@@ -129,6 +145,7 @@ public final class BvhHeuristicTimingSupport {
}
}
public
static
NestedRingsCheck
.
Variant
nestedVariantFor
(
SplitStrategy
strategy
)
{
public
static
NestedRingsCheck
.
Variant
nestedVariantFor
(
SplitStrategy
strategy
)
{
// NestedRingsCheck exposes variants as check modes, while the BVH builder uses SplitStrategy.
switch
(
strategy
)
{
switch
(
strategy
)
{
case
BINARY_OBJECT_MEDIAN:
case
BINARY_OBJECT_MEDIAN:
return
NestedRingsCheck
.
Variant
.
BVH_BINARY_OBJECT_MEDIAN
;
return
NestedRingsCheck
.
Variant
.
BVH_BINARY_OBJECT_MEDIAN
;
...
@@ -149,6 +166,7 @@ public final class BvhHeuristicTimingSupport {
...
@@ -149,6 +166,7 @@ public final class BvhHeuristicTimingSupport {
}
}
public
static
RingSelfIntCheck
.
Variant
rsiVariantFor
(
SplitStrategy
strategy
)
{
public
static
RingSelfIntCheck
.
Variant
rsiVariantFor
(
SplitStrategy
strategy
)
{
// RingSelfIntCheck has the same six BVH modes but owns its own enum.
switch
(
strategy
)
{
switch
(
strategy
)
{
case
BINARY_OBJECT_MEDIAN:
case
BINARY_OBJECT_MEDIAN:
return
RingSelfIntCheck
.
Variant
.
BVH_BINARY_OBJECT_MEDIAN
;
return
RingSelfIntCheck
.
Variant
.
BVH_BINARY_OBJECT_MEDIAN
;
...
@@ -212,7 +230,8 @@ public final class BvhHeuristicTimingSupport {
...
@@ -212,7 +230,8 @@ public final class BvhHeuristicTimingSupport {
List
<
BvhPerformanceTestSupport
.
Measurement
>
measurements
,
List
<
BvhPerformanceTestSupport
.
Measurement
>
measurements
,
String
prediction
)
{
String
prediction
)
{
for
(
BvhPerformanceTestSupport
.
Measurement
measurement
:
measurements
)
{
for
(
BvhPerformanceTestSupport
.
Measurement
measurement
:
measurements
)
{
if
(
measurement
.
variant
.
equals
(
prediction
))
{
if
(
measurement
.
variant
.
equals
(
prediction
)
||
(
"OLD"
.
equals
(
prediction
)
&&
"BRUTE_FORCE"
.
equals
(
measurement
.
variant
)))
{
return
measurement
;
return
measurement
;
}
}
}
}
...
@@ -223,19 +242,30 @@ public final class BvhHeuristicTimingSupport {
...
@@ -223,19 +242,30 @@ public final class BvhHeuristicTimingSupport {
int
run
();
int
run
();
}
}
/**
* Operation that can be executed with each concrete BVH split strategy.
*/
public
interface
StrategyOperation
{
public
interface
StrategyOperation
{
int
run
(
SplitStrategy
strategy
);
int
run
(
SplitStrategy
strategy
);
}
}
/**
* One measured row in the heuristic tables.
*
* It stores the input metrics, the fastest measured BVH strategy, and the
* timings for the current and candidate policies so regret can be calculated
* without re-running the checks in the Python post-processing step.
*/
public
static
final
class
Observation
{
public
static
final
class
Observation
{
public
final
String
checkName
;
public
final
String
checkName
;
public
final
BvhInputMetricsCollector
.
Metrics
metrics
;
public
final
BvhInputMetricsCollector
.
Metrics
metrics
;
public
final
BvhPerformanceTestSupport
.
Measurement
fastest
;
public
final
BvhPerformanceTestSupport
.
Measurement
fastest
;
public
final
BvhPerformanceTestSupport
.
Measurement
fastestBvh
;
public
final
BvhPerformanceTestSupport
.
Measurement
fastestBvh
;
public
final
BvhPerformanceTestSupport
.
Measurement
baseline
;
public
final
BvhPerformanceTestSupport
.
Measurement
baseline
;
public
final
BvhPerformanceTestSupport
.
Measurement
predictedBvh
;
public
final
BvhPerformanceTestSupport
.
Measurement
currentPolicyMeasurement
;
public
final
String
syntheticPrediction
;
public
final
BvhPerformanceTestSupport
.
Measurement
candidateRuleMeasurement
;
public
final
String
candidatePrediction
;
public
final
String
currentPolicyPrediction
;
public
final
String
candidateRulePrediction
;
Observation
(
Observation
(
String
checkName
,
String
checkName
,
...
@@ -243,24 +273,56 @@ public final class BvhHeuristicTimingSupport {
...
@@ -243,24 +273,56 @@ public final class BvhHeuristicTimingSupport {
BvhPerformanceTestSupport
.
Measurement
fastest
,
BvhPerformanceTestSupport
.
Measurement
fastest
,
BvhPerformanceTestSupport
.
Measurement
fastestBvh
,
BvhPerformanceTestSupport
.
Measurement
fastestBvh
,
BvhPerformanceTestSupport
.
Measurement
baseline
,
BvhPerformanceTestSupport
.
Measurement
baseline
,
String
syntheticPrediction
,
String
currentPolicyPrediction
,
String
candidatePrediction
,
String
candidateRulePrediction
,
BvhPerformanceTestSupport
.
Measurement
predictedBvh
)
{
BvhPerformanceTestSupport
.
Measurement
currentPolicyMeasurement
,
BvhPerformanceTestSupport
.
Measurement
candidateRuleMeasurement
)
{
this
.
checkName
=
checkName
;
this
.
checkName
=
checkName
;
this
.
metrics
=
metrics
;
this
.
metrics
=
metrics
;
this
.
fastest
=
fastest
;
this
.
fastest
=
fastest
;
this
.
fastestBvh
=
fastestBvh
;
this
.
fastestBvh
=
fastestBvh
;
this
.
baseline
=
baseline
;
this
.
baseline
=
baseline
;
this
.
syntheticPrediction
=
syntheticPrediction
;
this
.
currentPolicyPrediction
=
currentPolicyPrediction
;
this
.
candidatePrediction
=
candidatePrediction
;
this
.
candidateRulePrediction
=
candidateRulePrediction
;
this
.
predictedBvh
=
predictedBvh
;
this
.
currentPolicyMeasurement
=
currentPolicyMeasurement
;
this
.
candidateRuleMeasurement
=
candidateRuleMeasurement
;
}
}
public
String
candidate
Prediction
MatchLabel
()
{
public
String
candidate
Rule
MatchLabel
()
{
if
(
"OLD"
.
equals
(
candidatePrediction
))
{
if
(
"OLD"
.
equals
(
candidate
Rule
Prediction
))
{
return
"n/a"
;
return
"n/a"
;
}
}
return
candidatePrediction
.
equals
(
fastestBvh
.
variant
)
?
"yes"
:
"no"
;
return
candidateRulePrediction
.
equals
(
fastestBvh
.
variant
)
?
"yes"
:
"no"
;
}
public
double
candidateRegretMillis
()
{
if
(
candidateRuleMeasurement
==
null
||
fastestBvh
==
null
)
{
return
Double
.
NaN
;
}
return
candidateRuleMeasurement
.
averageMillis
()
-
fastestBvh
.
averageMillis
();
}
public
double
candidateRegretRatio
()
{
if
(
candidateRuleMeasurement
==
null
||
fastestBvh
==
null
||
fastestBvh
.
averageNanos
==
0L
)
{
return
Double
.
NaN
;
}
return
(
double
)
(
candidateRuleMeasurement
.
averageNanos
-
fastestBvh
.
averageNanos
)
/
fastestBvh
.
averageNanos
;
}
public
double
candidateSlowdownFactor
()
{
if
(
candidateRuleMeasurement
==
null
||
fastestBvh
==
null
||
fastestBvh
.
averageNanos
==
0L
)
{
return
Double
.
NaN
;
}
return
(
double
)
candidateRuleMeasurement
.
averageNanos
/
fastestBvh
.
averageNanos
;
}
public
double
currentPolicyRegretRatio
()
{
if
(
currentPolicyMeasurement
==
null
||
fastestBvh
==
null
||
fastestBvh
.
averageNanos
==
0L
)
{
return
Double
.
NaN
;
}
return
(
double
)
(
currentPolicyMeasurement
.
averageNanos
-
fastestBvh
.
averageNanos
)
/
fastestBvh
.
averageNanos
;
}
}
}
}
}
}
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
bh
t/BvhInputMetricsCollector.java
→
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
aabb/suppor
t/BvhInputMetricsCollector.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.checks.
bh
t
;
package
de.hft.stuttgart.citydoctor2.checks.
aabb.suppor
t
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.List
;
...
@@ -7,7 +7,7 @@ import java.util.function.Function;
...
@@ -7,7 +7,7 @@ import java.util.function.Function;
import
de.hft.stuttgart.citydoctor2.datastructure.LinearRing
;
import
de.hft.stuttgart.citydoctor2.datastructure.LinearRing
;
import
de.hft.stuttgart.citydoctor2.datastructure.Polygon
;
import
de.hft.stuttgart.citydoctor2.datastructure.Polygon
;
import
de.hft.stuttgart.citydoctor2.datastructure.
bht
.AABB
;
import
de.hft.stuttgart.citydoctor2.datastructure.
aabb
.AABB
;
import
de.hft.stuttgart.citydoctor2.checks.util.BvhUsagePolicy
;
import
de.hft.stuttgart.citydoctor2.checks.util.BvhUsagePolicy
;
/**
/**
...
@@ -19,7 +19,7 @@ import de.hft.stuttgart.citydoctor2.checks.util.BvhUsagePolicy;
...
@@ -19,7 +19,7 @@ import de.hft.stuttgart.citydoctor2.checks.util.BvhUsagePolicy;
*
*
* @author Numanoglu
* @author Numanoglu
*/
*/
final
class
BvhInputMetricsCollector
{
public
final
class
BvhInputMetricsCollector
{
private
static
final
double
DEGENERATE_TOLERANCE
=
1
e
-
12
;
private
static
final
double
DEGENERATE_TOLERANCE
=
1
e
-
12
;
private
static
final
long
MAX_PAIR_SAMPLES
=
20_000L
;
private
static
final
long
MAX_PAIR_SAMPLES
=
20_000L
;
...
@@ -28,30 +28,31 @@ final class BvhInputMetricsCollector {
...
@@ -28,30 +28,31 @@ final class BvhInputMetricsCollector {
private
BvhInputMetricsCollector
()
{
private
BvhInputMetricsCollector
()
{
}
}
static
Metrics
forPolygons
(
String
scenario
,
List
<?
extends
Polygon
>
polygons
)
{
public
static
Metrics
forPolygons
(
String
scenario
,
List
<?
extends
Polygon
>
polygons
)
{
return
collect
(
scenario
,
polygons
,
polygon
->
AABB
.
of
(
polygon
.
getOriginal
()));
return
collect
(
scenario
,
polygons
,
polygon
->
AABB
.
of
(
polygon
.
getOriginal
()));
}
}
static
Metrics
forPolygonsCheap
(
String
scenario
,
List
<?
extends
Polygon
>
polygons
)
{
public
static
Metrics
forPolygonsCheap
(
String
scenario
,
List
<?
extends
Polygon
>
polygons
)
{
// Used in heuristic runs where pair sampling would dominate the cost for large inputs.
return
collect
(
scenario
,
polygons
,
polygon
->
AABB
.
of
(
polygon
.
getOriginal
()),
false
);
return
collect
(
scenario
,
polygons
,
polygon
->
AABB
.
of
(
polygon
.
getOriginal
()),
false
);
}
}
static
Metrics
forRings
(
String
scenario
,
List
<?
extends
LinearRing
>
rings
)
{
public
static
Metrics
forRings
(
String
scenario
,
List
<?
extends
LinearRing
>
rings
)
{
return
collect
(
scenario
,
rings
,
AABB:
:
of
);
return
collect
(
scenario
,
rings
,
AABB:
:
of
);
}
}
static
Metrics
forRingsCheap
(
String
scenario
,
List
<?
extends
LinearRing
>
rings
)
{
public
static
Metrics
forRingsCheap
(
String
scenario
,
List
<?
extends
LinearRing
>
rings
)
{
return
collect
(
scenario
,
rings
,
AABB:
:
of
,
false
);
return
collect
(
scenario
,
rings
,
AABB:
:
of
,
false
);
}
}
/**
/**
* Collects metrics from any element type that can be converted to an AABB.
* Collects metrics from any element type that can be converted to an AABB.
*/
*/
static
<
E
>
Metrics
collect
(
String
scenario
,
List
<
E
>
elements
,
Function
<
E
,
AABB
>
aabbFunction
)
{
public
static
<
E
>
Metrics
collect
(
String
scenario
,
List
<
E
>
elements
,
Function
<
E
,
AABB
>
aabbFunction
)
{
return
collect
(
scenario
,
elements
,
aabbFunction
,
true
);
return
collect
(
scenario
,
elements
,
aabbFunction
,
true
);
}
}
static
<
E
>
Metrics
collect
(
public
static
<
E
>
Metrics
collect
(
String
scenario
,
String
scenario
,
List
<
E
>
elements
,
List
<
E
>
elements
,
Function
<
E
,
AABB
>
aabbFunction
,
Function
<
E
,
AABB
>
aabbFunction
,
...
@@ -71,11 +72,12 @@ final class BvhInputMetricsCollector {
...
@@ -71,11 +72,12 @@ final class BvhInputMetricsCollector {
* Collects metrics from precomputed AABBs. Pairwise overlap and containment
* Collects metrics from precomputed AABBs. Pairwise overlap and containment
* are sampled when the full pairs set would be too large.
* are sampled when the full pairs set would be too large.
*/
*/
static
Metrics
collectBoxes
(
String
scenario
,
List
<
AABB
>
boxes
)
{
public
static
Metrics
collectBoxes
(
String
scenario
,
List
<
AABB
>
boxes
)
{
return
collectBoxes
(
scenario
,
boxes
,
true
);
return
collectBoxes
(
scenario
,
boxes
,
true
);
}
}
static
Metrics
collectBoxesCheap
(
String
scenario
,
List
<
AABB
>
boxes
)
{
public
static
Metrics
collectBoxesCheap
(
String
scenario
,
List
<
AABB
>
boxes
)
{
// Cheap mode keeps the metrics close to what an AUTO policy could compute during tree setup.
return
collectBoxes
(
scenario
,
boxes
,
false
);
return
collectBoxes
(
scenario
,
boxes
,
false
);
}
}
...
@@ -160,7 +162,7 @@ final class BvhInputMetricsCollector {
...
@@ -160,7 +162,7 @@ final class BvhInputMetricsCollector {
/**
/**
* Emits one compact metrics line that can be read next to a performance table.
* Emits one compact metrics line that can be read next to a performance table.
*/
*/
static
void
print
(
Metrics
metrics
)
{
public
static
void
print
(
Metrics
metrics
)
{
System
.
out
.
println
(
metrics
.
toSummaryLine
());
System
.
out
.
println
(
metrics
.
toSummaryLine
());
}
}
...
@@ -195,6 +197,7 @@ final class BvhInputMetricsCollector {
...
@@ -195,6 +197,7 @@ final class BvhInputMetricsCollector {
}
}
private
static
PairSample
samplePairs
(
List
<
AABB
>
boxes
)
{
private
static
PairSample
samplePairs
(
List
<
AABB
>
boxes
)
{
// Pair metrics are diagnostic only; they are capped so exploratory tests remain runnable.
long
totalPairs
=
countPairs
(
boxes
.
size
());
long
totalPairs
=
countPairs
(
boxes
.
size
());
long
sampleEvery
=
Math
.
max
(
1L
,
totalPairs
/
MAX_PAIR_SAMPLES
);
long
sampleEvery
=
Math
.
max
(
1L
,
totalPairs
/
MAX_PAIR_SAMPLES
);
long
pairIndex
=
0L
;
long
pairIndex
=
0L
;
...
@@ -247,9 +250,9 @@ final class BvhInputMetricsCollector {
...
@@ -247,9 +250,9 @@ final class BvhInputMetricsCollector {
}
}
private
static
final
class
Aggregate
{
private
static
final
class
Aggregate
{
final
double
dx
;
public
final
double
dx
;
final
double
dy
;
public
final
double
dy
;
final
double
dz
;
public
final
double
dz
;
Aggregate
(
double
dx
,
double
dy
,
double
dz
)
{
Aggregate
(
double
dx
,
double
dy
,
double
dz
)
{
this
.
dx
=
dx
;
this
.
dx
=
dx
;
...
@@ -259,9 +262,9 @@ final class BvhInputMetricsCollector {
...
@@ -259,9 +262,9 @@ final class BvhInputMetricsCollector {
}
}
private
static
final
class
PairSample
{
private
static
final
class
PairSample
{
final
long
sampledPairs
;
public
final
long
sampledPairs
;
final
long
overlappingPairs
;
public
final
long
overlappingPairs
;
final
long
containedPairs
;
public
final
long
containedPairs
;
PairSample
(
long
sampledPairs
,
long
overlappingPairs
,
long
containedPairs
)
{
PairSample
(
long
sampledPairs
,
long
overlappingPairs
,
long
containedPairs
)
{
this
.
sampledPairs
=
sampledPairs
;
this
.
sampledPairs
=
sampledPairs
;
...
@@ -282,25 +285,25 @@ final class BvhInputMetricsCollector {
...
@@ -282,25 +285,25 @@ final class BvhInputMetricsCollector {
}
}
}
}
static
final
class
Metrics
{
public
static
final
class
Metrics
{
final
String
scenario
;
public
final
String
scenario
;
final
int
elementCount
;
public
final
int
elementCount
;
final
long
totalPairs
;
public
final
long
totalPairs
;
final
long
sampledPairs
;
public
final
long
sampledPairs
;
final
double
sampledOverlapRate
;
public
final
double
sampledOverlapRate
;
final
double
sampledContainmentRate
;
public
final
double
sampledContainmentRate
;
final
double
degenerateRate
;
public
final
double
degenerateRate
;
final
double
thinBoxRate
;
public
final
double
thinBoxRate
;
final
double
averageAspectRatio
;
public
final
double
averageAspectRatio
;
final
double
averageRelativeBoxVolume
;
public
final
double
averageRelativeBoxVolume
;
final
double
averageExtentX
;
public
final
double
averageExtentX
;
final
double
averageExtentY
;
public
final
double
averageExtentY
;
final
double
averageExtentZ
;
public
final
double
averageExtentZ
;
final
double
normalizedCenterSpreadX
;
public
final
double
normalizedCenterSpreadX
;
final
double
normalizedCenterSpreadY
;
public
final
double
normalizedCenterSpreadY
;
final
double
normalizedCenterSpreadZ
;
public
final
double
normalizedCenterSpreadZ
;
final
double
centerSpreadRatio
;
public
final
double
centerSpreadRatio
;
final
BvhUsagePolicy
.
BvhInputSummary
summary
;
public
final
BvhUsagePolicy
.
BvhInputSummary
summary
;
private
Metrics
(
private
Metrics
(
String
scenario
,
String
scenario
,
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
bh
t/BvhPerformanceTestSupport.java
→
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/
aabb/suppor
t/BvhPerformanceTestSupport.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.checks.
bh
t
;
package
de.hft.stuttgart.citydoctor2.checks.
aabb.suppor
t
;
import
java.io.OutputStream
;
import
java.io.OutputStream
;
import
java.io.PrintStream
;
import
java.io.PrintStream
;
...
@@ -9,7 +9,7 @@ import org.apache.logging.log4j.Level;
...
@@ -9,7 +9,7 @@ import org.apache.logging.log4j.Level;
import
org.apache.logging.log4j.LogManager
;
import
org.apache.logging.log4j.LogManager
;
import
org.apache.logging.log4j.core.config.Configurator
;
import
org.apache.logging.log4j.core.config.Configurator
;
import
de.hft.stuttgart.citydoctor2.datastructure.
bht
.SplitStrategy
;
import
de.hft.stuttgart.citydoctor2.datastructure.
aabb
.SplitStrategy
;
/**
/**
* Shared support for manual BVH performance probes.
* Shared support for manual BVH performance probes.
...
@@ -21,17 +21,17 @@ import de.hft.stuttgart.citydoctor2.datastructure.bht.SplitStrategy;
...
@@ -21,17 +21,17 @@ import de.hft.stuttgart.citydoctor2.datastructure.bht.SplitStrategy;
*
*
* @author Numanoglu
* @author Numanoglu
*/
*/
final
class
BvhPerformanceTestSupport
{
public
final
class
BvhPerformanceTestSupport
{
static
final
int
DEFAULT_WARMUP_RUNS
=
2
;
public
static
final
int
DEFAULT_WARMUP_RUNS
=
2
;
static
final
int
DEFAULT_MEASURE_RUNS
=
5
;
public
static
final
int
DEFAULT_MEASURE_RUNS
=
5
;
private
static
final
boolean
SUPPRESS_MEASURED_STDOUT
=
true
;
private
static
final
boolean
SUPPRESS_MEASURED_STDOUT
=
true
;
private
static
final
PrintStream
SILENT_OUT
=
new
PrintStream
(
OutputStream
.
nullOutputStream
());
private
static
final
PrintStream
SILENT_OUT
=
new
PrintStream
(
OutputStream
.
nullOutputStream
());
private
BvhPerformanceTestSupport
()
{
private
BvhPerformanceTestSupport
()
{
}
}
static
SplitStrategy
[]
concreteStrategies
()
{
public
static
SplitStrategy
[]
concreteStrategies
()
{
return
new
SplitStrategy
[]
{
return
new
SplitStrategy
[]
{
SplitStrategy
.
BINARY_OBJECT_MEDIAN
,
SplitStrategy
.
BINARY_OBJECT_MEDIAN
,
SplitStrategy
.
BINARY_OBJECT_MEAN
,
SplitStrategy
.
BINARY_OBJECT_MEAN
,
...
@@ -42,7 +42,7 @@ final class BvhPerformanceTestSupport {
...
@@ -42,7 +42,7 @@ final class BvhPerformanceTestSupport {
};
};
}
}
static
Measurement
measure
(
String
scenario
,
String
variant
,
int
inputSize
,
Supplier
<
Integer
>
measuredOperation
)
{
public
static
Measurement
measure
(
String
scenario
,
String
variant
,
int
inputSize
,
Supplier
<
Integer
>
measuredOperation
)
{
return
measure
(
return
measure
(
scenario
,
scenario
,
variant
,
variant
,
...
@@ -56,7 +56,7 @@ final class BvhPerformanceTestSupport {
...
@@ -56,7 +56,7 @@ final class BvhPerformanceTestSupport {
* Measures one variant. The supplied operation must return a deterministic
* Measures one variant. The supplied operation must return a deterministic
* result count so correctness can still be checked while timing is collected.
* result count so correctness can still be checked while timing is collected.
*/
*/
static
Measurement
measure
(
public
static
Measurement
measure
(
String
scenario
,
String
scenario
,
String
variant
,
String
variant
,
int
inputSize
,
int
inputSize
,
...
@@ -115,7 +115,7 @@ final class BvhPerformanceTestSupport {
...
@@ -115,7 +115,7 @@ final class BvhPerformanceTestSupport {
* Prints a scenario-level table and highlights the fastest measured variant.
* Prints a scenario-level table and highlights the fastest measured variant.
* The first measurement is treated as the baseline for the speedup column.
* The first measurement is treated as the baseline for the speedup column.
*/
*/
static
void
printScenarioSummary
(
String
scenario
,
List
<
Measurement
>
measurements
)
{
public
static
void
printScenarioSummary
(
String
scenario
,
List
<
Measurement
>
measurements
)
{
if
(
measurements
.
isEmpty
())
{
if
(
measurements
.
isEmpty
())
{
return
;
return
;
}
}
...
@@ -160,12 +160,12 @@ final class BvhPerformanceTestSupport {
...
@@ -160,12 +160,12 @@ final class BvhPerformanceTestSupport {
return
(
double
)
baseline
.
averageNanos
/
measurement
.
averageNanos
;
return
(
double
)
baseline
.
averageNanos
/
measurement
.
averageNanos
;
}
}
static
final
class
Measurement
{
public
static
final
class
Measurement
{
final
String
scenario
;
public
final
String
scenario
;
final
String
variant
;
public
final
String
variant
;
final
int
inputSize
;
public
final
int
inputSize
;
final
int
resultCount
;
public
final
int
resultCount
;
final
long
averageNanos
;
public
final
long
averageNanos
;
Measurement
(
String
scenario
,
String
variant
,
int
inputSize
,
int
resultCount
,
long
averageNanos
)
{
Measurement
(
String
scenario
,
String
variant
,
int
inputSize
,
int
resultCount
,
long
averageNanos
)
{
this
.
scenario
=
scenario
;
this
.
scenario
=
scenario
;
...
@@ -175,7 +175,7 @@ final class BvhPerformanceTestSupport {
...
@@ -175,7 +175,7 @@ final class BvhPerformanceTestSupport {
this
.
averageNanos
=
averageNanos
;
this
.
averageNanos
=
averageNanos
;
}
}
double
averageMillis
()
{
public
double
averageMillis
()
{
return
averageNanos
/
1_000_000.0
;
return
averageNanos
/
1_000_000.0
;
}
}
}
}
...
...
CityDoctorParent/CityDoctorValidation/src/test/python/bvh_exploration_tables.py
0 → 100644
View file @
2c9eea60
"""Create compact report tables from the BVH exploration CSV exports."""
from
__future__
import
annotations
import
argparse
from
pathlib
import
Path
import
pandas
as
pd
OBSERVATION_COLUMNS
=
[
"dataSplit"
,
"dataset"
,
"datasetLabel"
,
"check"
,
"scenario"
,
"n"
,
"thin"
,
"relVol"
,
"spread"
,
"aspect"
,
"currentPolicy"
,
"currentPolicyMs"
,
"currentPolicyRegretRatio"
,
"candidatePolicy"
,
"winnerBvh"
,
"match"
,
"bestBvhMs"
,
"candidatePolicyMs"
,
"regretMs"
,
"regretRatio"
,
"slowdownFactor"
,
]
def
parse_args
()
->
argparse
.
Namespace
:
"""Read input and output directories from the command line."""
parser
=
argparse
.
ArgumentParser
(
description
=
__doc__
)
parser
.
add_argument
(
"--input-dir"
,
type
=
Path
,
default
=
Path
(
"target/bvh-exploration"
),
help
=
"Directory containing the Java CSV exports."
,
)
parser
.
add_argument
(
"--output-dir"
,
type
=
Path
,
default
=
Path
(
"target/bvh-exploration/report"
),
help
=
"Directory for compact CSV and HTML tables."
,
)
return
parser
.
parse_args
()
def
read_csv
(
path
:
Path
)
->
pd
.
DataFrame
:
"""Load one Java export and fail early when the expected file is missing."""
if
not
path
.
is_file
():
raise
FileNotFoundError
(
f
"Missing BVH exploration export:
{
path
}
"
)
return
pd
.
read_csv
(
path
)
def
compact_observations
(
frame
:
pd
.
DataFrame
)
->
pd
.
DataFrame
:
"""Keep the columns that are useful for reading and reporting observations."""
columns
=
[
column
for
column
in
OBSERVATION_COLUMNS
if
column
in
frame
.
columns
]
return
frame
.
loc
[:,
columns
].
copy
()
def
candidate_rules
(
bucket_summary
:
pd
.
DataFrame
)
->
pd
.
DataFrame
:
"""Extract bucket rules that passed the Java-side support and share thresholds."""
candidate_mask
=
bucket_summary
[
"candidateRule"
].
astype
(
str
).
str
.
lower
().
eq
(
"true"
)
rules
=
bucket_summary
.
loc
[
candidate_mask
].
copy
()
columns
=
[
"check"
,
"metric"
,
"bucket"
,
"cases"
,
"mostCommonBvh"
,
"winnerShare"
,
"winnerMargin"
,
]
return
rules
.
loc
[:,
columns
].
sort_values
([
"check"
,
"metric"
,
"bucket"
])
def
real_dataset_summary
(
real
:
pd
.
DataFrame
)
->
pd
.
DataFrame
:
"""Aggregate real CityGML observations by dataset split, dataset label, and check."""
evaluated
=
real
.
loc
[
real
[
"regretRatio"
].
notna
()].
copy
()
if
evaluated
.
empty
:
return
pd
.
DataFrame
(
columns
=
[
"dataSplit"
,
"dataset"
,
"datasetLabel"
,
"check"
,
"cases"
,
"matches"
,
"matchRate"
,
"medianRegretRatio"
,
"p90RegretRatio"
,
"medianSlowdownFactor"
,
"maxSlowdownFactor"
,
]
)
summary
=
(
evaluated
.
groupby
([
"dataSplit"
,
"dataset"
,
"datasetLabel"
,
"check"
],
as_index
=
False
)
.
agg
(
cases
=
(
"scenario"
,
"size"
),
matches
=
(
"match"
,
lambda
values
:
(
values
==
"yes"
).
sum
()),
medianRegretRatio
=
(
"regretRatio"
,
"median"
),
p90RegretRatio
=
(
"regretRatio"
,
lambda
values
:
values
.
quantile
(
0.90
)),
medianSlowdownFactor
=
(
"slowdownFactor"
,
"median"
),
maxSlowdownFactor
=
(
"slowdownFactor"
,
"max"
),
)
.
sort_values
([
"dataSplit"
,
"datasetLabel"
,
"dataset"
,
"check"
])
)
summary
.
insert
(
6
,
"matchRate"
,
summary
[
"matches"
]
/
summary
[
"cases"
])
return
summary
def
main
()
->
None
:
"""Create compact CSV exports from the raw Java tables."""
args
=
parse_args
()
args
.
output_dir
.
mkdir
(
parents
=
True
,
exist_ok
=
True
)
synthetic
=
read_csv
(
args
.
input_dir
/
"synthetic_observations.csv"
)
buckets
=
read_csv
(
args
.
input_dir
/
"synthetic_bucket_summary.csv"
)
real
=
read_csv
(
args
.
input_dir
/
"real_citygml_observations.csv"
)
synthetic_compact
=
compact_observations
(
synthetic
)
rules
=
candidate_rules
(
buckets
)
real_compact
=
compact_observations
(
real
)
real_summary
=
real_dataset_summary
(
real
)
synthetic_compact
.
to_csv
(
args
.
output_dir
/
"synthetic_strategy_comparison.csv"
,
index
=
False
)
rules
.
to_csv
(
args
.
output_dir
/
"synthetic_candidate_rules.csv"
,
index
=
False
)
real_compact
.
to_csv
(
args
.
output_dir
/
"real_strategy_comparison.csv"
,
index
=
False
)
real_summary
.
to_csv
(
args
.
output_dir
/
"real_dataset_summary.csv"
,
index
=
False
)
if
__name__
==
"__main__"
:
main
()
CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/
bht
/TestCityGmlAABB.java
→
CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/
aabb
/TestCityGmlAABB.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.healing.
bht
;
package
de.hft.stuttgart.citydoctor2.healing.
aabb
;
import
de.hft.stuttgart.citydoctor2.datastructure.
bht
.*
;
import
de.hft.stuttgart.citydoctor2.datastructure.
aabb
.*
;
import
de.hft.stuttgart.citydoctor2.datastructure.*
;
import
de.hft.stuttgart.citydoctor2.datastructure.*
;
import
de.hft.stuttgart.citydoctor2.parser.*
;
import
de.hft.stuttgart.citydoctor2.parser.*
;
import
de.hft.stuttgart.citydoctor2.check.*
;
import
de.hft.stuttgart.citydoctor2.check.*
;
...
...
CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/
bht
/TestExample.java
→
CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/
aabb
/TestExample.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.healing.
bht
;
package
de.hft.stuttgart.citydoctor2.healing.
aabb
;
import
java.io.IOException
;
import
java.io.IOException
;
//import java.io.InputStream; //new
//import java.io.InputStream; //new
...
...
CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/
bht
/TestExampleAABB.java
→
CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/
aabb
/TestExampleAABB.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.healing.
bht
;
package
de.hft.stuttgart.citydoctor2.healing.
aabb
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertFalse
;
import
static
org
.
junit
.
Assert
.
assertFalse
;
...
...
CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/
bht
/TestModel.java
→
CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/
aabb
/TestModel.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.healing.
bht
;
package
de.hft.stuttgart.citydoctor2.healing.
aabb
;
/**
/**
* Represents a CityGML test model with metadata for easier handling in tests.
* Represents a CityGML test model with metadata for easier handling in tests.
...
...
CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/
bht
/TestPaths.java
→
CityDoctorParent/Extensions/CityDoctorHealer/src/test/java/de/hft/stuttgart/citydoctor2/healing/
aabb
/TestPaths.java
View file @
2c9eea60
package
de.hft.stuttgart.citydoctor2.healing.
bht
;
package
de.hft.stuttgart.citydoctor2.healing.
aabb
;
...
...
Prev
1
2
3
Next
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment