Closed heidmic closed 2 years ago
@Saethox Do you have an idea?
Different bound representations mean that we have components that can work with several or just one representation. Let's say we replace bounds
of Rule
with some generic Bounds
class, which implements matched_data()
and volume()
. The best ways I can think of right now to adapt our components to this structure are
rule.bounds
inside the components with rule.bounds.interval
or whatever, which we just assume is the representation this component is meant for. If it's not, the user is at fault for configuring invalid components for this type of bound representation.Generic[T]
as superclass to components, and restrict T
to subclasses of Bound
that this component works with.But even without typechecking we would still need to introduce (rule) optimizers (or at least some of their components such as mutation operators and rates) for each representation, wouldn't we? Which might become convoluted very fast. Even if we were to only think from a naming convention perspective.
Generics would be the way to go to make user errors somewhat less likely, but the core problem would be how to select the right operators (ideally, automatically rather than manually).
If we consider rotatable hyperellipsoids (so 3 parameters per dimension) or more complex stuff it becomes even worse. Not even considering the issues with maintainability. For now, what others such as @hubanton and @tniemeier are doing is to create individual branches per representation. This allows changes more easily, but if we were to introduce some changes to SupRBs core structure (or even find and fix some bugs) this might become quite an issue, which prompted my concern if we might be able to do that better
@RomanSraj how would you approach this with your large scale projects in C++? Can we do something similar here? Or will the overhead kill us?
But even without typechecking we would still need to introduce (rule) optimizers (or at least some of their components such as mutation operators and rates) for each representation, wouldn't we?
If you're asking if we have to add code to support more bound representations, then the answer is of course yes. But I don't think this will get out of hand.
Which might become convoluted very fast. Even if we were to only think from a naming convention perspective.
I don't really see the problem. Our components are very modular, so adding 5 new mutation operators for e.g., ellipsoidal representation is exactly the same as adding 5 new operators for interval-based bounds, provided we make bounds exchangeable. And our optimizers only rely on these components. The ES for example never even accesses rule bounds directly, and it should be similar for the other optimizers.
I think there is some misunderstanding in what I want to avoid. I talked to @RomanSraj "offscreen" on how this might be done most user-friendly (which is essentially the important aspect here) while still improving the way we handle bounds. I had an idea from this I will implement soon
So as of right now matching functions are very deeply engrained into the whole system. While we can rather easily exchange the match set and matched volume calculations in rule.base, our optimizers very much depend on the representation. Is there a good way to do this? At least without creating "new optimizer classes"? We could probably do some abstract factory type thing but that does seem like a lot of overhead.
To specify the issue with the optimizers. Imagine we have an ordered bound and a center spread notation. In the case of ordered bound mutating either bound is semantically the same (well, almost), however, with center spread moving center or in-/decreasing spread is very differently from each other.