[6152] | 1 | /* |
---|
| 2 | Copyright 2006 by Sean Luke and George Mason University |
---|
| 3 | Licensed under the Academic Free License version 3.0 |
---|
| 4 | See the file "LICENSE" for more information |
---|
| 5 | */ |
---|
| 6 | |
---|
| 7 | |
---|
| 8 | package ec.eval; |
---|
| 9 | |
---|
| 10 | import ec.*; |
---|
| 11 | import ec.util.*; |
---|
| 12 | import ec.coevolve.GroupedProblemForm; |
---|
| 13 | import ec.simple.SimpleProblemForm; |
---|
| 14 | import ec.steadystate.QueueIndividual; |
---|
| 15 | import java.util.ArrayList; |
---|
| 16 | |
---|
| 17 | import java.io.*; |
---|
| 18 | |
---|
| 19 | /** |
---|
| 20 | * MasterProblem.java |
---|
| 21 | * |
---|
| 22 | |
---|
| 23 | <p>The MasterProblem is a special ECJ problem that performs evaluations by sending them to |
---|
| 24 | a remote Slave process to be evaluated. As it implements both the |
---|
| 25 | <i>SimpleProblemForm</i> and the <i>GroupedProblemForm</i> interfaces, the MasterProblem |
---|
| 26 | can perform both traditional EC evaluations as well as coevolutionary evaluations. |
---|
| 27 | |
---|
| 28 | <p>When a MasterProblem is specified by the Evaluator, the Problem is set up as usual, but then |
---|
| 29 | the MasterProblem replaces it. The Problem is not garbage collected -- instead, it's hung off the |
---|
| 30 | MasterProblem's <tt>problem</tt> variable. In some sense the Problem is "pushed aside". |
---|
| 31 | |
---|
| 32 | <p>If the Evaluator begins by calling prepareToEvaluate(), and we're not doing coevolution, then |
---|
| 33 | the MasterProblem does not evaluate individuals immediately. Instead, it waits for at most |
---|
| 34 | <i>jobSize</i> individuals be submitted via evaluate(), and then sends them all off in a group, |
---|
| 35 | called a <i>job</i>, to the remote slave. In other situations (coevolution, or no prepareToEvaluate()) |
---|
| 36 | the MasterProblem sends off individuals immediately. |
---|
| 37 | |
---|
| 38 | <p>It may be the case that no Slave has space in its queue to accept a new job containing, among others, |
---|
| 39 | your new individual. In this case, calling evaluate() will block until one comes available. You can avoid |
---|
| 40 | this by testing for availability first by calling canEvaluate(). Note that canEvaluate() and evaluate() |
---|
| 41 | together are not atomic and so you should not rely on this facility if your system uses multiple threads. |
---|
| 42 | |
---|
| 43 | <P>When the individuals or their fitnesses return, they are immediately updated in place. You have three |
---|
| 44 | options to wait for them: |
---|
| 45 | |
---|
| 46 | <ul> |
---|
| 47 | <li><p>You can wait for all the individuals to finish evaluation by calling finishEvaluating(). |
---|
| 48 | If you call this method before a job is entirely filled, it will be sent in truncated format (which |
---|
| 49 | generally is perfectly fine). You then block until all the jobs have been completed and the individuals |
---|
| 50 | updated. |
---|
| 51 | |
---|
| 52 | <li><p>You can block until at least one individual is available, by calling getNextEvaluatedIndividual(), |
---|
| 53 | which blocks and then returns the individual that was just completed. |
---|
| 54 | |
---|
| 55 | <li><p>You can test in non-blocking fashion to see if an individual is available, by calling |
---|
| 56 | evaluatedIndividualAvailable(). If this returns true, you may then call getNextEvaluatedIndividual() |
---|
| 57 | to get the individual. Note that this isn't atomic, so don't use it if you have multiple threads. |
---|
| 58 | </ul> |
---|
| 59 | |
---|
| 60 | <p><b>Parameters</b><br> |
---|
| 61 | <table> |
---|
| 62 | <tr><td valign=top><i>base.</i><tt>debug-info</tt><br> |
---|
| 63 | <font size=-1>boolean</font></td> |
---|
| 64 | <td valign=top>(whether the system should display information useful for debugging purposes)<br> |
---|
| 65 | |
---|
| 66 | <tr><td valign=top><i>base.</i><tt>job-size</tt><br> |
---|
| 67 | <font size=-1>integer > 0 </font></td> |
---|
| 68 | <td valign=top>(how large should a job be at most?)<br> |
---|
| 69 | </td></tr> |
---|
| 70 | |
---|
| 71 | |
---|
| 72 | <!-- technically these are handled by the SlaveMonitor --> |
---|
| 73 | |
---|
| 74 | <tr><td valign=top><tt>eval.master.port</tt><br> |
---|
| 75 | <font size=-1>int</font></td> |
---|
| 76 | <td valign=top>(the port where the slaves will connect)<br> |
---|
| 77 | </td></tr> |
---|
| 78 | <tr><td valign=top><tt>eval.compression</tt><br> |
---|
| 79 | <font size=-1>boolean</font></td> |
---|
| 80 | <td valign=top>(whether the communication with the slaves should be compressed or not)<br> |
---|
| 81 | </td></tr> |
---|
| 82 | <tr><td valign=top><tt>eval.masterproblem.max-jobs-per-slave</tt><br> |
---|
| 83 | <font size=-1>int</font></td> |
---|
| 84 | <td valign=top>(the maximum load (number of jobs) per slave at any point in time)<br> |
---|
| 85 | </td></tr> |
---|
| 86 | |
---|
| 87 | </table> |
---|
| 88 | |
---|
| 89 | * @author Liviu Panait, Keith Sullivan, and Sean Luke |
---|
| 90 | * @version 1.0 |
---|
| 91 | */ |
---|
| 92 | |
---|
| 93 | public class MasterProblem extends Problem implements SimpleProblemForm, GroupedProblemForm |
---|
| 94 | { |
---|
| 95 | public static final String P_DEBUG_INFO = "debug-info"; |
---|
| 96 | public static final String P_JOB_SIZE = "job-size"; |
---|
| 97 | |
---|
| 98 | int jobSize; |
---|
| 99 | boolean showDebugInfo; |
---|
| 100 | public Problem problem; |
---|
| 101 | public boolean batchMode; |
---|
| 102 | public SlaveMonitor monitor; |
---|
| 103 | |
---|
| 104 | // except for the problem, everything else is shallow-cloned |
---|
| 105 | public Object clone() |
---|
| 106 | { |
---|
| 107 | MasterProblem c = (MasterProblem)(super.clone()); |
---|
| 108 | |
---|
| 109 | // shallow-cloned stuff |
---|
| 110 | c.monitor = monitor; |
---|
| 111 | c.batchMode = batchMode; |
---|
| 112 | c.jobSize = jobSize; |
---|
| 113 | c.showDebugInfo = showDebugInfo; |
---|
| 114 | |
---|
| 115 | // deep-cloned stuff |
---|
| 116 | c.problem = (Problem)(problem.clone()); |
---|
| 117 | |
---|
| 118 | return c; |
---|
| 119 | } |
---|
| 120 | |
---|
| 121 | // setup |
---|
| 122 | public void setup(final EvolutionState state, final Parameter base) |
---|
| 123 | { |
---|
| 124 | Thread.currentThread().setName("MainThread: "); |
---|
| 125 | super.setup(state, base); |
---|
| 126 | showDebugInfo = state.parameters.getBoolean(base.push(P_DEBUG_INFO),null,false); |
---|
| 127 | |
---|
| 128 | jobSize = state.parameters.getIntWithDefault(base.push(P_JOB_SIZE),null,1); |
---|
| 129 | if (jobSize<=0) |
---|
| 130 | state.output.fatal("The job size must be an integer > 0.", base.push(P_JOB_SIZE)); |
---|
| 131 | |
---|
| 132 | batchMode = false; |
---|
| 133 | } |
---|
| 134 | |
---|
| 135 | // prepare for a batch of evaluations |
---|
| 136 | public void prepareToEvaluate(final EvolutionState state, final int threadnum) |
---|
| 137 | { |
---|
| 138 | if (jobSize > 1) queue = new ArrayList(); |
---|
| 139 | batchMode = true; |
---|
| 140 | } |
---|
| 141 | |
---|
| 142 | // wait until a batch of evaluations is finished |
---|
| 143 | public void finishEvaluating(final EvolutionState state, final int threadnum) |
---|
| 144 | { |
---|
| 145 | if(showDebugInfo) |
---|
| 146 | state.output.message(Thread.currentThread().getName() + "Waiting for all slaves to finish."); |
---|
| 147 | flush(state, threadnum); |
---|
| 148 | queue = null; // get rid of it just in case |
---|
| 149 | |
---|
| 150 | monitor.waitForAllSlavesToFinishEvaluating( state ); |
---|
| 151 | batchMode = false; |
---|
| 152 | if(showDebugInfo) |
---|
| 153 | state.output.message(Thread.currentThread().getName() + "All slaves have finished their jobs."); |
---|
| 154 | } |
---|
| 155 | |
---|
| 156 | // evaluate a regular individual |
---|
| 157 | public void evaluate(EvolutionState state, Individual ind, int subpopulation, int threadnum) |
---|
| 158 | { |
---|
| 159 | if (jobSize > 1 && batchMode == true) // chunked evaluation mechanism |
---|
| 160 | { |
---|
| 161 | queue.add(new QueueIndividual(ind, subpopulation)); |
---|
| 162 | if (queue.size() >= jobSize) |
---|
| 163 | flush(state, threadnum); |
---|
| 164 | } |
---|
| 165 | else /// ordinary evaluation mechanism |
---|
| 166 | evaluate(state, new Individual[] { ind }, new int[] { subpopulation }, threadnum); |
---|
| 167 | } |
---|
| 168 | |
---|
| 169 | |
---|
| 170 | ArrayList queue; |
---|
| 171 | void flush(EvolutionState state, int threadnum) |
---|
| 172 | { |
---|
| 173 | int subpopulation; |
---|
| 174 | if (queue!=null && queue.size() > 0 ) |
---|
| 175 | { |
---|
| 176 | Individual[] inds = new Individual[queue.size()]; |
---|
| 177 | int[] subpopulations = new int[queue.size()]; |
---|
| 178 | for(int i = 0; i < queue.size(); i++) |
---|
| 179 | { |
---|
| 180 | QueueIndividual qind = (QueueIndividual)(queue.get(i)); |
---|
| 181 | inds[i] = qind.ind; |
---|
| 182 | subpopulations[i] = qind.subpop; |
---|
| 183 | } |
---|
| 184 | evaluate(state, inds, subpopulations, threadnum); |
---|
| 185 | } |
---|
| 186 | queue = new ArrayList(); |
---|
| 187 | } |
---|
| 188 | |
---|
| 189 | |
---|
| 190 | // send a group of individuals to one slave for evaluation |
---|
| 191 | void evaluate(EvolutionState state, Individual inds[], int[] subpopulations, int threadnum) |
---|
| 192 | { |
---|
| 193 | if(showDebugInfo) |
---|
| 194 | state.output.message(Thread.currentThread().getName() + "Starting a " + (batchMode ? "batched " : "") + "SimpleProblemForm evaluation."); |
---|
| 195 | |
---|
| 196 | // Acquire a slave socket |
---|
| 197 | Job job = new Job(); |
---|
| 198 | job.type = Slave.V_EVALUATESIMPLE; |
---|
| 199 | job.inds = inds; |
---|
| 200 | job.subPops = subpopulations ; |
---|
| 201 | job.updateFitness = new boolean[inds.length]; |
---|
| 202 | for (int i=0 ; i < inds.length; i++) |
---|
| 203 | job.updateFitness[i]=true; |
---|
| 204 | monitor.scheduleJobForEvaluation(state,job); |
---|
| 205 | if( !batchMode ) |
---|
| 206 | monitor.waitForAllSlavesToFinishEvaluating( state ); |
---|
| 207 | if(showDebugInfo) state.output.message(Thread.currentThread().getName() + "Finished a " + (batchMode ? "batched " : "") + "SimpleProblemForm evaluation."); |
---|
| 208 | } |
---|
| 209 | |
---|
| 210 | |
---|
| 211 | |
---|
| 212 | |
---|
| 213 | /* (non-Javadoc) |
---|
| 214 | * @see ec.simple.SimpleProblemForm#describe(ec.EvolutionState, ec.Individual, int, int) |
---|
| 215 | */ |
---|
| 216 | public void describe(EvolutionState state, Individual ind, int subpopulation, int threadnum, int log) |
---|
| 217 | { |
---|
| 218 | if ((problem instanceof SimpleProblemForm)) |
---|
| 219 | { |
---|
| 220 | ((SimpleProblemForm)problem).describe(state, ind, subpopulation, threadnum, log); |
---|
| 221 | } |
---|
| 222 | } |
---|
| 223 | |
---|
| 224 | /* (non-Javadoc) |
---|
| 225 | * @see ec.coevolve.GroupedProblemForm#preprocessPopulation(ec.EvolutionState, ec.Population) |
---|
| 226 | */ |
---|
| 227 | public void preprocessPopulation(EvolutionState state, Population pop, boolean countVictoriesOnly) |
---|
| 228 | { |
---|
| 229 | if (!(problem instanceof GroupedProblemForm)) |
---|
| 230 | { |
---|
| 231 | state.output.fatal("MasterProblem.preprocessPopulation(...) invoked, but the underlying Problem is not of GroupedProblemForm"); |
---|
| 232 | } |
---|
| 233 | |
---|
| 234 | ((GroupedProblemForm) problem).preprocessPopulation(state, pop, countVictoriesOnly); |
---|
| 235 | } |
---|
| 236 | |
---|
| 237 | /* (non-Javadoc) |
---|
| 238 | * @see ec.coevolve.GroupedProblemForm#postprocessPopulation(ec.EvolutionState, ec.Population) |
---|
| 239 | */ |
---|
| 240 | public void postprocessPopulation(EvolutionState state, Population pop, boolean countVictoriesOnly) |
---|
| 241 | { |
---|
| 242 | if (!(problem instanceof GroupedProblemForm)) |
---|
| 243 | { |
---|
| 244 | state.output.fatal("MasterProblem.postprocessPopulation(...) invoked, but the underlying Problem is not of GroupedProblemForm"); |
---|
| 245 | } |
---|
| 246 | |
---|
| 247 | ((GroupedProblemForm) problem).postprocessPopulation(state, pop, countVictoriesOnly); |
---|
| 248 | } |
---|
| 249 | |
---|
| 250 | // regular coevolutionary evaluation |
---|
| 251 | public void evaluate(EvolutionState state, Individual[] inds, |
---|
| 252 | boolean[] updateFitness, boolean countVictoriesOnly, int[] subpops, int threadnum) |
---|
| 253 | { |
---|
| 254 | if(showDebugInfo) |
---|
| 255 | state.output.message("Starting a GroupedProblemForm evaluation."); |
---|
| 256 | |
---|
| 257 | // Acquire a slave socket |
---|
| 258 | Job job = new Job(); |
---|
| 259 | job.type = Slave.V_EVALUATEGROUPED; |
---|
| 260 | job.subPops = subpops; |
---|
| 261 | job.countVictoriesOnly = countVictoriesOnly; |
---|
| 262 | job.inds = inds; |
---|
| 263 | job.updateFitness = updateFitness; |
---|
| 264 | monitor.scheduleJobForEvaluation(state,job); |
---|
| 265 | |
---|
| 266 | if( !batchMode ) |
---|
| 267 | monitor.waitForAllSlavesToFinishEvaluating( state ); |
---|
| 268 | |
---|
| 269 | if(showDebugInfo) |
---|
| 270 | state.output.message("Finished the GroupedProblemForm evaluation."); |
---|
| 271 | } |
---|
| 272 | |
---|
| 273 | /** Custom serialization */ |
---|
| 274 | private void writeObject(ObjectOutputStream out) throws IOException |
---|
| 275 | { |
---|
| 276 | out.writeObject(problem); |
---|
| 277 | } |
---|
| 278 | |
---|
| 279 | /** Custom serialization */ |
---|
| 280 | private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException |
---|
| 281 | { |
---|
| 282 | problem = (Problem) in.readObject(); |
---|
| 283 | } |
---|
| 284 | |
---|
| 285 | /** Initialize contacts with the slaves */ |
---|
| 286 | public void initializeContacts( final EvolutionState state ) |
---|
| 287 | { |
---|
| 288 | if(showDebugInfo) |
---|
| 289 | state.output.message(Thread.currentThread().getName() + "Spawning the server thread."); |
---|
| 290 | monitor = new SlaveMonitor(state, showDebugInfo); |
---|
| 291 | } |
---|
| 292 | |
---|
| 293 | /** Reinitialize contacts with the slaves */ |
---|
| 294 | public void reinitializeContacts( final EvolutionState state ) |
---|
| 295 | { |
---|
| 296 | initializeContacts(state); |
---|
| 297 | } |
---|
| 298 | |
---|
| 299 | /** Gracefully close contacts with the slaves */ |
---|
| 300 | public void closeContacts(EvolutionState state, int result) |
---|
| 301 | { |
---|
| 302 | monitor.shutdown(); |
---|
| 303 | } |
---|
| 304 | |
---|
| 305 | public boolean canEvaluate() |
---|
| 306 | { |
---|
| 307 | return (monitor.numAvailableSlaves() != 0); |
---|
| 308 | } |
---|
| 309 | |
---|
| 310 | /** This will only return true if (1) the EvolutionState is a SteadyStateEvolutionState and |
---|
| 311 | (2) an individual has returned from the system. If you're not doing steady state evolution, |
---|
| 312 | you should not call this method. */ |
---|
| 313 | public boolean evaluatedIndividualAvailable() |
---|
| 314 | { |
---|
| 315 | return monitor.evaluatedIndividualAvailable(); |
---|
| 316 | } |
---|
| 317 | |
---|
| 318 | /** This method blocks until an individual is available from the slaves (which will cause evaluatedIndividualAvailable() |
---|
| 319 | to return true), at which time it returns the individual. You should only call this method |
---|
| 320 | if you're doing steady state evolution -- otherwise, the method will block forever. */ |
---|
| 321 | public QueueIndividual getNextEvaluatedIndividual() |
---|
| 322 | { |
---|
| 323 | return monitor.waitForIndividual(); |
---|
| 324 | } |
---|
| 325 | } |
---|