Skip to content

feat(moeo): add finalize() lifecycle hook for algorithm post-processing#84

Open
evvaletov wants to merge 2 commits into
nojhan:masterfrom
evvaletov:feat/moeo-finalize-lifecycle
Open

feat(moeo): add finalize() lifecycle hook for algorithm post-processing#84
evvaletov wants to merge 2 commits into
nojhan:masterfrom
evvaletov:feat/moeo-finalize-lifecycle

Conversation

@evvaletov

@evvaletov evvaletov commented Feb 17, 2026

Copy link
Copy Markdown

Summary

Adds a finalize() extension point to the multi-objective algorithm
hierarchy, allowing algorithms to perform post-processing after external
population modifications.

  • moeoAlgo: virtual destructor + hasFinalize() const (default false)
  • moeoPopAlgo<MOEOT>: virtual finalize(eoPop<MOEOT>&) (default no-op)
  • moeoNSGAII: implements finalize() to recompute dominance-depth
    fitness and crowding diversity assignments

Motivation

In island model configurations, immigrant individuals are integrated into
an island's population between generations. NSGA-II's fitness and diversity
values become stale after integration — the non-dominated sorting ranks and
crowding distances no longer reflect the actual population composition.
Without recomputation, selection pressure degrades until the next natural
generation boundary.

The finalize()/hasFinalize() pattern lets the island model trigger
recomputation only on algorithms that need it, without coupling the island
infrastructure to specific algorithm internals.

Changes

File Change
moeo/src/algo/moeoAlgo.h Added virtual destructor, hasFinalize() const
moeo/src/algo/moeoPopAlgo.h Added virtual void finalize(eoPop<MOEOT>&) {}
moeo/src/algo/moeoNSGAII.h Override: recomputes fitnessAssignment + diversityAssignment

@nojhan

nojhan commented Feb 27, 2026

Copy link
Copy Markdown
Owner

Good idea, thanks!

I would be willing to merge this if I can review the code (same comment than for PR #83 )

Add virtual finalize() and hasFinalize() to moeoAlgo/moeoPopAlgo hierarchy.
NSGA-II implements finalize() to recompute fitness and diversity assignments
after population modifications (e.g. immigrant integration in island model).
@evvaletov
evvaletov force-pushed the feat/moeo-finalize-lifecycle branch from 45b4623 to d306ef8 Compare March 1, 2026 00:55
@evvaletov

Copy link
Copy Markdown
Author

Rebased onto HEAD, should be reviewable now.

On the design: finalize() exists for the island model (PR #86). After immigrants are integrated into a population, NSGA-II's dominance depth and crowding distance become stale. finalize() lets the island call fitnessAssignment + diversityAssignment on the updated population. The hasFinalize() query on the non-template base avoids coupling the island model to specific algorithm types.

@nojhan

nojhan commented Mar 2, 2026

Copy link
Copy Markdown
Owner

This is a good idea. I'm just wondering why adding a const check for the feature instead of derivating a new interface?

My immediate thought would have been to add some kind of moeoAlgoFinalized abstract class adding the finalize interface. That way, one don't rely on a test with hasFinalize and the compiler double-checks the interface for you, which should lead to less bugs.

But maybe there is some problem with that?

Replace the hasFinalize()/finalize() pattern with a moeoAlgoFinalized<MOEOT>
abstract interface. Algorithms opt in by inheriting from it. Consumers
check at compile time via std::is_base_of_v instead of a runtime bool.
@evvaletov

Copy link
Copy Markdown
Author

You're right, a separate interface is cleaner. I've refactored it.

moeoAlgoFinalized<MOEOT> is now an abstract class with a pure virtual finalize(eoPop<MOEOT>&). Algorithms opt in by inheriting from it. The hasFinalize() method on moeoAlgo and the default no-op finalize() on moeoPopAlgo are both removed.

In the island model (PR #86), the consumer becomes a compile-time check:

if constexpr (std::is_base_of_v<moeoAlgoFinalized<algoEOT>, EOAlgo<algoEOT>>)
    algo.finalize(pop);

This works because the island template already knows the concrete algorithm type. The compiler enforces the interface, and algorithms without finalize pay zero cost (the branch is eliminated at compile time).

NSGA-II now inherits from both moeoEA<MOEOT> and moeoAlgoFinalized<MOEOT>.

I've updated PRs #83, #86, and #87 accordingly (rebased onto this change).

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.

2 participants