46bit / evco

Genetic Programming in Rust
https://46bit.github.io/evco/
GNU Lesser General Public License v3.0
3 stars 2 forks source link

Improve the `Tree` API #1

Closed 46bit closed 7 years ago

46bit commented 7 years ago

The Tree trait is very powerful and would seem sufficiently so to build a Rust form of DEAP. However the naming is unclear or raises issues. In addition it is still improvised around the constraints to-hand.

Tree::rand_terminal

Tree::rand_terminal is an accurate name. The name matches the expectation that it returns a leaf node of some sort.

But it is a bad name. Originally, when I was trying to be reminiscent of Rust Quickcheck's Arbitrary API, this was named arbitrary_terminal. That name was closer to what I desire: the chosen terminal does not need to be random; in fact I want people to be able to put logic in there.

Tree::rand_nonterminal

Tree:: rand_nonterminal is an inaccurate and bad name. There is no requirement for this method to return a non-terminal. Returning a non-terminal is in keeping with how TreeGen generates trees of certain depths but this is much more of a handy guideline than a rule.

Tree::terminal_proportion

This is lifted straight from DEAP's genGrow logic. I like having properties about the implementation of Tree to hand, but this should probably be done some way other than having users calculate how many of their nodes are terminal.

There's a large design space here.

Both these implementations add requirements to users, but the functionality may become more straightforward.

Dealing with tree roots

I think it would be best to have a TreeRoot struct that wraps an entire Tree. From the DEAP and GP point-of-view this is better thought of as an Individual. Amongst other things this struct would store the number of nodes in the tree and its maximum depth - useful for logging.

Calculating number of nodes during generation would add complication. TreeGen isn't designed to do this and adding further method arguments is going to confuse users. The ability to visit each node is evidently called for - this would be a new section to the API.

46bit commented 7 years ago

I've solved many of the issues I mused about above.

I'm keeping "pick terminals and nonterminals yourself." I renamed those to leaf and branch based upon Terminology_used_in_Trees on Wikipedia.

As regards custom deriving nodes, it seems possible. I'd need a separate crate for that. I'd also need to require all types named in the Tree-implementing enums to be either Tree or rand::Rand. Presumably Self would be used as Tree and all other included types as Rand.

For now custom deriving exceeds requirements. I don't know what I have yet, so a solid day of engineering on pure convenience would be a mistake.