Skip to content

refactor(mab): eliminate code duplication using policy-based design#34

Open
lv416e wants to merge 26 commits intomainfrom
refactor/mab-selector-deduplication
Open

refactor(mab): eliminate code duplication using policy-based design#34
lv416e wants to merge 26 commits intomainfrom
refactor/mab-selector-deduplication

Conversation

@lv416e
Copy link
Owner

@lv416e lv416e commented Oct 19, 2025

Summary

Resolves #32

Eliminates ~270 lines of duplicated code between AdaptiveOperatorSelector and AdaptiveLocalSearchSelector using modern C++23 policy-based design with operator traits.

Changes

1. Operator Traits (✨ new)

  • CrossoverOperatorTraits<Problem>: Defines crossover operator interface
  • LocalSearchOperatorTraits<Problem>: Defines local search operator interface
  • Each trait provides wrap_operator() with C++20 requires clauses for type safety

2. Unified Implementation (♻️ refactor)

  • AdaptiveSelector<SchedulerType, Problem, Traits>: Single template class for both selector types
  • Variadic templates handle different operator signatures (crossover: 3 args, local search: 1 arg) without code duplication
  • C++20 requires clauses ensure apply_crossover() is only available for CrossoverOperatorTraits and apply_local_search() for LocalSearchOperatorTraits
  • Complete docstring coverage for improved maintainability
  • Thread-safety warnings preserved from original implementations

3. API Compatibility (✅ maintained)

  • AdaptiveOperatorSelector → type alias to AdaptiveSelector with CrossoverOperatorTraits
  • AdaptiveLocalSearchSelector → type alias to AdaptiveSelector with LocalSearchOperatorTraits
  • All convenience aliases preserved (UCB*, Thompson*)
  • Zero breaking changes - existing code works without modification

Technical Highlights

  • C++23 features: requires clauses, variadic templates, perfect forwarding
  • Type safety: Compile-time enforcement via C++20 concepts in traits
  • Zero overhead: All abstractions resolve at compile time
  • Extensibility: Adding a 3rd selector type now only requires defining new traits

Impact

Metric Before After Improvement
Lines of code ~320 ~50 -270 lines (-84%)
Duplicated logic ~110 lines × 2 0 100% deduplication
Type aliases 4 6 Maintained + improved docs
API compatibility N/A 100% No breaking changes

Commit Structure

This PR contains 3 atomic commits for easy review:

  1. feat(mab): add operator traits for adaptive selectors

    • Foundation: traits with type definitions and concept-constrained wrap_operator()
  2. refactor(mab): extract unified implementation for adaptive selectors

    • Core refactoring: single AdaptiveSelector template eliminates duplication
  3. refactor(mab): replace selector implementations with type aliases

    • API preservation: ~320 lines replaced with ~50 lines of type aliases

Test Plan

  • ✅ API compatibility verified in test_mab_local_search.cpp
  • ✅ All selector APIs unchanged (add_operator, apply_*, report_*, etc.)
  • ✅ Concepts properly constrain operator types
  • ✅ Existing test code compiles without modification

Future Work

This refactoring enables:

🔗 Related: #32, #29, #33

Summary by CodeRabbit

  • New Features

    • Unified adaptive selector that handles both crossover and local-search operators via a single trait-driven interface.
    • New convenience selector variants for common scheduling strategies.
  • Improvements

    • Trait-based operator wrapping and registration for more flexible operator types.
    • Stronger validation, clearer trait-aware error messages, and a single unified operator-application flow.
  • Refactor

    • Consolidated operator handling into one unified design while preserving public compatibility.

Introduce CrossoverOperatorTraits and LocalSearchOperatorTraits to enable
policy-based design for adaptive operator selection. These traits encapsulate
type differences between crossover and local search operators, providing:

- Type definitions (ResultType, OperatorFn)
- Concept-constrained wrap_operator() methods
- Type-safe operator wrapping with C++20 concepts

This is the foundation for eliminating ~110 lines of duplicated code between
AdaptiveOperatorSelector and AdaptiveLocalSearchSelector.

Related: #32
Introduce AdaptiveSelector<SchedulerType, Problem, Traits> template class
that provides a single, unified implementation for both crossover and local
search operator selection. Key features:

- Variadic templates handle different operator signatures (crossover: 3 args,
  local search: 1 arg) without code duplication
- C++20 requires clauses ensure apply_crossover() is only available for
  CrossoverOperatorTraits and apply_local_search() for LocalSearchOperatorTraits
- Complete docstring coverage for improved maintainability
- Thread-safety warnings preserved from original implementations

This eliminates the majority of the ~110 lines of duplicated code identified
in Issue #32. The existing AdaptiveOperatorSelector and
AdaptiveLocalSearchSelector classes will be replaced with type aliases in the
next commit to maintain full API compatibility.

Related: #32
Replace the original AdaptiveOperatorSelector and AdaptiveLocalSearchSelector
class implementations (~320 lines) with concise type aliases (~50 lines) that
instantiate the unified AdaptiveSelector template with appropriate traits.

Changes:
- AdaptiveOperatorSelector -> alias to AdaptiveSelector with CrossoverOperatorTraits
- AdaptiveLocalSearchSelector -> alias to AdaptiveSelector with LocalSearchOperatorTraits
- Maintain all convenience aliases (UCB*, Thompson*)
- 100% API compatibility - existing code requires zero changes

Impact:
- Eliminated ~270 lines of duplicated code
- Improved maintainability: single source of truth for selector logic
- Preserved full backward compatibility
- Enhanced type safety through C++20 concepts in traits

Related: #32
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @lv416e, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly refactors the adaptive operator and local search selectors by adopting a modern C++23 policy-based design. The core change involves introducing operator traits and a unified AdaptiveSelector template class, which effectively eliminates substantial code duplication between the previously separate selector implementations. This refactoring enhances maintainability, type safety through C++20 concepts, and extensibility, all while ensuring zero breaking changes to the existing API.

Highlights

  • Policy-Based Design: Implemented C++23 policy-based design to eliminate code duplication between adaptive operator and local search selectors.
  • Operator Traits: Introduced CrossoverOperatorTraits and LocalSearchOperatorTraits to define operator interfaces and provide type-safe wrapping functions for different operator types.
  • Unified Selector: Created a single AdaptiveSelector template class that unifies the logic for both crossover and local search operators, leveraging variadic templates and C++20 requires clauses for type safety and flexibility.
  • API Compatibility: Maintained full backward compatibility by providing type aliases for the original AdaptiveOperatorSelector and AdaptiveLocalSearchSelector classes, ensuring zero breaking changes.
  • Code Reduction: Achieved a significant reduction of approximately 270 lines of code and 100% deduplication of previously shared logic.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link

coderabbitai bot commented Oct 19, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Replaced two nearly-identical selectors with a single trait-driven template AdaptiveSelector<SchedulerType, Problem, Traits>, added CrossoverOperatorTraits and LocalSearchOperatorTraits, introduced WrappableBy concept, unified operator storage/application, preserved compatibility aliases, and switched messaging to std::format.

Changes

Cohort / File(s) Summary
MAB Scheduler refactor
include/evolab/schedulers/mab.hpp
Introduced CrossoverOperatorTraits<Problem> and LocalSearchOperatorTraits<Problem>, added WrappableBy<Op,Tr> concept, replaced AdaptiveOperatorSelector / AdaptiveLocalSearchSelector with AdaptiveSelector<SchedulerType,Problem,Traits>. Unified operator storage (OperatorFn), added wrap_operator, apply_operator_impl, trait-constrained apply_crossover / apply_local_search, improved validation/error messages, and added backwards-compatible type aliases (AdaptiveOperatorSelector, UCBOperatorSelector, ThompsonOperatorSelector, AdaptiveLocalSearchSelector, UCBLocalSearchSelector, ThompsonLocalSearchSelector). Also added <format> usage for messaging.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Selector as AdaptiveSelector<Scheduler,Problem,Traits>
    participant Traits
    participant Scheduler
    participant OperatorFn
    participant Stats

    Client->>Selector: add_operator(op, name)
    Selector->>Traits: wrap_operator(op)
    Traits-->>Selector: OperatorFn
    Selector->>Stats: create entry

    Client->>Selector: apply_crossover(...) / apply_local_search(...)
    Selector->>Scheduler: select_operator()
    Scheduler-->>Selector: index
    Selector->>OperatorFn: invoke via apply_operator_impl(...)
    OperatorFn-->>Selector: result
    Selector->>Stats: record execution & improvement
    Selector-->>Client: return result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐇 I stitched two selectors into one bright seam,

Traits tuck functions in, precise as a dream.
I wrap and time, pick which hop to try,
Fewer repeats — I nibble a carrot, sigh.
🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 61.11% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "refactor(mab): eliminate code duplication using policy-based design" accurately and concisely summarizes the main change. It is specific enough that a teammate scanning history would understand the primary refactoring approach (policy-based design) and its purpose (eliminating duplication). The title directly references the core modification without unnecessary noise.
Linked Issues Check ✅ Passed The PR successfully meets all acceptance criteria from issue #32. While the implementation uses policy-based design with traits rather than the suggested templated base class or CRTP, it achieves the same objectives: common functionality is unified in AdaptiveSelector with trait parameters, both selector types reuse this implementation via type aliases, the public API remains unchanged with full backward compatibility, and code duplication is dramatically reduced from ~320 to ~50 lines (−84%), far exceeding the <20 lines per class requirement. The PR objectives confirm that existing tests compile unchanged, fulfilling all stated criteria.
Out of Scope Changes Check ✅ Passed All changes in the PR are directly scoped to achieving the refactoring objective from issue #32. The modifications include adding traits (CrossoverOperatorTraits, LocalSearchOperatorTraits), the WrappableBy concept, the unified AdaptiveSelector implementation, backward-compatible type aliases, refined error handling tied to trait-aware messaging, and the header inclusion for improved output formatting. Each modification directly supports the goal of eliminating code duplication through policy-based design without introducing unrelated functionality.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/mab-selector-deduplication

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

gemini-code-assist[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

Add const qualifier to lambda call operators in both CrossoverOperatorTraits
and LocalSearchOperatorTraits. The operator concepts require const methods
(op.cross and op.improve), so the lambdas do not modify captured state and
their call operators should be const.

This change improves const-correctness and clearly expresses intent.

Addresses: Code review feedback from Gemini
…support

Replace std::function with std::move_only_function in operator traits to
support move-only operators. std::function requires CopyConstructible
callables, which prevents storing move-only operator types.

Benefits:
- Supports move-only operators (e.g., unique_ptr-captured state)
- Aligns with C++23 best practices
- Zero overhead for move-only types
- Maintains const-correctness with 'const' qualified signatures

This change is non-breaking as existing copyable operators continue to work.

Addresses: Code review feedback from CodeRabbit
…er diagnostics

Add explicit concept constraint to add_operator method to fail fast with
clear compile-time errors when an invalid operator type is passed. The
constraint verifies that wrap_operator() can convert the operator to the
expected OperatorFn type.

Benefits:
- Clearer compiler diagnostics for invalid operator types
- Fails at template instantiation rather than deep in SFINAE
- More explicit contract for users of the API

Addresses: Code review feedback from CodeRabbit
Add validation to reject NaN and infinite improvement values, preventing
corruption of MAB learning statistics. Non-finite values can poison the
reward calculations and lead to incorrect operator selection.

The validation throws std::invalid_argument with a clear error message
when a non-finite value is provided, allowing the caller to detect and
handle the issue early.

Addresses: Code review feedback from CodeRabbit
coderabbitai[bot]

This comment was marked as resolved.

Add explicit `-isystem` flag to all CMake presets to point to the SDK's C++
standard library headers. This fixes build failures on macOS where the compiler
cannot find standard library headers like <chrono>, <algorithm>, etc.

The issue occurs because macOS Command Line Tools now place C++ headers in
the SDK directory, but the compiler's default search paths point to the old
location. Adding the explicit include path ensures compatibility across
different macOS and Xcode versions.

Affected presets: default, debug, release-common, debug-asan, debug-ubsan,
debug-tsan, debug-msan

Fixes compilation errors like:
  fatal error: 'chrono' file not found
  fatal error: 'algorithm' file not found
Revert from std::move_only_function back to std::function in both
CrossoverOperatorTraits and LocalSearchOperatorTraits. While
std::move_only_function is a C++23 feature that enables move-only operator
support, it is not yet implemented in AppleClang 17.0.0.

This change prioritizes compiler compatibility over the move-only feature.
The move-only function support can be re-enabled once AppleClang implements
this C++23 feature, or users can switch to a different compiler (GCC 12+ or
Clang 16+ with libc++) that supports it.

Also removes the const qualifier from lambda call operators, as std::function
doesn't require it (unlike move_only_function const).

Fixes compilation errors:
  error: no template named 'move_only_function' in namespace 'std'

Related: Previous commits migrating to move_only_function
gemini-code-assist[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

Tighten misuse detection by throwing immediately when report_fitness_improvement
is called without a pending apply_* operation, rather than silently no-oping.
This fail-fast behavior helps catch programming errors early.

Also add finite value validation to report_fitness_change to ensure both
old_fitness and new_fitness are valid before calculating improvement.

Changes:
- report_fitness_improvement: Invert condition to throw std::logic_error
  when called without tracking_improvement_ or valid current_selection_
- report_fitness_change: Add std::invalid_argument check for NaN/Inf inputs

Addresses: CodeRabbit and Gemini review feedback
Remove the hardcoded `-isystem /Library/Developer/CommandLineTools/SDKs/
MacOSX.sdk/usr/include/c++/v1` flag from all CMake presets. This path is
specific to macOS systems and breaks portability for Linux, Windows, and
alternative macOS configurations.

The AppleClang toolchain already uses libc++ by default and includes the
correct system paths automatically. The explicit -isystem flag was
masking warnings from the standard library and causing build failures on
non-macOS systems.

If macOS-specific include issues arise, they should be handled through:
- CMakeUserPresets.json (for local-only workarounds)
- CMAKE_OSX_SYSROOT (for portable macOS SDK configuration)
- Conditional flags based on platform detection

Affected presets: default, debug, release-common, debug-asan, debug-ubsan,
debug-tsan, debug-msan

Addresses: CodeRabbit and Gemini critical review feedback on portability
Add a named concept WrappableBy<Op, Tr> to clarify the constraint on
add_operator's template parameter. This makes the intent more explicit and
improves code readability compared to the inline requires-expression.

The concept validates that an operator type can be wrapped by the traits'
wrap_operator function and produces a compatible OperatorFn type.

Before:
  template <typename OpType>
    requires requires(OpType&& o) {
      { Traits::template wrap_operator<OpType>(...) }
        -> std::convertible_to<typename Traits::OperatorFn>;
    }

After:
  template <typename OpType>
    requires WrappableBy<OpType, Traits>

Addresses: Gemini review feedback on code clarity
gemini-code-assist[bot]

This comment was marked as resolved.

Remove redundant `current_selection_ < 0` check from report_fitness_improvement.
The `tracking_improvement_` flag is the canonical state indicator:
- Set to true only after successful apply_* operation (when current_selection_
  is also set to a valid index)
- Set back to false after report_fitness_improvement is called

Therefore, `!tracking_improvement_` is sufficient to detect invalid calls,
making the `current_selection_ < 0` check redundant.

Addresses: Gemini code review feedback on logic simplification
gemini-code-assist[bot]

This comment was marked as resolved.

…essages

Replace all std::stringstream usages with std::format in error message
construction for better readability, type safety, and performance. This
change leverages modern C++20 features already in use throughout the project.

Changes:
- Add #include <format> and remove #include <sstream>
- Replace 5 error message constructions in AdaptiveSelector:
  * "Cannot apply {} operator" in apply_operator_impl
  * "apply method called again" in apply_operator_impl
  * "Selected {} operator index {} is out of bounds" in apply_operator_impl
  * "AdaptiveSelector for {} must be configured" in constructor
  * "Cannot add more {} operators" in add_operator

Benefits:
- More concise and readable error message formatting
- Better compile-time type safety with std::format
- Consistent with modern C++ best practices
- Potentially better runtime performance

Addresses: Gemini code review feedback on modernizing error messages
gemini-code-assist[bot]

This comment was marked as resolved.

lv416e and others added 4 commits October 20, 2025 17:25
This prevents accidental ignoring of crossover results, which could
lead to logic bugs where offspring are generated but not used.

Addresses code review feedback from Gemini Code Assistant.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This prevents accidental ignoring of fitness values returned by local
search operators, which could lead to logic bugs where local search
is executed but the result is not properly tracked.

Addresses code review feedback from Gemini Code Assistant.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This prevents accidental ignoring of operator statistics and names,
ensuring that calls to these methods are intentional and the returned
data is used for its intended purpose.

Addresses code review feedback from Gemini Code Assistant.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This prevents accidental ignoring of operator state information
(count, selection, improvement, execution time), ensuring that
calls to these methods are intentional and the returned data
is used for monitoring and decision-making.

Addresses code review feedback from Gemini Code Assistant.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
gemini-code-assist[bot]

This comment was marked as resolved.

…pport

Mark the LocalSearchOperatorTraits lambda as mutable to prepare for
future support of stateful local search operators like Tabu Search
and Simulated Annealing variants.

Added comprehensive TODO comment outlining the required steps:
1. Relax core::LocalSearchOperator concept to accept non-const methods
2. Update operator implementations to support mutable state

This change maintains current functionality while enabling future
extensibility for advanced algorithms that maintain internal state.

Addresses code review feedback from Gemini Code Assistant.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
coderabbitai[bot]

This comment was marked as resolved.

gemini-code-assist[bot]

This comment was marked as resolved.

lv416e and others added 4 commits October 20, 2025 22:41
Include the actual number of operators added in the error message
to help users diagnose the problem more easily, even though it will
be 0 when operators_.empty() is true.

Addresses code review feedback from Gemini Code Assistant.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Change "Inf" to "Infinity" and add "number" for better clarity
in the error message when an invalid improvement value is provided.

Addresses code review feedback from Gemini Code Assistant.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Replace generic "apply_* operation" with specific method names
"apply_crossover or apply_local_search" to make debugging easier
for users when report_fitness_improvement is called incorrectly.

Addresses code review feedback from Gemini Code Assistant.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Change "Inf" to "Infinity" and add "numbers" for better clarity
in the error message when invalid fitness values are provided.

Addresses code review feedback from Gemini Code Assistant.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
coderabbitai[bot]

This comment was marked as resolved.

lv416e and others added 3 commits October 20, 2025 23:39
Add const qualifier to lambda's call operator to clarify intent
and enforce immutability, as the captured operator doesn't need
modification.

Addresses code review feedback from CodeRabbit.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Remove redundant operators_.size() from error message since
it's always 0 when operators_.empty() is true.

Addresses code review feedback from CodeRabbit.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Remove const qualifier from lambda that was added in previous commit
(6e7d33a) as it causes compilation errors. While the const qualifier
is theoretically correct per CodeRabbit's review, it's not compatible
with the current compiler/environment setup.

The lambda captures the operator by value, so immutability is still
maintained implicitly without the explicit const qualifier.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
include/evolab/schedulers/mab.hpp (2)

104-107: Consider marking the lambda as const for correctness.

The lambda doesn't modify its captured state, and the CrossoverOperator concept requires op.cross to be const. Marking the lambda's call operator as const would better express intent.

Based on past review comments, apply this diff:

-        return [op = std::forward<OpType>(op)](const Problem& problem, const GenomeT& parent1,
-                                               const GenomeT& parent2, std::mt19937& rng) {
+        return [op = std::forward<OpType>(op)](const Problem& problem, const GenomeT& parent1,
+                                               const GenomeT& parent2, std::mt19937& rng) const {
             return op.cross(problem, parent1, parent2, rng);
         };

443-461: Consider adding [[nodiscard]] to operator application methods.

The return values from apply_crossover (offspring genomes) and apply_local_search (fitness) are critical results that shouldn't be ignored. Adding [[nodiscard]] would help prevent accidental misuse.

Based on past review comments:

-    [[nodiscard]] auto apply_crossover(const Problem& problem, const GenomeT& parent1,
+    [[nodiscard]] auto apply_crossover(const Problem& problem, const GenomeT& parent1,
                                        const GenomeT& parent2, std::mt19937& rng)
         requires std::same_as<Traits, CrossoverOperatorTraits<Problem>>

-    [[nodiscard]] auto apply_local_search(const Problem& problem, GenomeT& genome,
+    [[nodiscard]] auto apply_local_search(const Problem& problem, GenomeT& genome,
                                           std::mt19937& rng)
         requires std::same_as<Traits, LocalSearchOperatorTraits<Problem>>

Note: The code already has [[nodiscard]] - the diff shows no change is needed. I see these are already marked correctly!

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4c03b4e and 2b20e28.

📒 Files selected for processing (1)
  • include/evolab/schedulers/mab.hpp (6 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build-test (windows-latest, off)
  • GitHub Check: build-test (ubuntu-latest, off)
  • GitHub Check: build-test (ubuntu-latest, on)
🔇 Additional comments (6)
include/evolab/schedulers/mab.hpp (6)

111-145: LGTM! Well-designed for future extensibility.

The mutable lambda marking and comprehensive TODO comment demonstrate thoughtful planning for stateful local search operators (e.g., Tabu Search). The design correctly anticipates future requirements while maintaining current functionality.


303-315: Excellent concept extraction!

The WrappableBy concept significantly improves readability and maintainability compared to the previous ad-hoc requires clause. This addresses the past review feedback effectively.


470-506: LGTM! Robust validation and error handling.

The fitness validation logic correctly prevents NaN and infinity values, and the state tracking ensures proper call sequencing. The error messages using std::format are clear and helpful.


509-537: LGTM! Proper use of [[nodiscard]] on getters.

All getter methods are correctly marked with [[nodiscard]], preventing accidental misuse where return values are ignored. This follows best practices for const accessor methods.


540-592: Excellent backward compatibility design!

The type aliases successfully maintain the existing API while leveraging the unified implementation. This achieves the refactoring goal of eliminating duplication (~270 lines) without breaking changes. Well-documented and clearly structured.


81-592: Outstanding refactoring work!

This PR successfully achieves its objectives:

  • Eliminates ~270 lines of duplication through elegant policy-based design
  • Maintains full API compatibility via type aliases
  • Leverages modern C++ features (concepts, variadic templates, requires clauses) effectively
  • Preserves all thread-safety warnings and documentation
  • Code is well-structured and highly maintainable

The unified AdaptiveSelector template with trait policies is a textbook example of how to eliminate duplication while maintaining type safety and zero-cost abstractions (modulo std::function overhead, which is acceptable for this use case).

Minor recommended improvements from past reviews remain (lambda const marking), but they don't detract from the overall quality of this refactoring.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MAB Scheduler: Refactor Selector Classes

1 participant