Open thelmuth opened 9 years ago
Before I do this, does it seem like a good idea to others? Right now, the genetic operators each take individuals as input (i.e. the parents), so it is more symmetric to return individuals as we do now. But, it also seems like it would be simpler to just return the genome and have breed
make the individual. So, I'm not sure which way would be better.
If it removes duplication without changing the functionality or reducing the communicativeness of the code, it makes a lot of sense to me.
Personally, I'd like to see all the operators become more modular & "pluggable". I guess if you were to chain a few operators in a row, it feels like you'd want to collect and call all the prospective contributor individuals the "parents". But off the top of my head I can only think of super stupid contrived "operator chains" that might have multiple steps in them. So maybe that can't happen?
Operator chains (or "pipelines") are implemented in Clojush, and actually are used in many problem files as well as the default operators. See, for example, https://github.com/lspector/Clojush/blob/master/src/clojush/pushgp/pushgp.clj#L53
Then I guess I take it back, and now it seems they should all accept and return collections of individuals?
That's not to say that you might want to use them outside of the context of our current breeding system. In that case, I could see it making more sense to have parent genomes passed to operators, and the operators return child genomes; currently, parent individuals are passed and child individuals returned.
I checked, and none of the operators use any info stored in individuals besides the genomes, so there's no reason the operators couldn't work on and return genomes instead of individuals.
Oh, and I'm not a big fan of sets of individuals/genomes -- I'd prefer each operator know how many parents it requires. YMMV
Isn't selection a search operator?
[goes to check]
Yes? But how is that related to this conversation?
Oh, do you mean because we're talking about genetic operators? Selection isn't a genetic operator IMO.
Selection: It's complicated, and probably wrong in the context. Just looked, and I was thinking of a different architecture.
Having looked, I guess the only question for me is: Where is the responsibility for recording ancestors going to be, if the genetic operators don't write it down in the offspring? In the breed
caller?
Where is the responsibility for recording ancestors going to be, if the genetic operators don't write it down in the offspring? In the breed caller?
Yes, that is what I was envisioning. We're currently only tracking 1 parent even for 2-parent operators. So, it would be easy enough to do where the operators are called in breed
.
Right, because you know all the arguments for any given genetic operator before you call it. Makes sense to me.
I think I'm in favor of sticking with the idea that genetic operators get whole individuals, not just genomes, as inputs. This is because I can imagine wanting to do different things in the operator depending on other features of the individual, e.g. mutating proportionately to total error or some particular error, or doing something that depends on the variance in the history, or whatever. I understand that nothing in the code base does this currently, but something along these lines might be worthwhile, and in fact we might (I'm not sure) have used exactly this mechanism to do some past autoconstructive experimentation that's not currently in the code base but we might want to revisit. Why make it impossible? As for what the operators return, I guess it's fine to return a genome since that can be turned into an individual by any caller who wants an individual... unless the operator would be returning more information in the individual than just the new genome and program... but this seems less likely to me, so I don't think it's a big deal. Still, since I'd like to stick with individuals as inputs, maybe it's most natural to also stick with individuals as outputs.
I guess I think of these really as variation operators, rather than genetic operators. We call them genetic operators, but I guess I want to keep them more general anyway (or conceivably rename them). There are probably some elegant ways to allow any variation operator (individual(s)->individual(s)) while also making things simple for the more usual genome(s)->genome(s) scenario, if we think about it... But it doesn't seem worth it to me to design this out unless other plans depend on it.
Ok, that's fine with me! If we have individuals as inputs, I think it makes more sense to have individuals as outputs, even if it means duplicated code. I think this is now closed, which I guess I can close?
Except: the docs should probably make this explicit. I'd be happy to do that, but where should it go? (I'm thinking in OOP it would go into the Module or parent class doc, but here how does that work?)
I don't know what would be considered "standard" for Clojure here. Personally, I would just put it in a regular-ol' comment at the top of genetic_operators.clj
.
I noticed that the end of every single genetic operator looks the same -- it calls
make-individual
, setting some of the fields. But, every one is the same! This seems like something that should be moved tobreed
, and have the genetic operators just return the Plush genome, not an individual. I will have to check and make sure that none of the operators are called from other places outside ofbreed
, but otherwise this should be a clean refactoring that will simplify some things nicely.