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

Expanding customization #1

Closed
chrisbloom7 opened this issue Nov 3, 2021 · 7 comments
Closed

Expanding customization #1

chrisbloom7 opened this issue Nov 3, 2021 · 7 comments

Comments

@chrisbloom7
Copy link
Collaborator

👋🏻 hello! I've recently started to experiment with simulated annealing libraries. In terms of ruby annealer gems, this one seems to be the most recently updated and has the most tests. I've used it in a project of mine and am happy with its performance, but it does make a few assumptions about the annealing process and I had to monkey patch a few things to get it working with my solution. I'd like to contribute those back to this project. Are you open to discussing them and to contributions? Here's what I'm proposing:

Add a way to specify a custom move function that defaults to what Metal#swap_collection does now.

Metal#swap_collection assumes that the collection is a flat array and a neighboring state can be found by swapping elements. In my case, I'm working with a two dimensional array and neighboring states are created from shifting elements of one internal array to another. I'm sure there are other use cases that might call for more complicated object manipulations. Being able to specify a custom move function that receives the current collection and returns the next collection state would allow this library to be able to anneal more complex objects.

Add a way to specify termination conditions

The simulated annealing algorithm definition typically provides for the ability to specify some condition that will halt the annealing process other than the temperature reaching 0. This could be a "good enough" energy threshold, a temperature other than 0, or some other arbitrary condition. Right now Simulator#run will continue until the temperature reaches 0 and then return whatever state it had settled on. It would be beneficial to be able to specify a function that receives the current metal and temperature, and returns a boolean result as to whether to continue annealing. In my case, for example, the first state that has an energy cost of 0, including the initial state, is considered "good enough" and there's no need to continue annealing.

Add a way to specify a different cooling function

Simulator#cool_down provides a linear temperature reduction algorithm, but simulated annealing is not always linear. There are allowances in the definition for raising, lowering, and holding the temperature until certain conditions are met. It would be useful to provide a way to specify a custom cooling method that takes the current metal, and temperature, and returns the new temperature, such that it could operate linearly, geometrically, exponentially, or based on some other custom formula with the current state in mind. It would use the linear Simulator#cool_down as the default.


With those three changes, each step of the annealing process becomes configurable with reasonable defaults. None of these changes are particularly difficult, and all seem to fit in the same framework as what you already provide for specifying a custom energy calculator:

  • provide a new configuration option that can hold a lambda
  • set a default if nothing is provided
  • allow lambdas to be passed in at run time
  • call the passed or configured lambda

The biggest change would be that we would probably want to switch to keyword arguments for Simulator#run and/or add the ability to pass them to Simulator#initialize, and that's why I wanted to run this by you first since it changes the interface a bit. (We could also do it with positional arguments without changing the existing interface, but that makes it harder to work with IMO.)

@3zcurdia
Copy link
Owner

3zcurdia commented Dec 8, 2021

Hi Cris it has been a while since work on this project. Originally I did it just as a proof of concept for the annealing algorithm, and I didn't consider the changes that you mentioned.

However since you already mentioned that you monkey patch those changes feel free to open pull requests so they get integrated into the next version of this gem.

@chrisbloom7
Copy link
Collaborator Author

@3zcurdia thanks for the response! I'll work on a PR shortly. Any particular direction you'd like to provide on this question?

The biggest change would be that we would probably want to switch to keyword arguments for Simulator#run and/or add the ability to pass them to Simulator#initialize, and that's why I wanted to run this by you first since it changes the interface a bit. (We could also do it with positional arguments without changing the existing interface, but that makes it harder to work with IMO.)

@chrisbloom7
Copy link
Collaborator Author

@3zcurdia I've opened #2 which implements the first suggestion above. I've also got chrisbloom7#1 waiting in the wings which cleans up and backfills the test suite. I'd appreciate any feedback you had on either.

@chrisbloom7
Copy link
Collaborator Author

chrisbloom7#2 is on deck behind chrisbloom7#1, It implements the second suggestion above.

@chrisbloom7
Copy link
Collaborator Author

I realize these are big sweeping changes. I am definitely open to feedback about the implementation, argument naming, tradeoffs, etc., should you have any.

@chrisbloom7
Copy link
Collaborator Author

@3zcurdia are you still interested in contributions? If so, do you have any feedback on the next PR #4?

@chrisbloom7
Copy link
Collaborator Author

Everything in here has been completed! 🎉

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

No branches or pull requests

2 participants