1 | /* |
---|
2 | Copyright 2006 by Sean Luke |
---|
3 | Licensed under the Academic Free License version 3.0 |
---|
4 | See the file "LICENSE" for more information |
---|
5 | */ |
---|
6 | |
---|
7 | |
---|
8 | package ec.gp; |
---|
9 | import ec.*; |
---|
10 | import ec.util.*; |
---|
11 | |
---|
12 | /* |
---|
13 | * GPNodeBuilder.java |
---|
14 | * |
---|
15 | * Created: Thu Oct 7 17:31:41 1999 |
---|
16 | * By: Sean Luke |
---|
17 | */ |
---|
18 | |
---|
19 | /** |
---|
20 | * GPNodeBuilder is a Prototype which defines the superclass for objects |
---|
21 | * which create ("grow") GP trees, whether for population initialization, |
---|
22 | * subtree mutation, or whatnot. It defines a single abstract method, |
---|
23 | * newRootedTree(...), which must be implemented to grow the tree. |
---|
24 | * |
---|
25 | * <p>GPNodeBuilder also provides some facilities for user-specification |
---|
26 | * of probabilities of various tree sizes, which the tree builder can use |
---|
27 | * as it sees fit (or totally ignore). |
---|
28 | * There are two such facilities. First, the user might |
---|
29 | * specify a minimum and maximum range for tree sizes to be picked from; |
---|
30 | * trees would likely be picked uniformly from this range. Second, the |
---|
31 | * user might specify an array, <tt>num-sizes</tt> long, of probabilities of |
---|
32 | * tree sizes, in order to give a precise probability distribution. |
---|
33 | |
---|
34 | <p><b>Parameters</b><br> |
---|
35 | <table> |
---|
36 | <tr><td valign=top><i>base</i>.<tt>min-size</tt><br> |
---|
37 | <font size=-1>int >= 1, or undefined</font></td> |
---|
38 | <td valign=top>(smallest valid size, see discussion above)</td></tr> |
---|
39 | |
---|
40 | <tr><td valign=top><i>base</i>.<tt>max-size</tt><br> |
---|
41 | <font size=-1>int >= <tt>min-size</tt>, or undefined</font></td> |
---|
42 | <td valign=top>(largest valid size, see discussion above)</td></tr> |
---|
43 | |
---|
44 | <tr><td valign=top><i>base</i>.<tt>num-sizes</tt><br> |
---|
45 | <font size=-1>int >= 1, or underfined</font></td> |
---|
46 | <td valign=top>(number of sizes in the size distribution, see discussion above) |
---|
47 | </td></tr> |
---|
48 | |
---|
49 | <tr><td valign=top><i>base</i>.<tt>size</tt>.<i>n</i><br> |
---|
50 | <font size=-1>0.0 <= float <= 1.0</font>, or undefined</td> |
---|
51 | <td valign=top>(probability of choosing size <i>n</i>. See discussion above) |
---|
52 | </td></tr> |
---|
53 | </table> |
---|
54 | |
---|
55 | |
---|
56 | * @author Sean Luke |
---|
57 | * @version 1.0 |
---|
58 | */ |
---|
59 | |
---|
60 | public abstract class GPNodeBuilder implements Prototype |
---|
61 | { |
---|
62 | /** Produces a new rooted tree of GPNodes whose root's return type is |
---|
63 | swap-compatible with <i>type</i>. When you build a brand-new |
---|
64 | tree out of GPNodes cloned from the |
---|
65 | prototypes stored in the GPNode[] arrays, you must remember |
---|
66 | to call resetNode() on each cloned GPNode. This gives ERCs a chance |
---|
67 | to randomize themselves and set themselves up. |
---|
68 | |
---|
69 | <p>requestedSize is an |
---|
70 | optional argument which differs based on the GPNodeBuilder used. |
---|
71 | Typically it is set to a tree size that the calling method wants |
---|
72 | the GPNodeBuilder to produce; the GPNodeBuilder is not obligated to |
---|
73 | produce a tree of this size, but it should attempt to interpret this |
---|
74 | argument as appropriate for the given algorithm. To indicate that |
---|
75 | you don't care what size the tree should be, you can pass NOSIZEGIVEN. |
---|
76 | However if the algorithm <i>requires</i> you to provide a size, it |
---|
77 | will generate a fatal error to let you know. */ |
---|
78 | |
---|
79 | public static final int NOSIZEGIVEN = -1; |
---|
80 | public static final int CHECK_BOUNDARY = 8; |
---|
81 | public static final String P_MINSIZE = "min-size"; |
---|
82 | public static final String P_MAXSIZE = "max-size"; |
---|
83 | public static final String P_NUMSIZES = "num-sizes"; |
---|
84 | public static final String P_SIZE = "size"; |
---|
85 | |
---|
86 | public int minSize; /** the minium possible size -- if unused, it's 0 */ |
---|
87 | public int maxSize; /** the maximum possible size -- if unused, it's 0 */ |
---|
88 | public float[] sizeDistribution; /* sizeDistribution[x] represents |
---|
89 | the likelihood of size x appearing |
---|
90 | -- if unused, it's null */ |
---|
91 | |
---|
92 | /** Returns true if some size distribution (either minSize and maxSize, |
---|
93 | or sizeDistribution) is set up by the user in order to pick sizes randomly. */ |
---|
94 | public boolean canPick() |
---|
95 | { |
---|
96 | return (minSize!=0 || sizeDistribution !=null); |
---|
97 | } |
---|
98 | |
---|
99 | /** Assuming that either minSize and maxSize, or sizeDistribution, is defined, |
---|
100 | picks a random size from minSize...maxSize inclusive, or randomly |
---|
101 | from sizeDistribution. */ |
---|
102 | public int pickSize(final EvolutionState state, final int thread) |
---|
103 | { |
---|
104 | if (minSize>0) |
---|
105 | { |
---|
106 | // pick from minSize...maxSize |
---|
107 | return state.random[thread].nextInt(maxSize-minSize+1) + minSize; |
---|
108 | } |
---|
109 | else if (sizeDistribution!=null) |
---|
110 | { |
---|
111 | // pick from distribution |
---|
112 | return RandomChoice.pickFromDistribution( |
---|
113 | sizeDistribution, |
---|
114 | state.random[thread].nextFloat()) + 1 ; |
---|
115 | } |
---|
116 | else throw new InternalError("Neither minSize nor sizeDistribution is defined in GPNodeBuilder"); |
---|
117 | } |
---|
118 | |
---|
119 | |
---|
120 | public Object clone() |
---|
121 | { |
---|
122 | try |
---|
123 | { |
---|
124 | GPNodeBuilder c = (GPNodeBuilder)(super.clone()); |
---|
125 | |
---|
126 | if (sizeDistribution != null) c.sizeDistribution = |
---|
127 | (float[]) (sizeDistribution.clone()); |
---|
128 | |
---|
129 | return c; |
---|
130 | } |
---|
131 | catch (CloneNotSupportedException e) |
---|
132 | { throw new InternalError(); } // never happens |
---|
133 | } |
---|
134 | |
---|
135 | |
---|
136 | |
---|
137 | |
---|
138 | public void setup(final EvolutionState state, final Parameter base) |
---|
139 | { |
---|
140 | Parameter def = defaultBase(); |
---|
141 | |
---|
142 | // min and max size |
---|
143 | |
---|
144 | if (state.parameters.exists(base.push(P_MINSIZE), |
---|
145 | def.push(P_MINSIZE))) |
---|
146 | { |
---|
147 | if (!(state.parameters.exists(base.push(P_MAXSIZE), |
---|
148 | def.push(P_MAXSIZE)))) |
---|
149 | state.output.fatal("This GPNodeBuilder has a " + |
---|
150 | P_MINSIZE + " but not a " + P_MAXSIZE + "."); |
---|
151 | |
---|
152 | minSize = state.parameters.getInt( |
---|
153 | base.push(P_MINSIZE), def.push(P_MINSIZE),1); |
---|
154 | if (minSize==0) |
---|
155 | state.output.fatal("The GPNodeBuilder must have a min size >= 1.", |
---|
156 | base.push(P_MINSIZE), def.push(P_MINSIZE)); |
---|
157 | |
---|
158 | maxSize = state.parameters.getInt( |
---|
159 | base.push(P_MAXSIZE), def.push(P_MAXSIZE),1); |
---|
160 | if (maxSize==0) |
---|
161 | state.output.fatal("The GPNodeBuilder must have a max size >= 1.", |
---|
162 | base.push(P_MAXSIZE), def.push(P_MAXSIZE)); |
---|
163 | |
---|
164 | if (minSize > maxSize) |
---|
165 | state.output.fatal( |
---|
166 | "The GPNodeBuilder must have min size <= max size.", |
---|
167 | base.push(P_MINSIZE), def.push(P_MINSIZE)); |
---|
168 | } |
---|
169 | else if (state.parameters.exists(base.push(P_MAXSIZE), |
---|
170 | def.push(P_MAXSIZE))) |
---|
171 | state.output.fatal("This GPNodeBuilder has a " + |
---|
172 | P_MAXSIZE + " but not a " + P_MINSIZE + ".", |
---|
173 | base.push(P_MAXSIZE), def.push(P_MAXSIZE)); |
---|
174 | |
---|
175 | // load sizeDistribution |
---|
176 | |
---|
177 | else if (state.parameters.exists(base.push(P_NUMSIZES), |
---|
178 | def.push(P_NUMSIZES))) |
---|
179 | { |
---|
180 | int siz = state.parameters.getInt( |
---|
181 | base.push(P_NUMSIZES), def.push(P_NUMSIZES),1); |
---|
182 | if (siz==0) |
---|
183 | state.output.fatal("The number of sizes in the GPNodeBuilder's distribution must be >= 1. "); |
---|
184 | sizeDistribution = new float[siz]; |
---|
185 | if (state.parameters.exists(base.push(P_SIZE).push("0"), |
---|
186 | def.push(P_SIZE).push("0"))) |
---|
187 | state.output.warning( |
---|
188 | "GPNodeBuilder does not use size #0 in the distribution", |
---|
189 | base.push(P_SIZE).push("0"), |
---|
190 | def.push(P_SIZE).push("0")); |
---|
191 | |
---|
192 | float sum = 0.0f; |
---|
193 | for(int x=0;x<siz;x++) |
---|
194 | { |
---|
195 | sizeDistribution[x] = state.parameters.getFloat( |
---|
196 | base.push(P_SIZE).push(""+(x+1)), |
---|
197 | def.push(P_SIZE).push(""+(x+1)), 0.0f); |
---|
198 | if (sizeDistribution[x]<0.0) |
---|
199 | { |
---|
200 | state.output.warning( |
---|
201 | "Distribution value #" + x + " negative or not defined, assumed to be 0.0", |
---|
202 | base.push(P_SIZE).push(""+(x+1)), |
---|
203 | def.push(P_SIZE).push(""+(x+1))); |
---|
204 | sizeDistribution[x] = 0.0f; |
---|
205 | } |
---|
206 | sum += sizeDistribution[x]; |
---|
207 | } |
---|
208 | if (sum>1.0) |
---|
209 | state.output.warning( |
---|
210 | "Distribution sums to greater than 1.0", |
---|
211 | base.push(P_SIZE), |
---|
212 | def.push(P_SIZE)); |
---|
213 | if (sum==0.0) |
---|
214 | state.output.fatal( |
---|
215 | "Distribution is all 0's", |
---|
216 | base.push(P_SIZE), |
---|
217 | def.push(P_SIZE)); |
---|
218 | |
---|
219 | // normalize and prepare |
---|
220 | RandomChoice.organizeDistribution(sizeDistribution); |
---|
221 | } |
---|
222 | } |
---|
223 | |
---|
224 | public abstract GPNode newRootedTree(final EvolutionState state, |
---|
225 | final GPType type, |
---|
226 | final int thread, |
---|
227 | final GPNodeParent parent, |
---|
228 | final GPFunctionSet set, |
---|
229 | final int argposition, |
---|
230 | final int requestedSize); |
---|
231 | |
---|
232 | /** Issues a warning that no terminal was found with a return type of the given type, and that an algorithm |
---|
233 | had requested one. If fail is true, then a fatal is issued rather than a warning. The warning takes |
---|
234 | the form of a one-time big explanatory message, followed by a one-time-per-type message. */ |
---|
235 | protected void warnAboutNoTerminalWithType(GPType type, boolean fail, EvolutionState state) |
---|
236 | { |
---|
237 | // big explanation -- appears only once |
---|
238 | state.output.warnOnce("A GPNodeBuilder has been requested at least once to generate a one-node tree with " + |
---|
239 | "a return value type-compatable with a certain type; but there is no TERMINAL which is type-compatable " + |
---|
240 | "in this way. As a result, the algorithm was forced to use a NON-TERMINAL, making the tree larger than " + |
---|
241 | "requested, and exposing more child slots to fill, which if not carefully considered, could " + |
---|
242 | "recursively repeat this problem and eventually fill all memory."); |
---|
243 | |
---|
244 | // shorter explanation -- appears for each node builder and type combo |
---|
245 | if (fail) |
---|
246 | state.output.fatal("" + this.getClass() + " can't find a terminal type-compatable with " + type + |
---|
247 | " and cannot replace it with a nonterminal. You may need to try a different node-builder algorithm."); |
---|
248 | else |
---|
249 | state.output.warnOnce("" + this.getClass() + " can't find a terminal type-compatable with " + type); |
---|
250 | } |
---|
251 | |
---|
252 | /** If the given test is true, issues a warning that no terminal was found with a return type of the given type, and that an algorithm |
---|
253 | had requested one. If fail is true, then a fatal is issued rather than a warning. The warning takes |
---|
254 | the form of a one-time big explanatory message, followed by a one-time-per-type message. Returns the value of the test. |
---|
255 | This form makes it easy to insert warnings into if-statements. */ |
---|
256 | protected boolean warnAboutNonterminal(boolean test, GPType type, boolean fail, EvolutionState state) |
---|
257 | { |
---|
258 | if (test) warnAboutNonTerminalWithType(type, fail, state); |
---|
259 | return test; |
---|
260 | } |
---|
261 | |
---|
262 | /** Issues a warning that no nonterminal was found with a return type of the given type, and that an algorithm |
---|
263 | had requested one. If fail is true, then a fatal is issued rather than a warning. The warning takes |
---|
264 | the form of a one-time big explanatory message, followed by a one-time-per-type message. */ |
---|
265 | protected void warnAboutNonTerminalWithType(GPType type, boolean fail, EvolutionState state) |
---|
266 | { |
---|
267 | // big explanation -- appears only once |
---|
268 | state.output.warnOnce("A GPNodeBuilder has been requested at least once to generate a tree with " + |
---|
269 | "a return value type-compatable with a certain type; but there is no NON-TERMINAL which is type-compatable " + |
---|
270 | "in this way. As a result, the algorithm was forced to use a TERMINAL, making the tree smaller than " + |
---|
271 | "requested."); |
---|
272 | |
---|
273 | // shorter explanation -- appears for each node builder and type combo |
---|
274 | if (fail) |
---|
275 | state.output.fatal("" + this.getClass() + " can't find a non-terminal type-compatable with " + type + |
---|
276 | " and cannot replace it with a terminal. You may need to try a different node-builder algorithm."); |
---|
277 | else |
---|
278 | state.output.warnOnce("" + this.getClass() + " can't find a non-terminal type-compatable with " + type); |
---|
279 | } |
---|
280 | |
---|
281 | /** Issues a fatal error that no node (nonterminal or terminal) was found with a return type of the given type, and that an algorithm |
---|
282 | had requested one. */ |
---|
283 | protected void errorAboutNoNodeWithType(GPType type, EvolutionState state) |
---|
284 | { |
---|
285 | state.output.fatal("" + this.getClass() + " could find no terminal or nonterminal type-compatable with " + type); |
---|
286 | } |
---|
287 | } |
---|