Project
Loading...
Searching...
No Matches
testTripletFitting.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
12#define BOOST_TEST_MODULE ITSMFTTrackingTripletFitting
13#include <boost/test/unit_test.hpp>
14
15#include <array>
16#include <chrono>
17#include <cmath>
18#include <cstring>
19#include <limits>
20
22
23using namespace o2::itsmft::tracking;
24
25namespace
26{
27
28constexpr double Radius = 50.;
29constexpr double TanLambda = 0.4;
30
31GlobalCovariance3F makeCovariance()
32{
33 // Positive definite, non-axis-aligned covariance in cm^2.
34 return {4.e-6f, 0.8e-6f, -0.4e-6f, 3.e-6f, 0.3e-6f, 5.e-6f};
35}
36
37GlobalMeasurement makeMeasurement(float x, float y, float z,
38 GlobalCovariance3F covariance = makeCovariance())
39{
40 GlobalMeasurement measurement{};
41 measurement.position = {x, y, z};
42 measurement.covariance = covariance;
43 return measurement;
44}
45
46std::array<GlobalMeasurement, 3> makeHelixMeasurements()
47{
48 const std::array<double, 3> angles{0.1, 0.16, 0.25};
49 std::array<GlobalMeasurement, 3> measurements{};
50 for (std::size_t i = 0; i < measurements.size(); ++i) {
51 measurements[i].position = {static_cast<float>(3. + Radius * std::cos(angles[i])),
52 static_cast<float>(-2. + Radius * std::sin(angles[i])),
53 static_cast<float>(1.5 + Radius * angles[i] * TanLambda)};
54 measurements[i].covariance = makeCovariance();
55 }
56 return measurements;
57}
58
59std::array<GlobalMeasurement, 4> makeAdjacentHelixMeasurements()
60{
61 const std::array<double, 4> angles{0.1, 0.16, 0.25, 0.33};
62 std::array<GlobalMeasurement, 4> measurements{};
63 for (std::size_t i = 0; i < measurements.size(); ++i) {
64 measurements[i].position = {static_cast<float>(3. + Radius * std::cos(angles[i])),
65 static_cast<float>(-2. + Radius * std::sin(angles[i])),
66 static_cast<float>(1.5 + Radius * angles[i] * TanLambda)};
67 measurements[i].covariance = makeCovariance();
68 }
69 return measurements;
70}
71
72std::array<TripletFitFactor, 2> fitAdjacentFactors(
73 const std::array<GlobalMeasurement, 4>& measurements)
74{
75 const std::array<GlobalMeasurement, 3> first{
76 measurements[0], measurements[1], measurements[2]};
77 const std::array<GlobalMeasurement, 3> second{
78 measurements[1], measurements[2], measurements[3]};
79 std::array<TripletFitFactor, 2> factors{};
80 BOOST_REQUIRE(makeTripletFitFactor(first, factors[0]));
81 BOOST_REQUIRE(makeTripletFitFactor(second, factors[1]));
82 return factors;
83}
84
85GlobalCovariance3F rotateCovarianceAroundZ(const GlobalCovariance3F& covariance,
86 double angle)
87{
88 const double cosine = std::cos(angle);
89 const double sine = std::sin(angle);
90 return {static_cast<float>(cosine * cosine * covariance.xx - 2. * sine * cosine * covariance.xy + sine * sine * covariance.yy),
91 static_cast<float>(sine * cosine * covariance.xx + (cosine * cosine - sine * sine) * covariance.xy -
92 sine * cosine * covariance.yy),
93 static_cast<float>(cosine * covariance.xz - sine * covariance.yz),
94 static_cast<float>(sine * sine * covariance.xx + 2. * sine * cosine * covariance.xy + cosine * cosine * covariance.yy),
95 static_cast<float>(sine * covariance.xz + cosine * covariance.yz),
96 covariance.zz};
97}
98
99void checkClose(double actual, double expected, double relativeTolerance, double absoluteTolerance = 0.)
100{
101 BOOST_CHECK_SMALL(actual - expected,
102 std::max(absoluteTolerance, relativeTolerance * std::max(std::abs(actual), std::abs(expected))));
103}
104
105double factorCovariance(const TripletFitFactor& factor,
106 const std::array<GlobalMeasurement, 3>& measurements,
107 bool leftTheta, bool rightTheta)
108{
109 double covariance = 0.;
110 for (std::size_t hit = 0; hit < measurements.size(); ++hit) {
111 const auto& left = leftTheta ? factor.h[hit].theta : factor.h[hit].phi;
112 const auto& right = rightTheta ? factor.h[hit].theta : factor.h[hit].phi;
113 const auto& v = measurements[hit].covariance;
114 covariance += left[0] * (v.xx * right[0] + v.xy * right[1] + v.xz * right[2]) +
115 left[1] * (v.xy * right[0] + v.yy * right[1] + v.yz * right[2]) +
116 left[2] * (v.xz * right[0] + v.yz * right[1] + v.zz * right[2]);
117 }
118 return covariance;
119}
120
121} // namespace
122
123BOOST_AUTO_TEST_CASE(ExactHelixProducesAConsistentFactor)
124{
125 const auto measurements = makeHelixMeasurements();
126 TripletFitFactor factor{};
127 BOOST_REQUIRE(makeTripletFitFactor(measurements, factor));
128 BOOST_REQUIRE(factor.isValid());
129 const double referenceCurvature = -static_cast<double>(factor.psi.phi) / factor.rho.phi;
130 const double expectedCurvature = (1. / Radius) / std::sqrt(1. + TanLambda * TanLambda);
131 checkClose(referenceCurvature, expectedCurvature, 4.e-4);
132 // Native float hit coordinates leave this residual after the otherwise
133 // double-precision geometry calculation.
134 BOOST_CHECK_SMALL(static_cast<double>(factor.psi.theta) +
135 static_cast<double>(factor.rho.theta) * referenceCurvature,
136 2.e-7);
137 BOOST_CHECK_GT(factorCovariance(factor, measurements, true, true), 0.);
138 BOOST_CHECK_NE(factorCovariance(factor, measurements, true, false), 0.);
139}
140
141BOOST_AUTO_TEST_CASE(AdjacentFactorsImplementEquation19ClosedForm)
142{
143 const GlobalCovariance3F exact{};
144 const std::array<GlobalMeasurement, 4> measurements{{
145 makeMeasurement(0.f, 0.f, 0.f, exact),
146 makeMeasurement(1.f, 0.f, 0.f, exact),
147 makeMeasurement(2.f, 0.f, 0.f, exact),
148 makeMeasurement(3.f, 0.f, 0.f, exact),
149 }};
150 std::array<TripletFitFactor, 2> factors{};
151 factors[0].psi = {1.f, 2.f};
152 factors[0].rho = {1.f, 1.f};
153 factors[1].psi = {3.f, 4.f};
154 factors[1].rho = {1.f, 1.f};
155
157 BOOST_REQUIRE(fitAdjacentTripletFactors(factors[0], factors[1], measurements, {4.f, 9.f}, result));
158 const double rhoKpsi = 1. / 4. + 2. / 4. + 3. / 9. + 4. / 9.;
159 const double rhoKrho = 1. / 4. + 1. / 4. + 1. / 9. + 1. / 9.;
160 const double psiKpsi = 1. / 4. + 4. / 4. + 9. / 9. + 16. / 9.;
161 checkClose(result.curvature, -rhoKpsi / rhoKrho, 2.e-6);
162 checkClose(result.curvatureVariance, 1. / rhoKrho, 2.e-6);
163 checkClose(result.chi2, psiKpsi - rhoKpsi * rhoKpsi / rhoKrho, 2.e-6);
164}
165
166BOOST_AUTO_TEST_CASE(AdjacentFactorsRetainSharedHitCrossCovariance)
167{
168 const GlobalCovariance3F exact{};
169 std::array<GlobalMeasurement, 4> measurements{{
170 makeMeasurement(0.f, 0.f, 0.f, exact),
171 makeMeasurement(1.f, 0.f, 0.f, {2.f, 0.5f, 0.f, 3.f, 0.f, 0.f}),
172 makeMeasurement(2.f, 0.f, 0.f, exact),
173 makeMeasurement(3.f, 0.f, 0.f, exact),
174 }};
175 std::array<TripletFitFactor, 2> factors{};
176 factors[0].rho.phi = 1.f;
177 factors[1].rho.phi = 1.f;
178 factors[0].h[1].theta = {1.f, 2.f, 0.f};
179 factors[0].h[1].phi = {-1.f, 1.f, 0.f};
180 factors[1].h[0].theta = {3.f, -2.f, 0.f};
181 factors[1].h[0].phi = {2.f, 4.f, 0.f};
182
183 AdjacentTripletFitResult correlated{};
184 BOOST_REQUIRE(fitAdjacentTripletFactors(factors[0], factors[1], measurements,
185 {100.f, 100.f}, correlated));
186
187 // Move the second factor's identical covariance contribution from shared
188 // hit 1 to private hit 3. Diagonal blocks stay equal; only H V H^T's
189 // cross-triplet block disappears.
190 auto independentFactors = factors;
191 auto independentMeasurements = measurements;
192 independentFactors[1].h[2] = independentFactors[1].h[0];
193 independentFactors[1].h[0] = {};
194 independentMeasurements[3].covariance = measurements[1].covariance;
195 AdjacentTripletFitResult independent{};
196 BOOST_REQUIRE(fitAdjacentTripletFactors(independentFactors[0], independentFactors[1],
197 independentMeasurements, {100.f, 100.f}, independent));
198 BOOST_CHECK_NE(correlated.curvatureVariance, independent.curvatureVariance);
199}
200
201BOOST_AUTO_TEST_CASE(AdjacentFactorsApplySpaceAngleMSGeometry)
202{
203 const GlobalCovariance3F exact{};
204 const std::array<GlobalMeasurement, 4> measurements{{
205 makeMeasurement(0.f, 0.f, 0.f, exact),
206 makeMeasurement(1.f, 0.f, 1.f, exact),
207 makeMeasurement(2.f, 0.f, 2.f, exact),
208 makeMeasurement(3.f, 0.f, 3.f, exact),
209 }};
210 std::array<TripletFitFactor, 2> factors{};
211 factors[0].rho = {1.f, 1.f};
212 factors[1].rho = {1.f, 1.f};
214 BOOST_REQUIRE(fitAdjacentTripletFactors(factors[0], factors[1], measurements, {4.f, 9.f}, result));
215 const double expectedPrecision = 1. / 4. + 1. / 8. + 1. / 9. + 1. / 18.;
216 checkClose(result.curvatureVariance, 1. / expectedPrecision, 2.e-6);
217}
218
219BOOST_AUTO_TEST_CASE(AdjacentExactHelixHasCommonCurvatureAndZeroQuality)
220{
221 const auto measurements = makeAdjacentHelixMeasurements();
222 const std::array<float, 2> angularVariance{1.e-8f, 2.e-8f};
223 const auto factors = fitAdjacentFactors(measurements);
225 BOOST_REQUIRE(fitAdjacentTripletFactors(factors[0], factors[1], measurements, angularVariance, result));
226 const double expectedCurvature = (1. / Radius) / std::sqrt(1. + TanLambda * TanLambda);
227 checkClose(result.curvature, expectedCurvature, 4.e-4);
228 // Native float measurements and persisted float factors leave only this
229 // numerical residue in an otherwise exactly common-curvature helix.
230 BOOST_CHECK_SMALL(result.chi2, 2.e-6f);
231 BOOST_CHECK_GT(result.curvatureVariance, 0.);
232}
233
234BOOST_AUTO_TEST_CASE(AdjacentFactorFitIsRotationInvariant)
235{
236 const auto original = makeAdjacentHelixMeasurements();
237 auto rotated = original;
238 const double angle = 0.73;
239 const double cosine = std::cos(angle);
240 const double sine = std::sin(angle);
241 for (auto& measurement : rotated) {
242 const double x = measurement.x;
243 const double y = measurement.y;
244 measurement.x = static_cast<float>(cosine * x - sine * y);
245 measurement.y = static_cast<float>(sine * x + cosine * y);
246 measurement.covariance = rotateCovarianceAroundZ(measurement.covariance, angle);
247 }
248 const std::array<float, 2> angularVariance{2.e-8f, 3.e-8f};
249 const auto originalFactors = fitAdjacentFactors(original);
250 const auto rotatedFactors = fitAdjacentFactors(rotated);
253 BOOST_REQUIRE(fitAdjacentTripletFactors(originalFactors[0], originalFactors[1], original, angularVariance, first));
254 BOOST_REQUIRE(fitAdjacentTripletFactors(rotatedFactors[0], rotatedFactors[1], rotated, angularVariance, second));
255 // Rotating and storing the coordinates and covariance back into floats
256 // limits the invariance of the derived Jacobian and covariance.
257 checkClose(second.curvature, first.curvature, 2.e-6);
258 checkClose(second.curvatureVariance, first.curvatureVariance, 5.e-2);
259 checkClose(second.chi2, first.chi2, 1.e-5, 5.e-7);
260}
261
262BOOST_AUTO_TEST_CASE(StraightTripletUsesTheRemovableZeroBendingLimit)
263{
264 const GlobalCovariance3F covariance{1.e-6f, 0.f, 0.f, 1.e-6f, 0.f, 1.e-6f};
265 const std::array<GlobalMeasurement, 3> measurements{{
266 makeMeasurement(1.f, 2.f, 3.f, covariance),
267 makeMeasurement(2.f, 2.f, 3.5f, covariance),
268 makeMeasurement(4.f, 2.f, 4.5f, covariance),
269 }};
270 TripletFitFactor factor{};
271 BOOST_REQUIRE(makeTripletFitFactor(measurements, factor));
272 BOOST_REQUIRE(factor.isValid());
273 BOOST_CHECK_SMALL(-static_cast<double>(factor.psi.phi) / factor.rho.phi, 1.e-15);
274}
275
276BOOST_AUTO_TEST_CASE(FactorConstructionGeometryFailureIsTransactional)
277{
278 TripletFitFactor sentinel{};
279 sentinel.psi = {1.f, 2.f};
280 sentinel.rho = {3.f, 4.f};
281 auto measurements = makeHelixMeasurements();
282 TripletFitFactor result = sentinel;
283
284 measurements[1].position = measurements[0].position;
285 BOOST_CHECK(!makeTripletFitFactor(measurements, result));
286 BOOST_CHECK_EQUAL(std::memcmp(&result, &sentinel, sizeof(result)), 0);
287}
288
289BOOST_AUTO_TEST_CASE(CharacterizeFactorConstructionHostCost)
290{
291 const auto measurements = makeHelixMeasurements();
292 constexpr int Repetitions = 20000;
293 double checksum = 0.;
294 const auto start = std::chrono::steady_clock::now();
295 for (int iteration = 0; iteration < Repetitions; ++iteration) {
296 TripletFitFactor factor{};
297 BOOST_REQUIRE(makeTripletFitFactor(measurements, factor));
298 checksum += factor.psi.theta + factor.psi.phi + factor.rho.theta + factor.rho.phi;
299 }
300 const auto elapsed = std::chrono::steady_clock::now() - start;
301 const double nanosecondsPerFit =
302 std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed).count() /
303 static_cast<double>(Repetitions);
304 BOOST_TEST_MESSAGE("triplet-factor construction host cost: " << nanosecondsPerFit << " ns/factor; checksum=" << checksum);
305 BOOST_CHECK_NE(checksum, 0.);
306}
307
308BOOST_AUTO_TEST_CASE(CharacterizeAdjacentFactorHostCost)
309{
310 const auto measurements = makeAdjacentHelixMeasurements();
311 const std::array<float, 2> angularVariance{2.e-7f, 3.e-7f};
312 const auto factors = fitAdjacentFactors(measurements);
313 constexpr int Repetitions = 20000;
314 double checksum = 0.;
315 const auto start = std::chrono::steady_clock::now();
316 for (int iteration = 0; iteration < Repetitions; ++iteration) {
318 BOOST_REQUIRE(fitAdjacentTripletFactors(factors[0], factors[1], measurements, angularVariance, result));
319 checksum += result.curvature + result.chi2;
320 }
321 const auto elapsed = std::chrono::steady_clock::now() - start;
322 const double nanosecondsPerFit =
323 std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed).count() /
324 static_cast<double>(Repetitions);
325 BOOST_TEST_MESSAGE("adjacent triplet-factor fit host cost: " << nanosecondsPerFit << " ns/fit; checksum=" << checksum);
326 BOOST_CHECK_GT(checksum, 0.);
327}
int32_t i
GLint GLenum GLint x
Definition glcorearb.h:403
GLuint64EXT * result
Definition glcorearb.h:5662
const GLdouble * v
Definition glcorearb.h:832
GLdouble GLdouble right
Definition glcorearb.h:4077
GLint first
Definition glcorearb.h:399
GLint y
Definition glcorearb.h:270
GLint left
Definition glcorearb.h:1979
GLfloat angle
Definition glcorearb.h:4071
GLuint start
Definition glcorearb.h:469
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
bool fitAdjacentTripletFactors(const TripletFitFactor &firstFactor, const TripletFitFactor &secondFactor, const std::array< GlobalMeasurement, 4 > &measurements, const std::array< float, 2 > &angularVariance, AdjacentTripletFitResult &result) noexcept
bool makeTripletFitFactor(const std::array< GlobalMeasurement, 3 > &measurements, TripletFitFactor &factor) noexcept
std::array< TripletHitJacobian, 3 > h
std::map< std::string, ID > expected
BOOST_AUTO_TEST_CASE(ExactHelixProducesAConsistentFactor)
BOOST_CHECK(tree)
BOOST_CHECK_EQUAL(triggersD.size(), triggers.size())