← Back to team overview

fema-team team mailing list archive

Re: Merged our branches

 

Hi JT,

I'm thinking of something like this (defined it java like to provide just enough information to convey the design)…

<<class>> Options
Options()
void setString(String name, String value)
String getString(String name, String defaultValue)
Float getFloat(String name, Float defaultValue)
Integer getInteger(String name, Integer defaultValue)

<<interface>> OptionsReader
Options read();

<<class>> PropertiesOptionsReader
PropertiesOptionsReader(file)
Options read();

Internally the Options class uses a Map<String, String> and its methods convert/cast the string values from the map into the required values.

The concrete PropertiesOptionsReader class reads the whole file and stores/sets each name-value pair in an Options class. Like you mention we would have a standard name = value file format with all algorithm/problem apps using the PropertiesOptionsReader class. Hence the Application would know which reader class to create an instance of and then pass the options class to the Algorithm Factory.

Then if we wanted to have specific Options classes like you mention for each class of algorithm the decorator pattern would provide a neat solution. Something like…

<<class>> DefaultOptions
DefaultOptions(Options)
int getMaxIterationCount() { _options.getInteger("max_iteration_count"); }

<<class>> MOOOptions extends DefaultOptions
MOOOptions(Options)
???

<<class>> PAESOptions extends MOOOptions
int maxArchiveSize
PAESOptions(Options) {
	maxArchiveSize = _options.getInteger("max_archive_size", 100);
}
int getMaxArchiveSize() { return maxArchiveSize;  }
void setMaxArchiveSize(int size) { maxArchiveSize = size; } //set method used when manually setting options (not loaded from a file) like in a unit test or something

This would then be used something like…

<<method>> Application.run() {

	//.….. read instance as per normal …..

	PropertiesOptionsReader reader("options.in.dat");
	Options options = reader.read();

	Algorithm algo = factory.createAlgorithm(instance, options);

	// … continue normal operation ….

}

<<method>> MQAPPAESFactory.createAlgorithm(Instance, Options) {

	//create operators etc

	return new PAESAlgorithm<MQAPChromosome>(createOp, mutateOp, objFn, PAESOptions(options)); 
	// or alternatively if my algorithm didn't have many options I could just take the generic options class 
	// e.g. return new PAESAlgorithm<MQAPChromosome>(createOp, mutateOp, objFn, options);

}

<<method>> PAESAlgorithm.isEnd() {

	if (evolutionCount > _options.getMaxEvolutionCount()) {
		return true;
	}

	return false;

}

The benefits of doing it this way are:

Order of options isn't important - don't have to have common options first, MOO options next and PAES options last
No need to write reader classes for each algorithm/problem - same single reader class used for all - we won't have to write file IO or tokenizer logic to read options because a common generic reader class will already exist
Has methods to convert/cast strings to integers etc so we don't have to write repetitive cast code
We have the freedom to change where the Options are read from or what format they are read in throughout the whole project from a single location without having to alter each algorithm factory method or adding lots of readFileXML, readFileJson etc methods to the options class
Freedom for the Algorithm implementor to use just the generic Options class or a decorator - this will allow us to support other non memetic EAs (e.g. the PAES algorithm doesn't have a population so inheriting CommonOptions.getPopulationSize() is kind of pointless and confusing)

Let me know if this is clear and what you think its flaws are. I'd be happy working on this if you want to start on the server ASAP seeing as you know BOINC best.

James

P.S. I'm not sure how many options would be common for all algorithms… max evolution count would be… population size would be a pretty common one but some algorithms might have a max and min population size rather than a fixed population size…… But I'd still recommend using option(s) decorators e.g. PAESOptions(Options) - just that the whole hierarchy of classes (CommonOptions > MOOOptions > PAESOptions) might not always be helpful.


On 12/10/2011, at 6:38 PM, Themiya Jayasinghe wrote:

> Hi,
> 
> Please ignore the getOptions(...) method in fema::algorithm::AlgorithmOptions. It's not being used by anything except the unit test.
> 
> The way it is being done at the moment is that I convert all the values and store them in public members of WuJi07Options ( please seee WuJi07Options.readFromStream(...) ). In my Algorithm class (WuJi07Algorithm) I have a
> private member of type WuJi07Options.
> 
> I think the best option would be to put options common to all algorithms (e.g: maxEvolutionCount) in fema::algorithm::Options and problems common to MOO algorithms in MOOOptions and problems specific to PAES in PAESOptions. However, the implementors of the MOOOptions / PAESOptions will need to call the super classes readFromStream(...) method to set the common options. e.g:
> 
> class fema::algorithm::Options {
> public:
> virtual void readFromStream(stream) {
>  //read common options
> }
> }
> 
> class fema::algorithm::moo::MOOOptions : public fema::algorithm::Options {
> 
> typedef fema::algorithm::Options _super;
> 
> public:
> virtual void readFromStream(stream) {
> _super.readFromStream(stream);
> 
> //read options specific to MOO
> }
> }
> 
> class fema::algorithm::moo::paes::PAESOptions : public fema::algorithm::MOOOptions {
> 
> typedef fema::algorithm::Options _super;
> 
> public:
> virtual void readFromStream(stream) {
> _super.readFromStream(stream);
> 
> //read options specific to paes
> this->maxArchiveSize = ...;
> 
> }
> 
> If we want to seperate the reading of options from the Options classes,  we need another heirarchy for OptionsReader 
> classes. (OptionsReader,  MOOOptionsReader, PAESOptionsReader). Then the Options classes will just have public  members for storing the different options
> 
> struct fema::algorthm::Options {
> size_t maxEvolutionCount;
> ...
> }
>  ...
> 
> struct fema::algorithm::moo::paes::PAESOptions : public fema::algorithm::moo::MOOOptions {
> size_t maxArchiveSize;
> }
> 
> Then in your PAESAlgorithm class you would have;
> 
> class PAESAlgorithm : .... {
> 
> public;
> const bool isEnd() { return _options->maxEvolutionCount >= this->_evolutionCount; }
> 
> void evolve() {
> 
> ....
> if (_options->maxArchiveSize < this->_archive.size()) {
> //add to archive
> }
> ...
> }
> private:
> shared_ptr<PAESOptions>  _options; 
> }
> 
> If we want to support multiple file formats for the options.in.dat file then we would need to extend the necessary OptionsReader classes. I think we should standardise the file format used for options.in.dat files so that we can reuse as much code as possible. (I went with the name : value format because I thought it is easier than parsing XML. But I'm open to other formats)
> 
> 
> In regards to the Application class loading the options, how would it know to create the appropriate type of  OptionsReader and Options instances ? That's why I put it in the factory.
> 
> Or did you mean create the correct OptionsReader in the main.cpp and read the Options from that ?
> 
> -jt
> 
> ---------- Forwarded message ----------
> From: James Newell <jameslnewell@xxxxxxxxx>
> Date: Wed, Oct 12, 2011 at 3:08 PM
> Subject: Merged our branches
> To: Themiya Jayasinghe <themiya.jayasinghe@xxxxxxxxx>
> 
> 
> Hi JT,
> 
> Just merged our branches and have a few comments/questions…
> 
> Other…
> Probably doesn't really matter about the .project files in our app folders (maybe we can ignore them) as we probably won't be loading each others apps in eclipse - if we want to run it its easier to bjam from the console… but doesn't matter that you've done it… just to save you time.
> 
> Options… 
> 
> The naming of the options class and the methods that it contains is a little confusing… Could we have the options class encapsulate a Map<string, string> with accessors something like
> 
> Options
> string getString(string name, int defaultValue);
> int getInteger(string name, int defaultValue);
> 
> PAESOptions
> int getMaxEvolutionCount() { return getInteger("max_evolution_count", 10000);  }
> int getMaxArchiveSize() { return getInteger("max_archive_size", 100); }
> 
> OptionsReader
> Options read();
> 
> JTFormattedFileOptionsReader
> JTFormattedFileOptionsReader(string filename);
> Options read(); 
> => which reads options from a file in the format "<option_name> : <option_value>"
> 
> Then we don't have to extend AlgorithmOptions for each of our algorithms - we can just take a generic instance of the class.
> But if one desired we could extend AlgorithmOptions with methods like getMaxEvolutionCount() which just calls the getInteger method.
> This will save lots of boilerplate code converting options from a string value to the desired type - something that everyone will be doing.
> 
> Could we have an AlgorithmOptionsReader (may be rename to Options and OptionsReader??) instead of the read methods within the current AlgorithmOptions class. This way there is the possibility of options being read from different sources and formats.
> Instead of each writing our own option readers there should be a common reader (e.g. JTFormattedFileOptionsReader and not WujiOptionsReader) that the Application class can use for all algorithms.
> 
> Can we have the Application responsible from loading the options (from file) and have Factory::createAlgorithm(Instance instance, Options options) instead?
> 
> Hope thats clear. Let me know what you think.
> 
> James
> 


Follow ups