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

Integrate most of the changes from PR #173 #178

Merged
merged 22 commits into from
Jul 5, 2023
Merged

Conversation

Saethox
Copy link
Collaborator

@Saethox Saethox commented Jun 27, 2023

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

Identifiers are zero-sized marker types that allow storing the same type T multiple times inside State by adding a I: Identifier type parameter together with a PhantomId to the type, which is handy in a bunch of places.

Lenses

  • Borrowed from functional programming, lenses (Lens, LensRef, LensMut) make it possible to extract any type T from the state without caring about the wrapper type that actually implements CustomState, which makes it possible to treat e.g. Iterations and Evaluations uniformly, as both just wrap an u32
  • IdLens<T> and ValueOf<T> implement Lens etc. and extract T and T::Target from the State, respectively

Documentation

  • Very much new documentation with many examples
  • Detailed module descriptions are mostly still missing, though, maybe we could update some documentation inside the docs folder for that
  • Especially documentation of many component and condition modules is still missing
  • I'd also like references for the heuristics and components that are implemented from literature

Problems

  • The Problem::default_evaluator method was removed in favor of specifying the Evaluator explicitly (either in Configuration::optimize or inserting through State::insert_evaluator)
  • Multiple evaluators are possible through identifiers

State

  • State is now a wrapper around StateRegistry, which is a fully-fledged set of types with dynamic borrowing through interior mutability, which means that borrowing multiple CustomState now feels really natural (this was very inspired from resman)
  • Many try_* methods which return a Result<_, StateError>

Components and Conditions

Logging

  • Trigger was removed in favor of using Condition directly: it is just so much easier to use Conditions which also support the boolean operators and such, we might want to think about temporary Conditions with a dummy Serialize implementation, though
  • Extracting data now happens primarily through lenses

Saethox added 12 commits June 27, 2023 20:36
- 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
@Saethox Saethox requested review from luleyleo and HeleNoir June 27, 2023 20:18
Copy link
Collaborator

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.

Copy link
Collaborator Author

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`].
Copy link
Collaborator

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.

src/components/cro.rs Outdated Show resolved Hide resolved
@@ -0,0 +1,175 @@
//! Common state mappings.
Copy link
Collaborator

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.

Copy link
Collaborator Author

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.

) -> 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")?;
Copy link
Collaborator

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).

Copy link
Collaborator Author

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.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Looks good, thanks!

Copy link
Collaborator

@HeleNoir HeleNoir left a 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.

@luleyleo
Copy link
Collaborator

I think I'll find time for it this weekend :)

Copy link
Collaborator

@luleyleo luleyleo left a 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.

  1. The documentation is missing for many parts, some modules don't have any.
  2. 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 :)

examples/sphere.rs Outdated Show resolved Hide resolved
src/components/mutation/common.rs Show resolved Hide resolved
src/components/mutation/de.rs Show resolved Hide resolved
src/components/mutation/functional.rs Show resolved Hide resolved
src/components/mutation/functional.rs Outdated Show resolved Hide resolved
src/problems/objective/mod.rs Outdated Show resolved Hide resolved
src/problems/evaluate.rs Show resolved Hide resolved
src/problems/evaluate.rs Show resolved Hide resolved
src/state/registry/mod.rs Outdated Show resolved Hide resolved
src/state/registry/mod.rs Show resolved Hide resolved
@Saethox
Copy link
Collaborator Author

Saethox commented Jul 1, 2023

The documentation is missing for many parts, some modules don't have any.

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'm not convinced that lenses are a good idea.

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.

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.

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.

@HeleNoir
Copy link
Collaborator

HeleNoir commented Jul 3, 2023

The documentation is missing for many parts, some modules don't have any.

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 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 not convinced that lenses are a good idea.

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.

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.

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'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.

@Saethox
Copy link
Collaborator Author

Saethox commented Jul 3, 2023

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.

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 LessThanN::iterations and LessThanN::evaluations methods, which hide that internally just a ValueOf lens is created with the corresponding custom state.

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.

Lenses in MAHF are not fully features lenses, but really basically what Extractor was in the old logging with a different name (my first implementation was actually named Extract when I didn't realize yet I'd just reimplemented a kind of lens) and a little bit more generic. You get the State as an argument, extract whatever you want from it and return it, that's basically it. And you can use it both in logging and components, in contrast to Extractor.

@luleyleo
Copy link
Collaborator

luleyleo commented Jul 3, 2023

If we assume competent users, then lenses should be nice. Having simple helper functions such as LessThanN::iterations makes it much nicer to write / read too.

Copy link
Collaborator

@luleyleo luleyleo left a comment

Choose a reason for hiding this comment

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

Looks good now!

@Saethox Saethox merged commit 5d7e4e4 into master Jul 5, 2023
@Saethox Saethox deleted the state-lenses-and-more branch July 5, 2023 11:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants