-
Notifications
You must be signed in to change notification settings - Fork 0
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
Integrate most of the changes from PR #173 #178
Conversation
- Remove MutState - Add StateRegistry as a generic state set - Make State a wrapper around StateRegistry - Add error handling - Move component state into components module
- Add Lens, LensRef, and LensMut - Add IdLens and ValueOf defaults - Add other defaults
- Add a default evaluator
- Rename initialize to init - Add a proper require phase - Split modules further - Add tested functional modules - Add error handling - Remove the concept of termination
src/components/boundary.rs
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For (future) documentation purposes: Which boundary constraint repair methods were implemented first was based on https://doi.org/10.1016/j.ins.2021.09.058, though individual papers for the different methods were not used.
If we want to include more different methods, https://dl.acm.org/doi/10.1145/3449726.3463214 and https://ieeexplore.ieee.org/document/9878135/ are good resources.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add the first paper as a reference. Maybe open a new issue with the other references, so this won't get lost in this PR?
/// This corresponds to a `while` loop. | ||
/// # State | ||
/// | ||
/// This component inserts and updates the current number of [`Iterations`]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should put a disclaimer here that we can, of course, also terminate depending on the number of evaluations, as this is more common.
@@ -0,0 +1,175 @@ | |||
//! Common state mappings. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bit more documentation in terms of why this exists would be good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added some documentation to components::mapping
.
src/components/selection/common.rs
Outdated
) -> ExecResult<Vec<&'a Individual<P>>> { | ||
let weights = f::proportional_weights(population, self.offset) | ||
.wrap_err("population contains invalid objective values") | ||
.note("roulette wheel note does work with infinite or negative objective values")?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are possible ways to make this work for negative objective values. I thought I had implemented one of those at some point. Ultimately, we should make this work, either by adapting this implementation or by offering a second roulette wheel variant (though that would be rather unnecessary).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I originally removed the handling of negative fitness values because I wasn't sure we were handling all corner cases correctly, and didn't have the time to research this in more detail.
I now have, so please take a look and verify the new implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could merge this, though I commented some aspects, but for a more implementation-focused review we should wait if @luleyleo has time to take a look.
I think I'll find time for it this weekend :) |
- Allow negative weights in roulette wheel - Handle ties in ranks correctly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The smaller comments aside, I still see two issues here.
- The documentation is missing for many parts, some modules don't have any.
- I'm not convinced that lenses are a good idea.
more on lenses
While I do think lenses are powerful and useful, I don't think they are easy to understand. Especially, people without a mathematical background seem to struggle with it. I've spent quite some time working on Druid, which is basically built around lenses as well, and it causes A LOT of confusion. Particularly when they escalate and turn into a whole meta programming language. That's the main reason I did not add them before and instead went with a less flexible but much simpler design. So, in short, I would prefer to avoid this concept and instead do things with normal procedural code instead of type-level meta-programming :)
Writing (good) documentation, unfortunately, takes so much time that I wanted to open this PR even if some stuff is missing documentation. I planned to do another PR focusing on documentation, but I could also make that part of this PR if you think that's better.
I agree that they add complexity, but they are just so elegant. They allow massive code reuse and integrate really well with logging. Why would I want a new component every time I want some parameter to decrease with iterations if I can just specify what parameter should decrease? But they certainly should be really well documented, which they are currently not really.
One advantage of developing a "scientific" library is that we can assume some mathematical background of people who might want to use MAHF, so the benefit really outweighs the additional complexity (at least in my opinion), in this case. And not that we have to copy what others do, but e.g. CIlib also extensively relies on functional programming concepts like lenses. |
I wouldn't mind if the documentation is completed in another PR. However, (thinking about presenting MAHF at GECCO) the parts that are most difficult to understand should have documentation.
I'm quite torn on the topic of lenses. I can see that they are complex to really understand, but my feeling was that even without proper understanding, you can still implement and use them (maybe I will try later and see what happens). I just don't know if that's what we want. However, I also think that we can expect some background knowledge of possible users. So most important would definitely be a proper documentation, preferably in this PR. And maybe a stupid idea: can we offer both options, i.e. lenses and components, without breaking the general logging implementation (or without having to do too much adaptation of the code)? Then people could choose depending on their experience when they want to write own extensions and we can use lenses. |
I think we can also hide most of the complexity of lenses by just adding methods for instantiating components with the lenses they are most commonly used with. For example, I added
Lenses in MAHF are not fully features lenses, but really basically what |
If we assume competent users, then lenses should be nice. Having simple helper functions such as |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good now!
It's probably best to check out the branch locally and review this inside the IDE or run
cargo doc
, as this PR includes extensive documentation (yet there is still enough missing), which is not very nice to review on GitHub.I decided to add the logging refactor here already because entangling this is not really worth it, as I would either merge incorrect documentation on other items or spend time rewriting documentation which is then immediately updated.
Changes
Closes #161
Closes #160
Closes #157
Closes #84
New concepts
Identifiers
Identifier
s are zero-sized marker types that allow storing the same typeT
multiple times insideState
by adding aI: Identifier
type parameter together with aPhantomId
to the type, which is handy in a bunch of places.Lenses
Lens
,LensRef
,LensMut
) make it possible to extract any typeT
from the state without caring about the wrapper type that actually implementsCustomState
, which makes it possible to treat e.g.Iterations
andEvaluations
uniformly, as both just wrap anu32
IdLens<T>
andValueOf<T>
implementLens
etc. and extractT
andT::Target
from theState
, respectivelyDocumentation
docs
folder for thatProblems
Problem::default_evaluator
method was removed in favor of specifying theEvaluator
explicitly (either inConfiguration::optimize
or inserting throughState::insert_evaluator
)State
State
is now a wrapper aroundStateRegistry
, which is a fully-fledged set of types with dynamic borrowing through interior mutability, which means that borrowing multipleCustomState
now feels really natural (this was very inspired from resman)try_*
methods which return aResult<_, StateError>
Components and Conditions
require
phase betweeninit
andexecute
(Split our component initialization phase into a initialize and require phase #157)Component
methods return aResult
functional
module with unit testsLogging
Trigger
was removed in favor of usingCondition
directly: it is just so much easier to useCondition
s which also support the boolean operators and such, we might want to think about temporaryCondition
s with a dummySerialize
implementation, though