Print

Print


On Aug 24, 2008, at 10:26 PM, Andrew Wagner wrote:

> I'm trying to do something similar to this code:
>    public void eval(final EvolutionState state,
>                     final int thread,
>                     final GPData input,
>                     final ADFStack stack,
>                     final GPIndividual individual,
>                     final Problem problem)
>        {
>        double result;
>        DoubleData rd = ((DoubleData)(input));
>
>        children[0].eval(state,thread,input,stack,individual,problem);
>        result = rd.x;
>
>        children[1].eval(state,thread,input,stack,individual,problem);
>        rd.x = result + rd.x;
>        }
>    }
>
> This comes from tutorial 4, and it's simple enough, taking two nodes
> that return doubles, adding them together, and putting a new double in
> the input. In my case, however, I'm actually returning a different
> type. I have a BoolData class which encapsulates a Boolean value. How
> would you modify this code, for example, to return a BoolData which
> indicated whether the result was greater than 100?  The input is  
> final,
> so I can't replace the reference with a new GPData. What am I missing?

Sure you can replace it.  A final parameter just means that it won't  
change locally.  It's an unnecessary optimization ECJ made in the past  
(because Hotspot ignores 'final' in local variables) and we can get  
rid of it.

But that's not what you want to do.  The idea here is that ECJ trees  
typically use a SINGLE GPData object and pass it throughout the tree.   
It's handed from parent to child (when the parent calls eval).  It's  
where the child is expected to add stuff to be returned to the  
parent.  You probably don't want to make multiple GPData objects, it'd  
be extremely expensive at runtime.  Do this:

1. Create a subclass of GPData which holds a slot for every kind of  
return value you might need, like this maybe:

	public class FooData extends GPData
		{ public double d; public boolean b; }

2. Let's say you're trying to take two doubles, compare them and  
return a boolean if they're equal.  You'd do:

    public void eval(...)
        {
        double result;
        FooData rd = ((FooData)(input));

        children[0].eval(state,thread,input,stack,individual,problem);
        result = rd.d;

        children[1].eval(state,thread,input,stack,individual,problem);
        rd.b = (rd.d == result);
        rd.d = 0;  // heck, why not zero it out
        }

3.  Now you'd declare FooData as your data object in your parameter  
file.

4.  If your parent KNOWS that the child will return a boolean, it can  
just look in the boolean slot (b) and we're done.  If the parent can  
accept multiple kinds of return types from children (perhaps you're  
using set typing), then it's best to change this to specify the type  
of object presently stored, something like this:

	public class FooData extends GPData
		{
		public double d;
		public boolean b;
		public int type;
		public static final int TYPE_DOUBLE = 0;
		public static final int TYPE_BOOLEAN = 1;
		}

5. Then you'd do something like:

    public void eval(...)
        {
        double result;
        FooData rd = ((FooData)(input));

        children[0].eval(state,thread,input,stack,individual,problem);
        result = rd.d;

        children[1].eval(state,thread,input,stack,individual,problem);
        rd.b = (rd.d == result);
        rd.d = 0;  // heck, why not zero it out
        rd.type = FooData.TYPE_BOOLEAN;  // so my parent knows I'm  
giving him a boolean
        }

Sean