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

refactor!: move source link creation, track state creation, measurement selection into a single unit outside of the CKF #3825

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

goetzgaycken
Copy link
Contributor

Merge retrieval of source link ranges, measurement selection and
track state creation into one unit which the CKF interacts with
via a single delegate. The delegates for measurement selection
track state creation and calibration are removed from the CKF
options/extensions.

The original building blocks (SourceLink accessor, measurement selector,
and track state creator, or the combined measurement selector and track
state creator) can still be used, however require to setup an additional
helper object which combines these independent components. The
algorithmic code is unchanged.

As a prerequisite the track states are converted to bound states also on
empty sensitive surfaces.

This increases the computational effort slightly for empty, sensitive
surfaces, since the computation of bound states is slightly more
demanding than the computation of curvilinear states, but for complex
events like HL-LHC events the degradation was not measurable.

--- END COMMIT MESSAGE ---

Any further description goes here, @-mentions are ok here!

Copy link

coderabbitai bot commented Nov 7, 2024

Important

Review skipped

Auto reviews are limited to specific labels.

🏷️ Labels to auto review (1)
  • coderabbit

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot added Component - Core Affects the Core module Component - Examples Affects the Examples module Track Finding labels Nov 7, 2024
This allows to merge retrieval of source link ranges, measurement
selection and track state creation into one unit.

This increases the computational effort slightly for empty, sensitive
surfaces, since the computation of bound states is slightly more
demanding than the computation of curvilinear states, but for complex
events like HL-LHC events the degradation was not measurable.
@goetzgaycken goetzgaycken force-pushed the refactor_move_sl_creation_measurement_selection_to_single_unit branch from c1ce59a to d3a565e Compare November 7, 2024 13:20
@goetzgaycken
Copy link
Contributor Author

Copy link

github-actions bot commented Nov 7, 2024

📊: Physics performance monitoring for 2cc2ecc

Full contents

physmon summary

@goetzgaycken goetzgaycken force-pushed the refactor_move_sl_creation_measurement_selection_to_single_unit branch from d3a565e to b44b8fe Compare November 7, 2024 14:48
@goetzgaycken
Copy link
Contributor Author

changes d3a565e to b44b8fe:

  • fix doxygen template parameter tag

@goetzgaycken
Copy link
Contributor Author

Why should there be brackets arround slAccessorDelegate TrackFindingAlgorithm.cpp:365 (macos build complains):

364 | TrackStateCandidateCreatorType trackStateCreator{
365 |    slAccessorDelegate,
366 |    Acts::makeDelegate<&MeasurementCalibratorAdapter::calibrate>(calibrator),
367 |    Acts::makeDelegate<&MeasurementSelector::select>(measSel)};

Copy link

sonarcloud bot commented Nov 7, 2024

Comment on lines 364 to 367
TrackStateCandidateCreatorType trackStateCreator{
slAccessorDelegate,
Acts::makeDelegate<&MeasurementCalibratorAdapter::calibrate>(calibrator),
Acts::makeDelegate<&MeasurementSelector::select>(measSel)};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this pattern of deducing and then assigning the delegate types is a good idea.

The idea behind this connect API that I originally introduced is that you separate the signature definition from when you register a function.

Is this really so much worse than:

Suggested change
TrackStateCandidateCreatorType trackStateCreator{
slAccessorDelegate,
Acts::makeDelegate<&MeasurementCalibratorAdapter::calibrate>(calibrator),
Acts::makeDelegate<&MeasurementSelector::select>(measSel)};
TrackStateCandidateCreatorType trackStateCreator;
trackStateCreator.sourceLinkAccessor.connect<&IndexSourceLinkAccessor::range>(&slAccessor);
trackStateCreator.calibrator.connect<&MeasurementCalibratorAdapter::calibrate>(calibrator);
trackStateCreator.measurementSelector.connect<&MeasurementSelector::select>(measSel)};

? I my view, this is more legible and explicit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You create a not properly initialized object, and the compiler would not tell You if You missed something.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since these are supposed to be runtime configurable, at least optionally, I don't see this as an issue. What we do everywhere else is either introduce default functions (which you do) than can error on invocation, or when pushing the object to a consumer it can check if all of the delegates are connected.

Copy link
Contributor Author

@goetzgaycken goetzgaycken Nov 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a strong opinion. But if it is preferred I can remove these helper methods and the starting point would be an uninitialized object and it is up to the user to remember what needs to be connected.

Though I don't see it as an improvement to move something that could be caught during compile time to the runtime. But of course You are right that delegates could be fully configurable thus it often is a runtime problem anyways.

It also seems to simplify client code. There are a few cases like

Delgate delegate;
delegate.connect<&func>(obj):
Options option(delegate);

which just becomes

Options option(makeDelegate<&func>(obj));

Of course the first version could just be

Options option;
option.delegate.connect<&func>(obj):

But then the delegate has to get a default value also in cases in which there is no meaningful default value, and in which the default value is only short lived and in principle unused. The default value also could generate dead code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer this, yes. There's also the added benefit that explicitly writing the delegate being connected avoids any ordering confusion (although this is a compile error due to mismatching types).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand the argument about ordering confusion, either way You cannot make a "connected" delegate to a non existing object or function by accident, or at least I do not see how one snippet is more likely for errors than the other, except for uninitialized member delegates which are caught at runtime in one case and at compile time in the other.

But I see You prefer for some reason unused default values and or more boiler plate code.

secondOptions.targetSurface = m_cfg.reverseSearch ? nullptr : pSurface.get();
secondOptions.skipPrePropagationUpdate = true;
secondOptions.extendOrBranchTrajectory =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we discussed, we might want to roll this into the extentions struct.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the moment the extensions struct would only contain a delegate for the updater and branchStopper. Both are optional, aren't they? And in theory could be identical for the entire job. In practice e.g. the branchStopper might not be stateless (e.g. information whether it is the in-side-out or out-side-in pass). The extendOrBranchTrajectory has to know at least something about the current event, so cannot be stateless (not considering some thread local global variables ... )

It is not fully clear to me what the role of the extensions should be or become. At the moment the extensions seem to be a bit of an arbitrary subset of the options. But if it makes sense to collect all delegates in one struct and everything which is not a delegate stays in the parent struct then certainly the extendOrBranchTrajectory should be moved to the extensions.

There also was the question about the naming. Is this a good name ? Or is it misleading ? It does add either one or more new track states to the current branch (which still need some post processing), so it should do what the name suggests.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right that it really only pragmatically collects all delegates to the CKF as a grouped struct. The name comes from the delegates being "extension points" of the CKF, at least that's what I originally came up with.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concerning the name, I did not question the name "extensions", but the was wondering about "extendOrBranchTrajectory"


/// the stateCandidator to be used
/// @note will be set to a default trackStateCandidateCreator or the one
// provided via the extension
TrackStateCandidateCreator trackStateCandidateCreator;
TrajectoryExtender extendOrBranchTrajectory;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit inconsistent already in this actor but maybe we should rename all of these to m_*?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the moment none of the members in options or extensions has the "m_" prefix. In that sense adding an "m_" prefix would make it inconsistent.

Comment on lines 124 to 125
using SourceLinkRange = decltype(sourceLinkAccessor(surface));
std::optional<SourceLinkRange> slRange = sourceLinkAccessor(surface);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand how these two function calls can have different return types? This looks to me like you're unconditionally boxing SourceLinkRange into an extra std::optional only to unbox it right after. I'm probably missing something.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is copy&pasted from the original code in the CKF the optional lost its purpose. So should be removed. the sourceLinkAccessor returns a range not an optional.

// data which is to be filled by the next stage e.g. the
// CombinatorialKalmanFilter.
template <typename source_link_iterator_t, typename track_container_t>
struct TrackStateCandidateCreatorImpl
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the adapter class that you mentioned, right @goetzgaycken?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the class which got the original code from the CKF. The code is just moved over.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the class should be renamed to fit the delegate name. In the original implementation in which the hierarchy was inverted this was the base class, and then this name made more sense.

// CAREFUL! This trackstate has a previous index that is not in this
// MultiTrajectory Visiting brackwards from this track state will
// fail!
auto ts = bufferTrajectory.makeTrackState(mask, prevTip);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only remaining reason for this bufferTrajectory is to allow the use of the previous measurement selector after calibration, which requires track states, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes exactly. The buffer trajectory could also become a member. Then the interfaces including the new interface of extendOrBranchTrajectory could be simplified. But the object would gain a state.

};
};

/// @brief Base class for the ComposableTrackStateCreator which simply delegates its tasks
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now derived, not base anymore, isn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exactly. Thanks!


SourceLinkAccessor sourceLinkAccessor;

const track_state_creator_derived_t& derived() const {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be private maybe.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure whether that is something that needs to be hidden from the public, but it is presumably also not really necessary to keep it public:

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO it is always a good idea to keep the public surface as small as possible. I don't think this is useful to the user?

options.sourceLinkAccessor.connect<&Fixture::TestSourceLinkAccessor::range>(
&slAccessor);

auto trackStateCreator = makeTrackStateCreator(slAccessor, f.measSel);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this used anywhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that is the delegate for extendOrBranchTrajectory. Maybe I should rename it ?

@andiwand andiwand changed the title refactor: move source link creation, track state creation, measurement selection into a single unit outside of the CKF refactor!: move source link creation, track state creation, measurement selection into a single unit outside of the CKF Nov 13, 2024
@paulgessinger
Copy link
Member

Couple more thoughts on this:

I think going with a single level of composition would be clearer. Currently there's the base composable class that connects to the previous SourceLinkAccessor, and then a second layer which accepts the other original delegates.

A single composable struct that by itself has the three (?) original delegates including the SourceLinkAccessor would be sufficient if we drop the compatibility with the intermediate track state creator style delegate.

What do we need this second layer for after this change? I think having one layer of adapter to the fully separate makes sense, but the surface area of the track state creator seems small enough to me to remove that altogether.

@goetzgaycken
Copy link
Contributor Author

It is only needed for the transition period. In my opinion it would be better to first introduce the new api and then merge on the athena side the track state creator and source link accessor. The amount of code duplication when introducing different helper objects for the two cases is small, though the simplification is also not significant. Therefore I would rather not duplicate the code, and rather remove the unnecessary code and layers later when they are not needed anymore. But I do not have a strong opinion.

@andiwand
Copy link
Contributor

The question for me would be, do we need a transition period? The change is breaking either way no? Touching this twice means we have to go through two breaking cycles.

@goetzgaycken
Copy link
Contributor Author

The question for me would be, do we need a transition period? The change is breaking either way no? Touching this twice means we have to go through two breaking cycles.

It is certainly possible to merge at the same time on the athena side the source link accessor and track state creator. I don't have any strong argument to motivate why it is better to break this into smaller steps.

Comment on lines 34 to 67
namespace detail {
// helper method to deduce the return type and arguments of the member function
template <auto memberfunc_ptr, typename return_t, typename instance_t,
typename... Args>
inline auto makeDelegate(
[[maybe_unused]] return_t (instance_t::*memberfunc)(Args...) const,
const instance_t& instance) {
Delegate<return_t(Args...)> delegate;
delegate.template connect<memberfunc_ptr>(&instance);
return delegate;
}
// helper method to deduce the return type and arguments of the function
template <auto func_ptr, typename return_t, typename... Args>
inline auto makeDelegate([[maybe_unused]] return_t (*func)(Args...)) {
Delegate<return_t(Args...)> delegate;
delegate.template connect<func_ptr>();
return delegate;
}
} // namespace detail

/// @brief convenience method to create and connect a delegate.
/// @tparam memberfunc_ptr a pointer to a member function of the class instance_t
/// @param instance an instance which outlives this delegate.
template <auto memberfunc_ptr, typename instance_t>
inline auto makeDelegate(const instance_t& instance) {
return detail::makeDelegate<memberfunc_ptr>(memberfunc_ptr, instance);
}

/// @brief convenience method to create and connect a delegate.
/// @tparam func_ptr a pointer to a function
template <auto func_ptr>
inline auto makeDelegate() {
return detail::makeDelegate<func_ptr>(func_ptr);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems a bit generic for a header called ComposableTrackStateCreator.hpp. Either this should be a utility placed in the delegate header or dropped in favor of doing this by hand.

I think the deduction should also work without a function wrapper?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • I agree that these helper functions would be better located somewhere else.
  • I haven't found a solution without the unused argument:
// 1:
 template <return_t(*T_Func)(Args...), typename return_t, typename... Args>  auto makeDelegate();  // does not compile
 
// 2:
 template <auto func_ptr, typename return_t, typename... Args>
 constexpr auto makeDelegate2([[maybe_unused]] return_t (*func)(Args...) =func_ptr) ;
 ... {
   auto a = makeDelegate2( &func ) ; // deduction failed
 }

/// stateCandidateCreator
/// @note if the number of states exceeds this number dynamic memory allocation will occur.
/// the number is chosen to yield a container size of 64 bytes.
static constexpr std::size_t s_maxBranchesPerSurface = 10;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and the CkfTypes should not really be in this header. In principle this header is optional but it would definitions used by the CKF. But where to put it ?
If extendOrBranchTrajectory becomes an extension than it could go to the extensions header.

@goetzgaycken goetzgaycken force-pushed the refactor_move_sl_creation_measurement_selection_to_single_unit branch from b44b8fe to b00f570 Compare November 15, 2024 09:39
Merge retrieval of source link ranges, measurement selection and
track state creation into one unit which the CKF interacts with
via a single delegate. The delegates for measurement selection
track state creation and calibration are removed from the CKF
options/extensions.

The original building blocks (SourceLink accessor, measurement selector,
and track state creator, or the combined measurement selector and track
state creator) can still be used, however require to setup an additional
helper object which combines these independent components. The
algorithmic code is unchanged.
@goetzgaycken goetzgaycken force-pushed the refactor_move_sl_creation_measurement_selection_to_single_unit branch from b00f570 to 2cc2ecc Compare November 15, 2024 10:13
@goetzgaycken
Copy link
Contributor Author

Changes from b44b8fe to b00f570 :

  • moved the extendOrBranchTrajectory delegate to the extensions. This improves the structure and simplifies the code a bit since now all "extensions" are copied to the actor with a single statement.
  • also moved some general definitions to the new extensions header (BranchVector, BoundState). they are needed for the signature of extendOrBranchTrajectory. The location is not necessarily ideal.
  • declared "derived" private. Though, "protected" might be a better choice. But likely nobody is going to extend of the relevant classes.
  • removed the helpers to create connected delegates, since this was not liked. I still don't think the removal is an improvement. For example when I adjusted athena for this revert I immediately made a copy & paste mistake and assigned the values to the wrong parent object and found the problem with the debugger, rather then by a complaining compiler.
  • removed unnecessary optional to store the result from the source link accessor which was just copied from the original code, but lost its purpose in the new context.

changes from b00f570 to 2cc2ecc:

  • compile fix missing from the above update.

I did not remove the adapter to support the components of the version 2 interface, which is currently used in ATLAS. I agree that there is no good reason to keep that around for long, but I am not sure whether it is a good strategy to make the interface change on the Acts side, and directly migrate on the ATLAS side to the new interface. The changes are not super complicated but also not trivial. Minimal changes to support the new interface on the athena side gitlab:EliminateSourceLinkAccessor_main~1, direct support of the new interface gitlab:EliminateSourceLinkAccessor_main,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Component - Core Affects the Core module Component - Examples Affects the Examples module Track Finding
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants