Skip to content

Commit

Permalink
feat: MultiTrajectory improvements (#1061)
Browse files Browse the repository at this point in the history
This does three things:
1. `MultiTrajectory` and it's internal column storage can be queried for
   their size and "cleared", meaning their logical size reduced to zero,
   while keeping their capacity
2. `ConstTrackStateProxy` is now (auto-)constructible from
   `TrackStateProxy`. This is necessary to enable interfaces for certain
   components to accept `ConstTrackStateProxy` and passing a non-const
   `TrackStateProxy`
3. Allows `MultiTrajectory::TrackStateProxy::copyFrom` to also copy
   regardless of underlying allocation. If this flag id set to `false`,
   the method will copy everyhing which has a valid index **potentially
   overwriting information in other track states via shared storage**
  • Loading branch information
paulgessinger committed Nov 11, 2021
1 parent a95b3f0 commit 7d0e02c
Show file tree
Hide file tree
Showing 2 changed files with 230 additions and 44 deletions.
186 changes: 142 additions & 44 deletions Core/include/Acts/EventData/MultiTrajectory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,23 @@ struct GrowableColumns {
/// Return the current allocated storage capacity
size_t capacity() const { return static_cast<size_t>(data.cols()); }

/// Return the size of the storage column
size_t size() const { return m_size; }

/// Resize the storage column, without changing the allocated capacity
/// @param size The new size of the storage
void resize(size_t size) {
if (size > m_size) {
addCol(size - m_size);
} else {
m_size = size;
}
}

/// Clear the storage of the storage column
/// Equivalent to ``resize(0)``
void clear() { resize(0); }

private:
Storage data;
size_t m_size{0};
Expand Down Expand Up @@ -160,6 +175,18 @@ class TrackStateProxy {
Eigen::Matrix<typename Projector::Scalar, Eigen::Dynamic, Eigen::Dynamic,
ProjectorFlags, M, eBoundSize>;

// Constructor and assignment operator to construct ReadOnly TrackStateProxy
// from ReadWrite (mutable -> const)
TrackStateProxy(const TrackStateProxy<M, false>& other)
: m_traj{other.m_traj}, m_istate{other.m_istate} {}

TrackStateProxy& operator=(const TrackStateProxy<M, false>& other) {
m_traj = other.m_traj;
m_istate = other.m_istate;

return *this;
}

/// Index within the trajectory.
/// @return the index
size_t index() const { return m_istate; }
Expand All @@ -184,58 +211,113 @@ class TrackStateProxy {
/// Copy the contents of another track state proxy into this one
/// @param other The other track state to copy from
/// @param mask An optional mask to determine what to copy from
/// @param onlyAllocated Whether to only copy allocated components
/// @note If the this track state proxy does not have compatible allocations
/// with the source track state proxy, an exception is thrown.
/// with the source track state proxy, and @p onlyAllocated is false,
/// an exception is thrown.
/// @note The mask parameter will not cause a copy of components that are
/// not allocated in the source track state proxy.
template <bool RO = ReadOnly, bool ReadOnlyOther,
typename = std::enable_if<!RO>>
void copyFrom(const TrackStateProxy<M, ReadOnlyOther>& other,
TrackStatePropMask mask = TrackStatePropMask::All) {
TrackStatePropMask mask = TrackStatePropMask::All,
bool onlyAllocated = true) {
using PM = TrackStatePropMask;
auto dest = getMask();
auto src = other.getMask() &
mask; // combine what we have with what we want to copy
if (static_cast<std::underlying_type_t<TrackStatePropMask>>((src ^ dest) &
src) != 0) {
throw std::runtime_error(
"Attempt track state copy with incompatible allocations");
}

// we're sure now this has correct allocations, so just copy
if (ACTS_CHECK_BIT(src, PM::Predicted)) {
predicted() = other.predicted();
predictedCovariance() = other.predictedCovariance();
}

if (ACTS_CHECK_BIT(src, PM::Filtered)) {
filtered() = other.filtered();
filteredCovariance() = other.filteredCovariance();
}

if (ACTS_CHECK_BIT(src, PM::Smoothed)) {
smoothed() = other.smoothed();
smoothedCovariance() = other.smoothedCovariance();
}

if (ACTS_CHECK_BIT(src, PM::Uncalibrated)) {
// need to do it this way since other might be nullptr
m_traj->m_sourceLinks[data().iuncalibrated] =
other.m_traj->m_sourceLinks[other.data().iuncalibrated];
}

if (ACTS_CHECK_BIT(src, PM::Jacobian)) {
jacobian() = other.jacobian();
}

if (ACTS_CHECK_BIT(src, PM::Calibrated)) {
// need to do it this way since other might be nullptr
m_traj->m_sourceLinks[data().icalibratedsourcelink] =
other.m_traj->m_sourceLinks[other.data().icalibratedsourcelink];
calibrated() = other.calibrated();
calibratedCovariance() = other.calibratedCovariance();
data().measdim = other.data().measdim;
setProjectorBitset(other.projectorBitset());
if (onlyAllocated) {
auto dest = getMask();
auto src = other.getMask() &
mask; // combine what we have with what we want to copy
if (static_cast<std::underlying_type_t<TrackStatePropMask>>((src ^ dest) &
src) != 0) {
throw std::runtime_error(
"Attempt track state copy with incompatible allocations");
}

// we're sure now this has correct allocations, so just copy
if (ACTS_CHECK_BIT(src, PM::Predicted)) {
predicted() = other.predicted();
predictedCovariance() = other.predictedCovariance();
}

if (ACTS_CHECK_BIT(src, PM::Filtered)) {
filtered() = other.filtered();
filteredCovariance() = other.filteredCovariance();
}

if (ACTS_CHECK_BIT(src, PM::Smoothed)) {
smoothed() = other.smoothed();
smoothedCovariance() = other.smoothedCovariance();
}

if (ACTS_CHECK_BIT(src, PM::Uncalibrated)) {
// need to do it this way since other might be nullptr
m_traj->m_sourceLinks[data().iuncalibrated] =
other.m_traj->m_sourceLinks[other.data().iuncalibrated];
}

if (ACTS_CHECK_BIT(src, PM::Jacobian)) {
jacobian() = other.jacobian();
}

if (ACTS_CHECK_BIT(src, PM::Calibrated)) {
// need to do it this way since other might be nullptr
m_traj->m_sourceLinks[data().icalibratedsourcelink] =
other.m_traj->m_sourceLinks[other.data().icalibratedsourcelink];
calibrated() = other.calibrated();
calibratedCovariance() = other.calibratedCovariance();
data().measdim = other.data().measdim;
setProjectorBitset(other.projectorBitset());
}
} else {
if (ACTS_CHECK_BIT(mask, PM::Predicted) &&
data().ipredicted != IndexData::kInvalid &&
other.data().ipredicted != IndexData::kInvalid) {
predicted() = other.predicted();
predictedCovariance() = other.predictedCovariance();
}

if (ACTS_CHECK_BIT(mask, PM::Filtered) &&
data().ifiltered != IndexData::kInvalid &&
other.data().ifiltered != IndexData::kInvalid) {
filtered() = other.filtered();
filteredCovariance() = other.filteredCovariance();
}

if (ACTS_CHECK_BIT(mask, PM::Smoothed) &&
data().ismoothed != IndexData::kInvalid &&
other.data().ismoothed != IndexData::kInvalid) {
smoothed() = other.smoothed();
smoothedCovariance() = other.smoothedCovariance();
}

if (ACTS_CHECK_BIT(mask, PM::Uncalibrated) &&
data().iuncalibrated != IndexData::kInvalid &&
other.data().iuncalibrated != IndexData::kInvalid) {
// need to do it this way since other might be nullptr
m_traj->m_sourceLinks[data().iuncalibrated] =
other.m_traj->m_sourceLinks[other.data().iuncalibrated];
}

if (ACTS_CHECK_BIT(mask, PM::Jacobian) &&
data().ijacobian != IndexData::kInvalid &&
other.data().ijacobian != IndexData::kInvalid) {
jacobian() = other.jacobian();
}

if (ACTS_CHECK_BIT(mask, PM::Calibrated) &&
data().icalibrated != IndexData::kInvalid &&
other.data().icalibrated != IndexData::kInvalid &&
data().icalibratedsourcelink != IndexData::kInvalid &&
other.data().icalibratedsourcelink != IndexData::kInvalid) {
// need to do it this way since other might be nullptr
m_traj->m_sourceLinks[data().icalibratedsourcelink] =
other.m_traj->m_sourceLinks[other.data().icalibratedsourcelink];
calibrated() = other.calibrated();
calibratedCovariance() = other.calibratedCovariance();
data().measdim = other.data().measdim;
setProjectorBitset(other.projectorBitset());
}
}

chi2() = other.chi2();
Expand Down Expand Up @@ -647,6 +729,22 @@ class MultiTrajectory {
template <typename F>
void applyBackwards(size_t iendpoint, F&& callable);

/// Clear the @c MultiTrajectory. Leaves the underlying storage untouched
void clear() {
m_index.clear();
m_params.clear();
m_cov.clear();
m_meas.clear();
m_measCov.clear();
m_jac.clear();
m_sourceLinks.clear();
m_projectors.clear();
m_referenceSurfaces.clear();
}

/// Returns the number of track states contained
size_t size() const { return m_index.size(); }

private:
/// index to map track states to the corresponding
std::vector<detail_lt::IndexData> m_index;
Expand Down
88 changes: 88 additions & 0 deletions Tests/UnitTests/Core/EventData/MultiTrajectoryTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,23 @@ BOOST_AUTO_TEST_CASE(Build) {
BOOST_CHECK_EQUAL_COLLECTIONS(act.begin(), act.end(), exp.begin(), exp.end());
}

BOOST_AUTO_TEST_CASE(Clear) {
constexpr TrackStatePropMask kMask = TrackStatePropMask::Predicted;
MultiTrajectory t;
BOOST_CHECK_EQUAL(t.size(), 0);

auto i0 = t.addTrackState(kMask);
// trajectory bifurcates here into multiple hypotheses
auto i1a = t.addTrackState(kMask, i0);
auto i1b = t.addTrackState(kMask, i0);
t.addTrackState(kMask, i1a);
t.addTrackState(kMask, i1b);

BOOST_CHECK_EQUAL(t.size(), 5);
t.clear();
BOOST_CHECK_EQUAL(t.size(), 0);
}

BOOST_AUTO_TEST_CASE(ApplyWithAbort) {
constexpr TrackStatePropMask kMask = TrackStatePropMask::Predicted;

Expand Down Expand Up @@ -781,4 +798,75 @@ BOOST_AUTO_TEST_CASE(TrackStateProxyCopy) {
&ts2.referenceSurface()); // always copied
}

BOOST_AUTO_TEST_CASE(TrackStateProxyCopyDiffMTJ) {
using PM = TrackStatePropMask;

std::array<PM, 6> values{PM::Predicted, PM::Filtered, PM::Smoothed,
PM::Jacobian, PM::Uncalibrated, PM::Calibrated};

MultiTrajectory mj;
MultiTrajectory mj2;
auto mkts = [&](PM mask) { return mj.getTrackState(mj.addTrackState(mask)); };
auto mkts2 = [&](PM mask) {
return mj2.getTrackState(mj2.addTrackState(mask));
};

// orthogonal ones
for (PM a : values) {
for (PM b : values) {
auto tsa = mkts(a);
auto tsb = mkts2(b);
// doesn't work
if (a != b) {
BOOST_CHECK_THROW(tsa.copyFrom(tsb), std::runtime_error);
BOOST_CHECK_THROW(tsb.copyFrom(tsa), std::runtime_error);
} else {
tsa.copyFrom(tsb);
tsb.copyFrom(tsa);
}
}
}

// make sure they are actually on different MultiTrajectories
BOOST_CHECK_EQUAL(mj.size(), 36);
BOOST_CHECK_EQUAL(mj2.size(), 36);

auto ts1 = mkts(PM::Filtered | PM::Predicted); // this has both
ts1.filtered().setRandom();
ts1.filteredCovariance().setRandom();
ts1.predicted().setRandom();
ts1.predictedCovariance().setRandom();

// ((src XOR dst) & src) == 0
auto ts2 = mkts2(PM::Predicted);
ts2.predicted().setRandom();
ts2.predictedCovariance().setRandom();

// they are different before
BOOST_CHECK(ts1.predicted() != ts2.predicted());
BOOST_CHECK(ts1.predictedCovariance() != ts2.predictedCovariance());

// ts1 -> ts2 fails
BOOST_CHECK_THROW(ts2.copyFrom(ts1), std::runtime_error);
BOOST_CHECK(ts1.predicted() != ts2.predicted());
BOOST_CHECK(ts1.predictedCovariance() != ts2.predictedCovariance());

// ts2 -> ts1 is ok
ts1.copyFrom(ts2);
BOOST_CHECK(ts1.predicted() == ts2.predicted());
BOOST_CHECK(ts1.predictedCovariance() == ts2.predictedCovariance());
}

BOOST_AUTO_TEST_CASE(ProxyAssignment) {
constexpr TrackStatePropMask kMask = TrackStatePropMask::Predicted;
MultiTrajectory t;
auto i0 = t.addTrackState(kMask);

MultiTrajectory::TrackStateProxy tp = t.getTrackState(i0); // mutable
MultiTrajectory::TrackStateProxy tp2{tp}; // mutable to mutable
MultiTrajectory::ConstTrackStateProxy tp3{tp}; // mutable to const
// const to mutable: this won't compile
// MultiTrajectory::TrackStateProxy tp4{tp3};
}

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit 7d0e02c

Please sign in to comment.