>     In this example, I find that the "nil" GPType is used for the 
> arguments and the return type of the "Add", "Sub" and "Mul" 
> GPNodes. 
>     Could I have some information on what this "nil" GPType stand 
> for and 
> could I define my own GPTypes for the GPNodeConstraints of these 
> GPNodes ?


( * I've written this in haste so beware of possible errors and
misleading things * )

Nil is just a label for data if you do standard (untyped) GP, where all
nodes pass the same type of information between them (in the case of
regression, they pass to one another numerical values). Suppose you
build a GP problem that requires all nodes to pass strings, then you can
use the nil type again. Also, you can rename the nil type to whatever
you want (give it a try) and still do regression. ECJ names this
communication data "atomic type". 

See ecj/ec/gp/koza/koza.params, here are some sparse bits:
# untyped
# Here we define a single atomic type, "nil", which everyone will use.
# There are no set types defined.
gp.type.a.size = 1 = nil

# some lines below this declares what the tree is supposed to return = nil

# then node types definitions (this would be a terminal, no children) = = nc0 = nil

# and then back in tutorial4.params the terminal X is defined
gp.fs.0.func.0 = = nc0

If you do strongly-typed GP (say you have both boolean and numeric
types), you need to declare different atomic types and declare nodes
Say you have a silly if function of the type "if terminal_1 then
something" in a regression problem. "terminal1" would be boolean and
"something" maybe numeric.

If you put your problem in ec/app/your_experiment, then you would have:
# a stands for atomic I think
gp.type.a.size = 2 = boolean # boolean and numeric are just names = numeric # you could call them num and bool

# what the tree is going to return = numeric # if regression problem
# = boolean # if you had, say, a classifier to say true
or false

# then node type = = some_name = numeric = 1 = boolean

# and then the node declaration itself
gp.fs.0.func.0 = = some_name

# a second if node with the same constraints, different implementation
gp.fs.0.func.1 = = some_name

Hopefully this helps more than messing things.