diff --git a/README.md b/README.md index 5b1f879..eea5251 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ First, you need to add the following repository, which is a repository that can ``` Than you can add the dependency on our `aitoa-code` repository into your `dependencies` section. -Here, `0.8.70` is the current version of `aitoa-code`. +Here, `0.8.71` is the current version of `aitoa-code`. Notice that you may have more dependencies in your `dependencies` section, say on `junit`, but here I just put the one for `aitoa-code` as example. ```xml @@ -53,7 +53,7 @@ Notice that you may have more dependencies in your `dependencies` section, say o com.github.thomasWeise aitoa-code - 0.8.70 + 0.8.71 ``` diff --git a/pom.xml b/pom.xml index 76a49fe..0652510 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ aitoa aitoa-code - 0.8.70 + 0.8.71 jar aitoa-code Example Source Codes from the Book "Introduction to Optimization Algorithms" diff --git a/src/main/java/aitoa/algorithms/EA.java b/src/main/java/aitoa/algorithms/EA.java index 9b98c9a..018578f 100644 --- a/src/main/java/aitoa/algorithms/EA.java +++ b/src/main/java/aitoa/algorithms/EA.java @@ -10,9 +10,9 @@ import aitoa.structure.INullarySearchOperator; import aitoa.structure.ISpace; import aitoa.structure.IUnarySearchOperator; -import aitoa.structure.Individual; import aitoa.structure.LogFormat; import aitoa.structure.Metaheuristic2; +import aitoa.structure.Record; import aitoa.utils.Experiment; import aitoa.utils.RandomUtils; @@ -119,14 +119,13 @@ public void solve(final IBlackBoxProcess process) { final ISpace searchSpace = process.getSearchSpace(); int p2; // to hold index of second selected record - final Individual[] P = - new Individual[this.mu + this.lambda]; + final Record[] P = new Record[this.mu + this.lambda]; // start relevant -// first generation: fill population with random individuals +// first generation: fill population with random solutions for (int i = P.length; (--i) >= 0;) { final X x = searchSpace.create(); // allocate point this.nullary.apply(x, random); // fill with random data - P[i] = new Individual<>(x, process.evaluate(x)); // evaluate + P[i] = new Record<>(x, process.evaluate(x)); // evaluate // end relevant if (process.shouldTerminate()) { // we return return; // best solution is stored in process @@ -135,8 +134,8 @@ public void solve(final IBlackBoxProcess process) { } for (;;) { // main loop: one iteration = one generation -// sort the population: mu best individuals at front are selected - Arrays.sort(P, Individual.BY_QUALITY); +// sort the population: mu best records at front are selected + Arrays.sort(P, Record.BY_QUALITY); // shuffle the first mu solutions to ensure fairness RandomUtils.shuffle(random, P, 0, this.mu); int p1 = -1; // index to iterate over first parent @@ -147,16 +146,16 @@ public void solve(final IBlackBoxProcess process) { return; // best solution is stored in process } - final Individual dest = P[index]; + final Record dest = P[index]; p1 = (p1 + 1) % this.mu; // step the parent 1 index - final Individual sel = P[p1]; + final Record sel = P[p1]; // end relevant // start withcrossover if (random.nextDouble() <= this.cr) { // crossover! do { // find a second, different record p2 = random.nextInt(this.mu); } while (p2 == p1); // repeat until p1 != p2 -// perform recombination of the two selected individuals +// perform recombination of the two selected records this.binary.apply(sel.x, P[p2].x, dest.x, random); } else { // end withcrossover diff --git a/src/main/java/aitoa/algorithms/EA1p1WithFitness.java b/src/main/java/aitoa/algorithms/EA1p1WithFitness.java index babb4a4..c60a05e 100644 --- a/src/main/java/aitoa/algorithms/EA1p1WithFitness.java +++ b/src/main/java/aitoa/algorithms/EA1p1WithFitness.java @@ -58,19 +58,17 @@ public EA1p1WithFitness( public void solve(final IBlackBoxProcess process) { // init local variables P, nullary, unary, random // end relevant - final FitnessIndividual[] P = new FitnessIndividual[] { - new FitnessIndividual<>( - process.getSearchSpace().create(), + final FitnessRecord[] P = new FitnessRecord[] { + new FitnessRecord<>(process.getSearchSpace().create(), Double.POSITIVE_INFINITY), - new FitnessIndividual<>( - process.getSearchSpace().create(), + new FitnessRecord<>(process.getSearchSpace().create(), Double.POSITIVE_INFINITY) }; final Random random = process.getRandom();// get random gen this.fitness.initialize(); // start relevant this.nullary.apply(P[0].x, random); // create and evaluate - P[0].quality = process.evaluate(P[0].x); // individual + P[0].quality = process.evaluate(P[0].x); while (!process.shouldTerminate()) { // create a slightly modified copy of xBest and store in xCur @@ -78,10 +76,9 @@ public void solve(final IBlackBoxProcess process) { // map xCur from X to Y and evaluate candidate solution P[1].quality = process.evaluate(P[1].x); this.fitness.assignFitness(P); // compute fitness - if (FitnessIndividual.BY_FITNESS.compare(P[0], - P[1]) >= 0) { - final FitnessIndividual temp = P[0]; - P[0] = P[1]; // if new individual has better or + if (FitnessRecord.BY_FITNESS.compare(P[0], P[1]) >= 0) { + final FitnessRecord temp = P[0]; + P[0] = P[1]; // if new solution has better or P[1] = temp; // equal fitness: accept it } } // until time is up diff --git a/src/main/java/aitoa/algorithms/EA2.java b/src/main/java/aitoa/algorithms/EA2.java new file mode 100644 index 0000000..d81ae91 --- /dev/null +++ b/src/main/java/aitoa/algorithms/EA2.java @@ -0,0 +1,147 @@ +package aitoa.algorithms; + +import java.io.IOException; +import java.io.Writer; +import java.util.Random; + +import aitoa.structure.IBinarySearchOperator; +import aitoa.structure.IBlackBoxProcess; +import aitoa.structure.INullarySearchOperator; +import aitoa.structure.ISpace; +import aitoa.structure.IUnarySearchOperator; +import aitoa.structure.LogFormat; +import aitoa.structure.Metaheuristic2; +import aitoa.structure.Record; +import aitoa.utils.Experiment; + +/** + * A variant of the EA where each offspring competes with their + * directly corresponding parent solution only. It is something + * like a set of parallel {@link EA1p1}, but the addition of + * {@linkplain aitoa.structure.IBinarySearchOperator crossover}. + * + * @param + * the search space + * @param + * the solution space + */ +// start relevant +public final class EA2 extends Metaheuristic2 { +// end relevant + + /** the crossover rate */ + public final double cr; + /** the number of selected parents */ + public final int mu; + + /** + * Create a new instance of the evolutionary algorithm + * + * @param pNullary + * the nullary search operator. + * @param pUnary + * the unary search operator + * @param pBinary + * the binary search operator + * @param pCr + * the crossover rate + * @param pMu + * the number of parents to be selected + */ + public EA2(final INullarySearchOperator pNullary, + final IUnarySearchOperator pUnary, + final IBinarySearchOperator pBinary, final double pCr, + final int pMu) { + super(pNullary, pUnary, pBinary); + if ((pCr < 0d) || (pCr > 1d) || (!(Double.isFinite(pCr)))) { + throw new IllegalArgumentException( + "Invalid crossover rate: " + pCr); //$NON-NLS-1$ + } + this.cr = pCr; + if ((pMu < 1) || (pMu > 1_000_000)) { + throw new IllegalArgumentException("Invalid mu: " + pMu); //$NON-NLS-1$ + } + if ((pMu <= 1) && (pCr > 0d)) { + throw new IllegalArgumentException(// + "crossover rate must be 0 if mu is 1, but cr is " //$NON-NLS-1$ + + pCr); + } + this.mu = pMu; + } + + /** {@inheritDoc} */ + @SuppressWarnings("unchecked") + @Override +// start relevant + public void solve(final IBlackBoxProcess process) { +// omitted: initialize local variables random, searchSpace, and +// array P of length mu and variable dest for new solution +// end relevant +// create local variables + final Random random = process.getRandom(); + final ISpace searchSpace = process.getSearchSpace(); + int p2; // to hold index of second selected record + + final Record[] P = new Record[this.mu]; + Record dest = + new Record<>(searchSpace.create(), Double.NaN); +// start relevant +// first generation: fill population with random solutions + for (int i = P.length; (--i) >= 0;) { + final X x = searchSpace.create(); // allocate point + this.nullary.apply(x, random); // fill with random data + P[i] = new Record<>(x, process.evaluate(x)); // evaluate +// end relevant + if (process.shouldTerminate()) { // we return + return; // best solution is stored in process + } +// start relevant + } + + int p1 = -1; // index to iterate over first parent + while (!process.shouldTerminate()) { // main loop + p1 = (p1 + 1) % this.mu; // loop over parent population + final Record s1 = P[p1]; + if (random.nextDouble() <= this.cr) { // crossover! + do { // find a second, different record + p2 = random.nextInt(this.mu); + } while (p2 == p1); // repeat until p1 != p2 +// perform recombination of the two selected solutions + this.binary.apply(s1.x, P[p2].x, dest.x, random); + } else { // perform unary operation (i.e., mutation) +// create modified copy of parent using unary operator + this.unary.apply(s1.x, dest.x, random); + } // end mutation + dest.quality = process.evaluate(dest.x); + if (dest.quality <= s1.quality) { // compare dest with s1 + P[p1] = dest; + dest = s1; + } + } // the end of the main loop + } +// end relevant + + /** {@inheritDoc} */ + @Override + public void printSetup(final Writer output) + throws IOException { + output.write(LogFormat.mapEntry(// + LogFormat.SETUP_BASE_ALGORITHM, "ea2")); //$NON-NLS-1$ + output.write(System.lineSeparator()); + super.printSetup(output); + output.write(LogFormat.mapEntry("mu", this.mu));///$NON-NLS-1$ + output.write(System.lineSeparator()); + output.write(LogFormat.mapEntry("cr", this.cr));//$NON-NLS-1$ + output.write(System.lineSeparator()); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return Experiment.nameFromObjectsMerge(((("ea2_" + //$NON-NLS-1$ + this.mu) + '@') + this.cr), // + this.unary, this.binary); + } +// start relevant +} +// end relevant diff --git a/src/main/java/aitoa/algorithms/EAWithClearing.java b/src/main/java/aitoa/algorithms/EAWithClearing.java index ba89324..560a323 100644 --- a/src/main/java/aitoa/algorithms/EAWithClearing.java +++ b/src/main/java/aitoa/algorithms/EAWithClearing.java @@ -9,9 +9,9 @@ import aitoa.structure.INullarySearchOperator; import aitoa.structure.ISpace; import aitoa.structure.IUnarySearchOperator; -import aitoa.structure.Individual; import aitoa.structure.LogFormat; import aitoa.structure.Metaheuristic2; +import aitoa.structure.Record; import aitoa.utils.Experiment; import aitoa.utils.RandomUtils; @@ -108,15 +108,14 @@ public void solve(final IBlackBoxProcess process) { final ISpace searchSpace = process.getSearchSpace(); int p2; - final Individual[] P = - new Individual[this.mu + this.lambda]; + final Record[] P = new Record[this.mu + this.lambda]; -// first generation: fill population with random individuals +// first generation: fill population with random solutions // start relevant for (int i = P.length; (--i) >= 0;) { final X x = searchSpace.create(); this.nullary.apply(x, random); - P[i] = new Individual<>(x, process.evaluate(x)); + P[i] = new Record<>(x, process.evaluate(x)); if (process.shouldTerminate()) { // we return return; // best solution is stored in process } @@ -136,9 +135,9 @@ public void solve(final IBlackBoxProcess process) { return; // The best solution is stored in process. } // start relevant - final Individual dest = P[index]; // offspring + final Record dest = P[index]; // offspring p1 = (p1 + 1) % u; // parent 1 index - final Individual sel = P[p1]; // parent 1 + final Record sel = P[p1]; // parent 1 if ((u >= 2) && (random.nextDouble() <= this.cr)) { do { // find a second, different record p2 = random.nextInt(u); diff --git a/src/main/java/aitoa/algorithms/EAWithFitness.java b/src/main/java/aitoa/algorithms/EAWithFitness.java index b83062c..57b21f1 100644 --- a/src/main/java/aitoa/algorithms/EAWithFitness.java +++ b/src/main/java/aitoa/algorithms/EAWithFitness.java @@ -98,15 +98,15 @@ public void solve(final IBlackBoxProcess process) { final Random random = process.getRandom(); final ISpace searchSpace = process.getSearchSpace(); int p2; // to hold index of second selected record - final FitnessIndividual[] P = - new FitnessIndividual[this.mu + this.lambda]; + final FitnessRecord[] P = + new FitnessRecord[this.mu + this.lambda]; this.fitness.initialize(); // start relevant -// first generation: fill population with random individuals +// first generation: fill population with random solutions for (int i = P.length; (--i) >= 0;) { final X x = searchSpace.create(); this.nullary.apply(x, random); - P[i] = new FitnessIndividual<>(x, process.evaluate(x)); + P[i] = new FitnessRecord<>(x, process.evaluate(x)); // end relevant if (process.shouldTerminate()) { // we return return; // best solution is stored in process @@ -115,9 +115,9 @@ public void solve(final IBlackBoxProcess process) { } for (;;) { // main loop: one iteration = one generation -// sort the population: mu best individuals at front are selected +// sort the population: mu best records at front are selected this.fitness.assignFitness(P); - Arrays.sort(P, FitnessIndividual.BY_FITNESS); + Arrays.sort(P, FitnessRecord.BY_FITNESS); // shuffle the first mu solutions to ensure fairness RandomUtils.shuffle(random, P, 0, this.mu); int p1 = -1; // index to iterate over first parent @@ -128,14 +128,14 @@ public void solve(final IBlackBoxProcess process) { return; // best solution is stored in process } - final FitnessIndividual dest = P[index]; + final FitnessRecord dest = P[index]; p1 = (p1 + 1) % this.mu; - final FitnessIndividual sel = P[p1]; + final FitnessRecord sel = P[p1]; if (random.nextDouble() <= this.cr) { // crossover! do { // find a second, different record p2 = random.nextInt(this.mu); } while (p2 == p1); -// perform recombination of the two selected individuals +// perform recombination of the two selected records this.binary.apply(sel.x, P[p2].x, dest.x, random); } else { // create modified copy of parent using unary operator diff --git a/src/main/java/aitoa/algorithms/EAWithRestarts.java b/src/main/java/aitoa/algorithms/EAWithRestarts.java index e429841..33e05d4 100644 --- a/src/main/java/aitoa/algorithms/EAWithRestarts.java +++ b/src/main/java/aitoa/algorithms/EAWithRestarts.java @@ -10,9 +10,9 @@ import aitoa.structure.INullarySearchOperator; import aitoa.structure.ISpace; import aitoa.structure.IUnarySearchOperator; -import aitoa.structure.Individual; import aitoa.structure.LogFormat; import aitoa.structure.Metaheuristic2; +import aitoa.structure.Record; import aitoa.utils.Experiment; import aitoa.utils.RandomUtils; @@ -103,18 +103,17 @@ public void solve(final IBlackBoxProcess process) { final ISpace searchSpace = process.getSearchSpace(); int p2; - final Individual[] P = - new Individual[this.mu + this.lambda]; + final Record[] P = new Record[this.mu + this.lambda]; while (!process.shouldTerminate()) { // restart double bestF = Double.POSITIVE_INFINITY; int nonImprovedGen = 0; -// first generation: fill population with random individuals +// first generation: fill population with random solutions for (int i = P.length; (--i) >= 0;) { final X x = searchSpace.create(); this.nullary.apply(x, random); - P[i] = new Individual<>(x, process.evaluate(x)); + P[i] = new Record<>(x, process.evaluate(x)); if (process.shouldTerminate()) { return; } @@ -124,8 +123,8 @@ public void solve(final IBlackBoxProcess process) { // main loop: one iteration = one generation ++nonImprovedGen; // assume no improvement -// sort the P: mu best individuals at front are selected - Arrays.sort(P, Individual.BY_QUALITY); +// sort the P: mu best records at front are selected + Arrays.sort(P, Record.BY_QUALITY); // shuffle mating pool to ensure fairness if lambda process) { return; // return best solution } - final Individual dest = P[index]; + final Record dest = P[index]; p1 = (p1 + 1) % this.mu; - final Individual parent1 = P[p1]; + final Record parent1 = P[p1]; if (random.nextDouble() <= this.cr) { // crossover! do { // find a second parent who is different from diff --git a/src/main/java/aitoa/algorithms/EDA.java b/src/main/java/aitoa/algorithms/EDA.java index 358a062..c52416b 100644 --- a/src/main/java/aitoa/algorithms/EDA.java +++ b/src/main/java/aitoa/algorithms/EDA.java @@ -10,9 +10,9 @@ import aitoa.structure.IModel; import aitoa.structure.INullarySearchOperator; import aitoa.structure.ISpace; -import aitoa.structure.Individual; import aitoa.structure.LogFormat; import aitoa.structure.Metaheuristic0; +import aitoa.structure.Record; import aitoa.utils.Experiment; /** @@ -87,17 +87,17 @@ public void solve(final IBlackBoxProcess process) { final ISpace searchSpace = process.getSearchSpace(); final IModel M = this.model; - final Individual[] P = new Individual[this.lambda]; + final Record[] P = new Record[this.lambda]; restart: while (!process.shouldTerminate()) { // start relevant // local variable initialization omitted for brevity M.initialize(); // initialize to uniform distribution -// first generation: fill population with random individuals +// first generation: fill population with random solutions for (int i = P.length; (--i) >= 0;) { final X x = searchSpace.create(); this.nullary.apply(x, random); - P[i] = new Individual<>(x, process.evaluate(x)); + P[i] = new Record<>(x, process.evaluate(x)); // end relevant if (process.shouldTerminate()) { // we return return; // best solution is stored in process @@ -111,12 +111,12 @@ public void solve(final IBlackBoxProcess process) { continue restart; } // start relevant - Arrays.sort(P, Individual.BY_QUALITY); + Arrays.sort(P, Record.BY_QUALITY); // update model with mu dest : P) { + for (final Record dest : P) { M.apply(dest.x, random); // create new solution dest.quality = process.evaluate(dest.x); if (process.shouldTerminate()) { // we return diff --git a/src/main/java/aitoa/algorithms/EDAWithClearing.java b/src/main/java/aitoa/algorithms/EDAWithClearing.java index 00e6ba1..0855f54 100644 --- a/src/main/java/aitoa/algorithms/EDAWithClearing.java +++ b/src/main/java/aitoa/algorithms/EDAWithClearing.java @@ -9,9 +9,9 @@ import aitoa.structure.IModel; import aitoa.structure.INullarySearchOperator; import aitoa.structure.ISpace; -import aitoa.structure.Individual; import aitoa.structure.LogFormat; import aitoa.structure.Metaheuristic0; +import aitoa.structure.Record; import aitoa.utils.Experiment; /** @@ -89,17 +89,17 @@ public void solve(final IBlackBoxProcess process) { final ISpace searchSpace = process.getSearchSpace(); final IModel M = this.model; - final Individual[] P = new Individual[this.lambda]; + final Record[] P = new Record[this.lambda]; // start relevant // local variable initialization omitted for brevity restart: while (!process.shouldTerminate()) { M.initialize(); // initialize to uniform distribution -// first generation: fill population with random individuals +// first generation: fill population with random solutions for (int i = P.length; (--i) >= 0;) { final X x = searchSpace.create(); this.nullary.apply(x, random); - P[i] = new Individual<>(x, process.evaluate(x)); + P[i] = new Record<>(x, process.evaluate(x)); // end relevant if (process.shouldTerminate()) { // we return return; // best solution is stored in process @@ -116,7 +116,7 @@ public void solve(final IBlackBoxProcess process) { M.update(IModel.use(P, 0, u)); // sample new population - for (final Individual dest : P) { + for (final Record dest : P) { M.apply(dest.x, random); // create new solution dest.quality = process.evaluate(dest.x); if (process.shouldTerminate()) { // we return diff --git a/src/main/java/aitoa/algorithms/EDAWithFitness.java b/src/main/java/aitoa/algorithms/EDAWithFitness.java index 50224c5..486fd3f 100644 --- a/src/main/java/aitoa/algorithms/EDAWithFitness.java +++ b/src/main/java/aitoa/algorithms/EDAWithFitness.java @@ -115,8 +115,7 @@ public void solve(final IBlackBoxProcess process) { final ISpace searchSpace = process.getSearchSpace(); final IModel M = this.model; - final FitnessIndividual[] P = - new FitnessIndividual[this.lambda]; + final FitnessRecord[] P = new FitnessRecord[this.lambda]; this.fitness.initialize(); // end relevant @@ -124,11 +123,11 @@ public void solve(final IBlackBoxProcess process) { // start relevant // local variable initialization omitted for brevity M.initialize(); // initialize to uniform distribution -// first generation: fill population with random individuals +// first generation: fill population with random solutions for (int i = P.length; (--i) >= 0;) { final X x = searchSpace.create(); this.nullary.apply(x, random); - P[i] = new FitnessIndividual<>(x, process.evaluate(x)); + P[i] = new FitnessRecord<>(x, process.evaluate(x)); if (process.shouldTerminate()) { // we return return; // best solution is stored in process } @@ -141,12 +140,12 @@ public void solve(final IBlackBoxProcess process) { } // start relevant this.fitness.assignFitness(P); - Arrays.sort(P, FitnessIndividual.BY_FITNESS); + Arrays.sort(P, FitnessRecord.BY_FITNESS); // update model with mu dest : P) { + for (final FitnessRecord dest : P) { M.apply(dest.x, random); // create new solution dest.quality = process.evaluate(dest.x); if (process.shouldTerminate()) { // we return diff --git a/src/main/java/aitoa/algorithms/FitnessAssignmentProcess.java b/src/main/java/aitoa/algorithms/FitnessAssignmentProcess.java index 9e1a683..47e5e36 100644 --- a/src/main/java/aitoa/algorithms/FitnessAssignmentProcess.java +++ b/src/main/java/aitoa/algorithms/FitnessAssignmentProcess.java @@ -12,8 +12,8 @@ public abstract class FitnessAssignmentProcess implements ISetupPrintable { /** - * Assign the fitness a set of individuals. This process will - * fill the {@link FitnessIndividual#fitness} variable with a + * Assign the fitness a set of solution records. This process + * will fill the {@link FitnessRecord#fitness} variable with a * value based on the set of provided records. * * @param pop @@ -21,7 +21,7 @@ public abstract class FitnessAssignmentProcess * search space and a quality value. */ public abstract void - assignFitness(FitnessIndividual[] pop); + assignFitness(FitnessRecord[] pop); /** initialize the fitness assignment process */ public void initialize() { diff --git a/src/main/java/aitoa/algorithms/FitnessIndividual.java b/src/main/java/aitoa/algorithms/FitnessIndividual.java deleted file mode 100644 index d0143cf..0000000 --- a/src/main/java/aitoa/algorithms/FitnessIndividual.java +++ /dev/null @@ -1,35 +0,0 @@ -package aitoa.algorithms; - -import java.util.Comparator; - -import aitoa.structure.Individual; - -/** - * A shared class for individual records. Such a record record - * holds one point in search space along with its quality. - * - * @param - * the search space - */ -public class FitnessIndividual extends Individual { - - /** The comparator to be used for sorting according fitness */ - public static final Comparator< - FitnessIndividual> BY_FITNESS = - (a, b) -> Double.compare(a.fitness, b.fitness); - - /** the fitness */ - double fitness; - - /** - * create the individual record - * - * @param pX - * the point in the search space - * @param pQ - * the quality - */ - public FitnessIndividual(final X pX, final double pQ) { - super(pX, pQ); - } -} diff --git a/src/main/java/aitoa/algorithms/FitnessRecord.java b/src/main/java/aitoa/algorithms/FitnessRecord.java new file mode 100644 index 0000000..dde056d --- /dev/null +++ b/src/main/java/aitoa/algorithms/FitnessRecord.java @@ -0,0 +1,43 @@ +package aitoa.algorithms; + +import java.util.Comparator; + +import aitoa.structure.Record; + +/** + * A shared class for fitness records. Such a record holds one + * point in search space along with its quality and a + * fitness value. The {@link #quality} is the result of the + * objective function evaluation. The {@link #fitness} is + * computed by a {@link FitnessAssignmentProcess} and can combine + * the {@link #quality} with additional information useful for + * the search, such as the density of the solutions in a + * population. + * + * @param + * the search space + */ +public class FitnessRecord extends Record { + + /** + * The comparator to be used for sorting according fitness: + * smaller fitness values are better + */ + public static final Comparator> BY_FITNESS = + (a, b) -> Double.compare(a.fitness, b.fitness); + + /** the fitness */ + double fitness; + + /** + * create the record + * + * @param pX + * the point in the search space + * @param pQ + * the quality + */ + public FitnessRecord(final X pX, final double pQ) { + super(pX, pQ); + } +} diff --git a/src/main/java/aitoa/algorithms/HybridEDA.java b/src/main/java/aitoa/algorithms/HybridEDA.java index 07798c7..416519a 100644 --- a/src/main/java/aitoa/algorithms/HybridEDA.java +++ b/src/main/java/aitoa/algorithms/HybridEDA.java @@ -11,9 +11,9 @@ import aitoa.structure.INullarySearchOperator; import aitoa.structure.ISpace; import aitoa.structure.IUnarySearchOperator; -import aitoa.structure.Individual; import aitoa.structure.LogFormat; import aitoa.structure.Metaheuristic1; +import aitoa.structure.Record; import aitoa.utils.Experiment; /** @@ -96,25 +96,25 @@ public void solve(final IBlackBoxProcess process) { final ISpace searchSpace = process.getSearchSpace(); final IModel M = ((IModel) (this.model)); boolean improved; - final Individual[] P = new Individual[this.lambda]; + final Record[] P = new Record[this.lambda]; final X temp = searchSpace.create(); restart: while (!process.shouldTerminate()) { // the initialization of local variables is omitted for brevity M.initialize(); // initialize as uniform distribution -// first generation: fill population with random individuals +// first generation: fill population with random solutions for (int i = P.length; (--i) >= 0;) { final X x = searchSpace.create(); this.nullary.apply(x, random); - P[i] = new Individual<>(x, process.evaluate(x)); + P[i] = new Record<>(x, process.evaluate(x)); if (process.shouldTerminate()) { // we return return; // best solution is stored in process } } for (;;) { // each iteration: LS, update model, then sample - for (final Individual ind : P) { + for (final Record ind : P) { int steps = this.maxLSSteps; do { // local search in style of HillClimber2 improved = this.unary.enumerate(random, ind.x, temp, // @@ -137,11 +137,11 @@ public void solve(final IBlackBoxProcess process) { if (this.mu < M.minimumSamplesNeededForUpdate()) { continue restart; } - Arrays.sort(P, Individual.BY_QUALITY); + Arrays.sort(P, Record.BY_QUALITY); M.update(IModel.use(P, 0, this.mu)); // update // sample new population - for (final Individual dest : P) { + for (final Record dest : P) { if (process.shouldTerminate()) { // we return return; // best solution is stored in process } diff --git a/src/main/java/aitoa/algorithms/HybridEDAWithClearing.java b/src/main/java/aitoa/algorithms/HybridEDAWithClearing.java index c453ce0..7424420 100644 --- a/src/main/java/aitoa/algorithms/HybridEDAWithClearing.java +++ b/src/main/java/aitoa/algorithms/HybridEDAWithClearing.java @@ -11,9 +11,9 @@ import aitoa.structure.INullarySearchOperator; import aitoa.structure.ISpace; import aitoa.structure.IUnarySearchOperator; -import aitoa.structure.Individual; import aitoa.structure.LogFormat; import aitoa.structure.Metaheuristic1; +import aitoa.structure.Record; import aitoa.utils.Experiment; /** @@ -97,25 +97,25 @@ public void solve(final IBlackBoxProcess process) { final ISpace searchSpace = process.getSearchSpace(); final IModel M = ((IModel) (this.model)); boolean improved; - final Individual[] P = new Individual[this.lambda]; + final Record[] P = new Record[this.lambda]; final X temp = searchSpace.create(); restart: while (!process.shouldTerminate()) { // the initialization of local variables is omitted for brevity M.initialize(); // initialize to uniform distribution -// first generation: fill population with random individuals +// first generation: fill population with random solutions for (int i = P.length; (--i) >= 0;) { final X x = searchSpace.create(); this.nullary.apply(x, random); - P[i] = new Individual<>(x, process.evaluate(x)); + P[i] = new Record<>(x, process.evaluate(x)); if (process.shouldTerminate()) { // we return return; // best solution is stored in process } } for (;;) { // each iteration: LS, update model, then sample - for (final Individual ind : P) { + for (final Record ind : P) { int steps = this.maxLSSteps; do { // local search in style of HillClimber2 improved = this.unary.enumerate(random, ind.x, temp, // @@ -135,7 +135,7 @@ public void solve(final IBlackBoxProcess process) { } while (improved && ((--steps) > 0)); } - Arrays.sort(P, Individual.BY_QUALITY); + Arrays.sort(P, Record.BY_QUALITY); final int u = Utils.qualityBasedClearing(P, this.mu); if (u < M.minimumSamplesNeededForUpdate()) { continue restart; @@ -143,7 +143,7 @@ public void solve(final IBlackBoxProcess process) { M.update(IModel.use(P, 0, u)); // update // sample new population - for (final Individual dest : P) { + for (final Record dest : P) { if (process.shouldTerminate()) { // we return return; // best solution is stored in process } diff --git a/src/main/java/aitoa/algorithms/HybridEDAWithFitness.java b/src/main/java/aitoa/algorithms/HybridEDAWithFitness.java index 5603071..529e3a5 100644 --- a/src/main/java/aitoa/algorithms/HybridEDAWithFitness.java +++ b/src/main/java/aitoa/algorithms/HybridEDAWithFitness.java @@ -99,8 +99,7 @@ public void solve(final IBlackBoxProcess process) { final ISpace searchSpace = process.getSearchSpace(); final IModel M = ((IModel) (this.model)); boolean improved; - final FitnessIndividual[] P = - new FitnessIndividual[this.lambda]; + final FitnessRecord[] P = new FitnessRecord[this.lambda]; final X temp = searchSpace.create(); this.fitness.initialize(); @@ -108,18 +107,18 @@ public void solve(final IBlackBoxProcess process) { // the initialization of local variables is omitted for brevity M.initialize(); // initialize to uniform distribution -// first generation: fill population with random individuals +// first generation: fill population with random solutions for (int i = P.length; (--i) >= 0;) { final X x = searchSpace.create(); this.nullary.apply(x, random); - P[i] = new FitnessIndividual<>(x, process.evaluate(x)); + P[i] = new FitnessRecord<>(x, process.evaluate(x)); if (process.shouldTerminate()) { // we return return; // best solution is stored in process } } for (;;) { // each iteration: LS, update model, then sample - for (final FitnessIndividual ind : P) { + for (final FitnessRecord ind : P) { int steps = this.maxLSSteps; do { // local search in style of HillClimber2 improved = this.unary.enumerate(random, ind.x, temp, // @@ -144,11 +143,11 @@ public void solve(final IBlackBoxProcess process) { continue restart; } this.fitness.assignFitness(P); - Arrays.sort(P, FitnessIndividual.BY_FITNESS); + Arrays.sort(P, FitnessRecord.BY_FITNESS); M.update(IModel.use(P, 0, this.mu)); // update // sample new population - for (final FitnessIndividual dest : P) { + for (final FitnessRecord dest : P) { if (process.shouldTerminate()) { // we return return; // best solution is stored in process } diff --git a/src/main/java/aitoa/algorithms/IntFFA.java b/src/main/java/aitoa/algorithms/IntFFA.java index f6d02f6..6485d55 100644 --- a/src/main/java/aitoa/algorithms/IntFFA.java +++ b/src/main/java/aitoa/algorithms/IntFFA.java @@ -32,14 +32,14 @@ public void initialize() { /** {@inheritDoc} */ @Override public void assignFitness( - final FitnessIndividual[] pop) { - for (final FitnessIndividual ind : pop) { + final FitnessRecord[] pop) { + for (final FitnessRecord ind : pop) { final double d = ind.quality; if (Double.isFinite(d)) { ++this.mFrequencies[((int) (d))]; } } - for (final FitnessIndividual ind : pop) { + for (final FitnessRecord ind : pop) { final double d = ind.quality; ind.fitness = Double.isFinite(d) ? this.mFrequencies[((int) (d))] diff --git a/src/main/java/aitoa/algorithms/LSFitnessIndividual.java b/src/main/java/aitoa/algorithms/LSFitnessRecord.java similarity index 53% rename from src/main/java/aitoa/algorithms/LSFitnessIndividual.java rename to src/main/java/aitoa/algorithms/LSFitnessRecord.java index cd401e9..f2df967 100644 --- a/src/main/java/aitoa/algorithms/LSFitnessIndividual.java +++ b/src/main/java/aitoa/algorithms/LSFitnessRecord.java @@ -1,26 +1,26 @@ package aitoa.algorithms; /** - * An individual as used by memetic algorithms that perform - * partial local search and use fitness assignment. + * A record as used by memetic algorithms that perform partial + * local search and use fitness assignment. * * @param * the search space */ -final class LSFitnessIndividual extends FitnessIndividual { +final class LSFitnessRecord extends FitnessRecord { /** it has been confirmed that this is a local optimum */ boolean isOptimum; /** - * create the individual record + * create the record * * @param pX * the point in the search space * @param pQ * the quality */ - LSFitnessIndividual(final X pX, final double pQ) { + LSFitnessRecord(final X pX, final double pQ) { super(pX, pQ); } } diff --git a/src/main/java/aitoa/algorithms/LSIndividual.java b/src/main/java/aitoa/algorithms/LSRecord.java similarity index 55% rename from src/main/java/aitoa/algorithms/LSIndividual.java rename to src/main/java/aitoa/algorithms/LSRecord.java index 8690585..7c6ae5f 100644 --- a/src/main/java/aitoa/algorithms/LSIndividual.java +++ b/src/main/java/aitoa/algorithms/LSRecord.java @@ -1,28 +1,28 @@ package aitoa.algorithms; -import aitoa.structure.Individual; +import aitoa.structure.Record; /** - * An individual as used by memetic algorithms that perform - * partial local search + * A record as used by memetic algorithms that perform partial + * local search * * @param * the search space */ -final class LSIndividual extends Individual { +final class LSRecord extends Record { /** it has been confirmed that this is a local optimum */ boolean isOptimum; /** - * create the individual record + * create the record * * @param pX * the point in the search space * @param pQ * the quality */ - LSIndividual(final X pX, final double pQ) { + LSRecord(final X pX, final double pQ) { super(pX, pQ); } } diff --git a/src/main/java/aitoa/algorithms/MA.java b/src/main/java/aitoa/algorithms/MA.java index aed8753..e3d0160 100644 --- a/src/main/java/aitoa/algorithms/MA.java +++ b/src/main/java/aitoa/algorithms/MA.java @@ -10,9 +10,9 @@ import aitoa.structure.INullarySearchOperator; import aitoa.structure.ISpace; import aitoa.structure.IUnarySearchOperator; -import aitoa.structure.Individual; import aitoa.structure.LogFormat; import aitoa.structure.Metaheuristic2; +import aitoa.structure.Record; import aitoa.utils.Experiment; import aitoa.utils.RandomUtils; @@ -97,16 +97,15 @@ public void solve(final IBlackBoxProcess process) { final X temp = searchSpace.create(); int p2; - final LSIndividual[] P = - new LSIndividual[this.mu + this.lambda]; + final LSRecord[] P = new LSRecord[this.mu + this.lambda]; // start relevant -// first generation: fill population with random individuals +// first generation: fill population with random solutions for (int i = P.length; (--i) >= 0;) { -// set P[i] = random individual (code omitted) +// set P[i] = random solution (code omitted) // end relevant final X x = searchSpace.create(); this.nullary.apply(x, random); - P[i] = new LSIndividual<>(x, process.evaluate(x)); + P[i] = new LSRecord<>(x, process.evaluate(x)); if (process.shouldTerminate()) { // we return return; // best solution is stored in process } @@ -114,7 +113,7 @@ public void solve(final IBlackBoxProcess process) { } while (!process.shouldTerminate()) { // main loop - for (final LSIndividual ind : P) { + for (final LSRecord ind : P) { // If ind is not known to be local optimum, refine it with local // search a la HillClimber2 for a given number of maximum steps // (code omitted for brevity). @@ -142,8 +141,8 @@ public void solve(final IBlackBoxProcess process) { ind.isOptimum = !improved; // is it a local optimum? // start relevant } // end of 1 ls iteration: we have refined 1 solution -// sort the population: mu best individuals at front are selected - Arrays.sort(P, Individual.BY_QUALITY); +// sort the population: mu best records at front are selected + Arrays.sort(P, Record.BY_QUALITY); // shuffle the first mu solutions to ensure fairness RandomUtils.shuffle(random, P, 0, this.mu); int p1 = -1; // index to iterate over first parent @@ -155,13 +154,13 @@ public void solve(final IBlackBoxProcess process) { return; // best solution is stored in process } // start relevant - final LSIndividual dest = P[index]; - final LSIndividual sel = P[(++p1) % this.mu]; + final LSRecord dest = P[index]; + final LSRecord sel = P[(++p1) % this.mu]; do { // find a second, different record p2 = random.nextInt(this.mu); } while (p2 == p1); -// perform recombination of the two selected individuals +// perform recombination of the two selected solutions this.binary.apply(sel.x, P[p2].x, dest.x, random); dest.quality = process.evaluate(dest.x); // end relevant diff --git a/src/main/java/aitoa/algorithms/MAWithClearing.java b/src/main/java/aitoa/algorithms/MAWithClearing.java index e51c6e0..2fbd727 100644 --- a/src/main/java/aitoa/algorithms/MAWithClearing.java +++ b/src/main/java/aitoa/algorithms/MAWithClearing.java @@ -108,24 +108,23 @@ public void solve(final IBlackBoxProcess process) { final Random random = process.getRandom(); final ISpace searchSpace = process.getSearchSpace(); final X temp = searchSpace.create(); - final LSIndividual[] P = - new LSIndividual[this.mu + this.lambda]; + final LSRecord[] P = new LSRecord[this.mu + this.lambda]; boolean improved = false; int p2 = -1; restart: while (!process.shouldTerminate()) { -// first generation: fill population with random individuals +// first generation: fill population with random solutions for (int i = P.length; (--i) >= 0;) { final X x = searchSpace.create(); this.nullary.apply(x, random); - P[i] = new LSIndividual<>(x, process.evaluate(x)); + P[i] = new LSRecord<>(x, process.evaluate(x)); if (process.shouldTerminate()) { return; } } while (!process.shouldTerminate()) { // main loop - for (final LSIndividual ind : P) { + for (final LSRecord ind : P) { if (ind.isOptimum) { continue; } @@ -164,9 +163,9 @@ public void solve(final IBlackBoxProcess process) { if (process.shouldTerminate()) { // we return return; // best solution is stored in process } - final LSIndividual dest = P[index]; + final LSRecord dest = P[index]; p1 = (p1 + 1) % u; - final LSIndividual sel = P[p1]; + final LSRecord sel = P[p1]; // to hold index of second selected record do { p2 = random.nextInt(u); diff --git a/src/main/java/aitoa/algorithms/MAWithFitness.java b/src/main/java/aitoa/algorithms/MAWithFitness.java index 19df76c..41a06af 100644 --- a/src/main/java/aitoa/algorithms/MAWithFitness.java +++ b/src/main/java/aitoa/algorithms/MAWithFitness.java @@ -94,22 +94,22 @@ public void solve(final IBlackBoxProcess process) { final X temp = searchSpace.create(); int p2; - final LSFitnessIndividual[] P = - new LSFitnessIndividual[this.mu + this.lambda]; + final LSFitnessRecord[] P = + new LSFitnessRecord[this.mu + this.lambda]; this.fitness.initialize(); -// first generation: fill population with random individuals +// first generation: fill population with random solutions for (int i = P.length; (--i) >= 0;) { -// set P[i] = random individual (code omitted) +// set P[i] = random solution (code omitted) final X x = searchSpace.create(); this.nullary.apply(x, random); - P[i] = new LSFitnessIndividual<>(x, process.evaluate(x)); + P[i] = new LSFitnessRecord<>(x, process.evaluate(x)); if (process.shouldTerminate()) { // we return return; // best solution is stored in process } } while (!process.shouldTerminate()) { // main loop - for (final LSFitnessIndividual ind : P) { + for (final LSFitnessRecord ind : P) { if (ind.isOptimum) { continue; } @@ -134,9 +134,9 @@ public void solve(final IBlackBoxProcess process) { ind.isOptimum = !improved; // is it a local optimum? } // end of 1 ls iteration: we have refined 1 solution -// sort the population: mu best individuals at front are selected +// sort the population: mu best records at front are selected this.fitness.assignFitness(P); - Arrays.sort(P, FitnessIndividual.BY_FITNESS); + Arrays.sort(P, FitnessRecord.BY_FITNESS); // shuffle the first mu solutions to ensure fairness RandomUtils.shuffle(random, P, 0, this.mu); int p1 = -1; // index to iterate over first parent @@ -147,13 +147,13 @@ public void solve(final IBlackBoxProcess process) { return; // best solution is stored in process } - final LSFitnessIndividual dest = P[index]; - final LSFitnessIndividual sel = P[(++p1) % this.mu]; + final LSFitnessRecord dest = P[index]; + final LSFitnessRecord sel = P[(++p1) % this.mu]; do { // find a second, different record p2 = random.nextInt(this.mu); } while (p2 == p1); -// perform recombination of the two selected individuals +// perform recombination of the two selected solutions this.binary.apply(sel.x, P[p2].x, dest.x, random); // map to solution/schedule and evaluate quality dest.quality = process.evaluate(dest.x); diff --git a/src/main/java/aitoa/algorithms/PACOModelAge.java b/src/main/java/aitoa/algorithms/PACOModelAge.java index c87522e..9bf88aa 100644 --- a/src/main/java/aitoa/algorithms/PACOModelAge.java +++ b/src/main/java/aitoa/algorithms/PACOModelAge.java @@ -5,8 +5,8 @@ import java.util.Arrays; import java.util.Random; -import aitoa.structure.Individual; import aitoa.structure.LogFormat; +import aitoa.structure.Record; import aitoa.utils.ReflectionUtils; import aitoa.utils.graph.DirectedEdgeMultiSet; import aitoa.utils.graph.IntSet; @@ -221,10 +221,9 @@ final double getPheromone(final int a, final int b) { /** {@inheritDoc} */ @Override - public final void - update(final Iterable> selected) { - for (final Individual ind : selected) { // for each ant to - // be added + public final void update(final Iterable> selected) { + for (final Record ind : selected) { // for each ant to + // be added final int[] pi = this.permutationFromX(ind.x); final int size = this.mCurPopSize; final int index = this.mCurPopIndex; diff --git a/src/main/java/aitoa/algorithms/Utils.java b/src/main/java/aitoa/algorithms/Utils.java index bebb470..fa4941c 100644 --- a/src/main/java/aitoa/algorithms/Utils.java +++ b/src/main/java/aitoa/algorithms/Utils.java @@ -2,7 +2,7 @@ import java.util.Arrays; -import aitoa.structure.Individual; +import aitoa.structure.Record; /** * A utility class with frequently needed routines. @@ -10,40 +10,39 @@ public final class Utils { /** - * Put the {@code max} best individuals with unique quality to - * the front of an individual array {@code array}. First, - * {@code array} is sorted. Thus, the best individuals are at - * the front. However, some of them may have the same quality - * and we only want one individual of each quality level at - * front. We therefore process the sorted {@code array} from - * the front and whenever we encounter a quality repeatedly, we - * swap these repeated individuals towards higher indices. - * Since there may be less than {@code max} individuals with - * unique quality, we return the number of unique qualities. + * Put the {@code max} best records with unique quality to the + * front of an records array {@code array}. First, + * {@code array} is sorted. Thus, the best records are at the + * front. However, some of them may have the same quality and + * we only want one records of each quality level at front. We + * therefore process the sorted {@code array} from the front + * and whenever we encounter a quality repeatedly, we swap + * these repeated records towards higher indices. Since there + * may be less than {@code max} records with unique quality, we + * return the number of unique qualities. * * @param array * the array to process * @param max - * the maximum number of best individuals to make - * unique - * @return the number {@code u} of unique-quality individuals + * the maximum number of best records to make unique + * @return the number {@code u} of unique-quality records * retained, will be {@code 1<=u<=max} */ // start qualityClearing - public static int qualityBasedClearing( - final Individual[] array, final int max) { + public static int qualityBasedClearing(final Record[] array, + final int max) { - Arrays.sort(array, Individual.BY_QUALITY);// best -> first + Arrays.sort(array, Record.BY_QUALITY);// best -> first int unique = 0; double lastQuality = Double.NEGATIVE_INFINITY; // impossible for (int index = 0; index < array.length; index++) { - final Individual current = array[index]; + final Record current = array[index]; final double currentQuality = current.quality; if (currentQuality > lastQuality) { // unique so-far if (index > unique) { // need to move forward? - final Individual other = array[unique]; + final Record other = array[unique]; array[unique] = current; // swap with first non-unique array[index] = other; } diff --git a/src/main/java/aitoa/examples/jssp/EJSSPExperimentStageACO.java b/src/main/java/aitoa/examples/jssp/EJSSPExperimentStageACO.java index 210c45a..c3bd83b 100644 --- a/src/main/java/aitoa/examples/jssp/EJSSPExperimentStageACO.java +++ b/src/main/java/aitoa/examples/jssp/EJSSPExperimentStageACO.java @@ -8,8 +8,8 @@ import java.util.stream.Stream; import aitoa.algorithms.EDA; -import aitoa.examples.jssp.aco.JSSPACOIndividual; import aitoa.examples.jssp.aco.JSSPACOMakespanObjectiveFunction; +import aitoa.examples.jssp.aco.JSSPACORecord; import aitoa.examples.jssp.aco.JSSPACOSpace; import aitoa.examples.jssp.aco.JSSPPACOModelAge; import aitoa.structure.BlackBoxProcessBuilder; @@ -18,9 +18,9 @@ /** the stages of the JSSP experiment */ public enum EJSSPExperimentStageACO implements - IExperimentStage> { + IMetaheuristic> { /** the first stage: random sampling */ STAGE_ACO_1 { @@ -33,12 +33,14 @@ public enum EJSSPExperimentStageACO implements * @return the stream of suppliers */ @Override - public Stream>> + public + Stream>> getAlgorithms(// final JSSPACOMakespanObjectiveFunction problem) { - final ArrayList>> list = new ArrayList<>(); + final ArrayList>> list = + new ArrayList<>(); for (final int mu : new int[] { 1 }) { for (final int lambda : new int[] { 1024, 2048 }) { @@ -115,7 +117,7 @@ public enum EJSSPExperimentStageACO implements */ @Override public void configureBuilder(final BlackBoxProcessBuilder< - JSSPACOIndividual, JSSPACOIndividual> builder) { + JSSPACORecord, JSSPACORecord> builder) { builder.setMaxTime(TimeUnit.MINUTES.toMillis(3L)); } @@ -130,8 +132,8 @@ public void configureBuilder(final BlackBoxProcessBuilder< */ @Override public void configureBuilderForProblem( - final BlackBoxProcessBuilder builder, + final BlackBoxProcessBuilder builder, final JSSPACOMakespanObjectiveFunction problem) { final JSSPInstance inst = Objects.requireNonNull(problem.getInstance()); diff --git a/src/main/java/aitoa/examples/jssp/JSSPUMDAModel.java b/src/main/java/aitoa/examples/jssp/JSSPUMDAModel.java index 0651cd3..cb474c8 100644 --- a/src/main/java/aitoa/examples/jssp/JSSPUMDAModel.java +++ b/src/main/java/aitoa/examples/jssp/JSSPUMDAModel.java @@ -6,8 +6,8 @@ import java.util.Random; import aitoa.structure.IModel; -import aitoa.structure.Individual; import aitoa.structure.LogFormat; +import aitoa.structure.Record; import aitoa.utils.RandomUtils; /** @@ -128,8 +128,7 @@ public final void initialize() { /** {@inheritDoc} */ @Override // start update - public void - update(final Iterable> selected) { + public void update(final Iterable> selected) { final int l = this.mModel.length; // == m*n // Make sure that all values are >= 1 @@ -138,7 +137,7 @@ public final void initialize() { } // For each encountered job, add the large value this.base - for (final Individual ind : selected) { // selected + for (final Record ind : selected) { // selected final int[] sel = ind.x; for (int k = l; (--k) >= 0;) { // valid indices this.mModel[k][sel[k]] += this.base; diff --git a/src/main/java/aitoa/examples/jssp/aco/JSSPACOMakespanObjectiveFunction.java b/src/main/java/aitoa/examples/jssp/aco/JSSPACOMakespanObjectiveFunction.java index 314c643..d08f4ce 100644 --- a/src/main/java/aitoa/examples/jssp/aco/JSSPACOMakespanObjectiveFunction.java +++ b/src/main/java/aitoa/examples/jssp/aco/JSSPACOMakespanObjectiveFunction.java @@ -9,7 +9,7 @@ * the Job Shop Scheduling Problem, subject to minimization */ public final class JSSPACOMakespanObjectiveFunction - implements IObjectiveFunction { + implements IObjectiveFunction { /** * the inner makespan objective function, used only for @@ -57,7 +57,7 @@ public String toString() { /** {@inheritDoc} */ @Override - public double evaluate(final JSSPACOIndividual y) { + public double evaluate(final JSSPACORecord y) { return y.makespan; } diff --git a/src/main/java/aitoa/examples/jssp/aco/JSSPACOIndividual.java b/src/main/java/aitoa/examples/jssp/aco/JSSPACORecord.java similarity index 68% rename from src/main/java/aitoa/examples/jssp/aco/JSSPACOIndividual.java rename to src/main/java/aitoa/examples/jssp/aco/JSSPACORecord.java index b4f3b49..c98ecd4 100644 --- a/src/main/java/aitoa/examples/jssp/aco/JSSPACOIndividual.java +++ b/src/main/java/aitoa/examples/jssp/aco/JSSPACORecord.java @@ -3,10 +3,11 @@ import aitoa.examples.jssp.JSSPCandidateSolution; /** - * An individual holding a permutation and a JSSP candidate - * solution + * A record holding both a permutation and a JSSP candidate + * solution, since we can construct them together at once in our + * model sampling procedure. */ -public final class JSSPACOIndividual { +public final class JSSPACORecord { /** the permutation */ public final int[] permutation; @@ -25,7 +26,7 @@ public final class JSSPACOIndividual { * @param pN * the number of jobs */ - public JSSPACOIndividual(final int pM, final int pN) { + public JSSPACORecord(final int pM, final int pN) { super(); this.permutation = new int[pM * pN]; this.solution = new JSSPCandidateSolution(pM, pN); diff --git a/src/main/java/aitoa/examples/jssp/aco/JSSPACOSpace.java b/src/main/java/aitoa/examples/jssp/aco/JSSPACOSpace.java index adf6a5b..57b4b8e 100644 --- a/src/main/java/aitoa/examples/jssp/aco/JSSPACOSpace.java +++ b/src/main/java/aitoa/examples/jssp/aco/JSSPACOSpace.java @@ -9,8 +9,8 @@ import aitoa.structure.ISpace; import aitoa.utils.math.BigMath; -/** The space for the PACO individuals */ -public class JSSPACOSpace implements ISpace { +/** The space for the PACO records */ +public class JSSPACOSpace implements ISpace { /** the internal {@link JSSPSolutionSpace} */ private final JSSPSolutionSpace mY; @@ -27,15 +27,15 @@ public JSSPACOSpace(final JSSPInstance pInstance) { /** {@inheritDoc} */ @Override - public JSSPACOIndividual create() { - return new JSSPACOIndividual(this.mY.instance.m, + public JSSPACORecord create() { + return new JSSPACORecord(this.mY.instance.m, this.mY.instance.n); } /** {@inheritDoc} */ @Override - public void copy(final JSSPACOIndividual from, - final JSSPACOIndividual to) { + public void copy(final JSSPACORecord from, + final JSSPACORecord to) { System.arraycopy(from.permutation, 0, to.permutation, 0, to.permutation.length); this.mY.copy(from.solution, to.solution); @@ -44,8 +44,8 @@ public void copy(final JSSPACOIndividual from, /** {@inheritDoc} */ @Override - public void print(final JSSPACOIndividual z, - final Appendable out) throws IOException { + public void print(final JSSPACORecord z, final Appendable out) + throws IOException { out.append("new int[] "); //$NON-NLS-1$ char ch = '{'; for (final int i : z.permutation) { @@ -65,7 +65,7 @@ public void print(final JSSPACOIndividual z, /** {@inheritDoc} */ @Override - public void check(final JSSPACOIndividual z) { + public void check(final JSSPACORecord z) { Objects.requireNonNull(z); this.mY.check(z.solution); diff --git a/src/main/java/aitoa/examples/jssp/aco/JSSPPACOModelAge.java b/src/main/java/aitoa/examples/jssp/aco/JSSPPACOModelAge.java index 6634199..86cb0d4 100644 --- a/src/main/java/aitoa/examples/jssp/aco/JSSPPACOModelAge.java +++ b/src/main/java/aitoa/examples/jssp/aco/JSSPPACOModelAge.java @@ -42,7 +42,7 @@ * network, as the jobs are completed. */ public final class JSSPPACOModelAge - extends PACOModelAge { + extends PACOModelAge { /** the current time at a given machine */ private final int[] mMachineTime; @@ -101,7 +101,7 @@ public String toString() { /** {@inheritDoc} */ @Override - protected int[] permutationFromX(final JSSPACOIndividual x) { + protected int[] permutationFromX(final JSSPACORecord x) { return x.permutation; } @@ -117,7 +117,7 @@ protected int[] permutationFromX(final JSSPACOIndividual x) { * the point in the search space */ @Override - public void apply(final JSSPACOIndividual dest, + public void apply(final JSSPACORecord dest, final Random random) { Arrays.fill(this.mMachineState, 0); Arrays.fill(this.mMachineTime, 0); @@ -154,7 +154,7 @@ protected void initNodeSet(final Random random) { */ @Override protected double getCostOfAppending(final int value, - final JSSPACOIndividual x) { + final JSSPACORecord x) { // extract job id final int nextJob = value / this.mM; // get the definition of the steps that we need to take for @@ -181,7 +181,7 @@ protected double getCostOfAppending(final int value, /** {@inheritDoc} */ @Override protected void append(final int value, - final JSSPACOIndividual dest) { + final JSSPACORecord dest) { final int[] machineState = this.mMachineState; final int[] machineTime = this.mMachineTime; final int[] jobTime = this.mJobTime; diff --git a/src/main/java/aitoa/structure/IModel.java b/src/main/java/aitoa/structure/IModel.java index a8cd2c9..c4de17d 100644 --- a/src/main/java/aitoa/structure/IModel.java +++ b/src/main/java/aitoa/structure/IModel.java @@ -24,7 +24,7 @@ public interface IModel extends INullarySearchOperator { * the array with the points in the search space that * have been selected */ - void update(Iterable> selected); + void update(Iterable> selected); /** * Sample the model and fill the destination point in the diff --git a/src/main/java/aitoa/structure/Individual.java b/src/main/java/aitoa/structure/Individual.java deleted file mode 100644 index 393c9c9..0000000 --- a/src/main/java/aitoa/structure/Individual.java +++ /dev/null @@ -1,39 +0,0 @@ -package aitoa.structure; - -import java.util.Comparator; -import java.util.Objects; - -/** - * The individual record: hold one point in search space and its - * quality. Neither {@link #equals(Object)} nor - * {@link #hashCode()} are overridden, since this contain is - * explicitly designed to be mutable. - * - * @param - * the data structure of the search space - */ -public class Individual { - - /** The comparator to be used for sorting according quality */ - public static final Comparator> BY_QUALITY = - (a, b) -> Double.compare(a.quality, b.quality); - - /** the point in the search space */ - public final X x; - /** the quality */ - public double quality; - - /** - * create the individual record - * - * @param pX - * the point in the search space - * @param pQ - * the quality - */ - public Individual(final X pX, final double pQ) { - super(); - this.x = Objects.requireNonNull(pX); - this.quality = pQ; - } -} diff --git a/src/main/java/aitoa/structure/Record.java b/src/main/java/aitoa/structure/Record.java new file mode 100644 index 0000000..e9aaa63 --- /dev/null +++ b/src/main/java/aitoa/structure/Record.java @@ -0,0 +1,48 @@ +package aitoa.structure; + +import java.util.Comparator; +import java.util.Objects; + +/** + * A re-usable record that can hold {@linkplain #x one point in + * search space} and its {@link #quality}, i.e., the result of + * applying the objective function to {@link #x}. More formally, + * the point {@link #x} will be passed to the + * {@link IBlackBoxProcess#evaluate(Object)} function, which may + * internally perform a {@linkplain IRepresentationMapping + * representation mapping} and then invoke the + * {@linkplain IObjectiveFunction objective function}. The result + * of this call can then be stored in {@link #x}. This allows for + * better book-keeping if we need to handle multiple solutions. + * Neither {@link #equals(Object)} nor {@link #hashCode()} are + * overridden, since this contain is explicitly designed to be + * mutable. + * + * @param + * the data structure of the search space + */ +public class Record { + + /** The comparator to be used for sorting according quality */ + public static final Comparator> BY_QUALITY = + (a, b) -> Double.compare(a.quality, b.quality); + + /** the point in the search space */ + public final X x; + /** the quality */ + public double quality; + + /** + * create the individual record + * + * @param pX + * the point in the search space + * @param pQ + * the quality + */ + public Record(final X pX, final double pQ) { + super(); + this.x = Objects.requireNonNull(pX); + this.quality = pQ; + } +} diff --git a/src/test/java/aitoa/algorithms/TestUtils.java b/src/test/java/aitoa/algorithms/TestUtils.java index 1bba48e..9668734 100644 --- a/src/test/java/aitoa/algorithms/TestUtils.java +++ b/src/test/java/aitoa/algorithms/TestUtils.java @@ -7,7 +7,7 @@ import org.junit.Test; import aitoa.TestTools; -import aitoa.structure.Individual; +import aitoa.structure.Record; /** Test the algorithm utils. */ public class TestUtils { @@ -19,13 +19,12 @@ public final void testQualityBasedClearing() { final ThreadLocalRandom random = ThreadLocalRandom.current(); for (int test = 10000; (--test) >= 0;) { - final Individual[] source = - new Individual[random.nextInt(1, 1000)]; + final Record[] source = + new Record[random.nextInt(1, 1000)]; for (int i = source.length; (--i) >= 0;) { - source[i] = - new Individual(random, random.nextInt(0, 20)); + source[i] = new Record(random, random.nextInt(0, 20)); } - final Individual[] compare = source.clone(); + final Record[] compare = source.clone(); final int mu = (source.length > 1) ? random.nextInt(1, source.length) : 1; final int u = Utils.qualityBasedClearing(source, mu); @@ -51,7 +50,7 @@ public final void testQualityBasedClearing() { for (int i = compare.length; (--i) >= 0;) { int count = 0; - final Individual c = compare[i]; + final Record c = compare[i]; for (int j = source.length; (--j) >= 0;) { if (c == source[j]) { ++count; diff --git a/src/test/java/aitoa/algorithms/jssp/TestEA2OnJSSP.java b/src/test/java/aitoa/algorithms/jssp/TestEA2OnJSSP.java new file mode 100644 index 0000000..47a3a8d --- /dev/null +++ b/src/test/java/aitoa/algorithms/jssp/TestEA2OnJSSP.java @@ -0,0 +1,32 @@ +package aitoa.algorithms.jssp; + +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; + +import aitoa.algorithms.EA2; +import aitoa.examples.jssp.JSSPBinaryOperatorSequence; +import aitoa.examples.jssp.JSSPCandidateSolution; +import aitoa.examples.jssp.JSSPInstance; +import aitoa.examples.jssp.JSSPNullaryOperator; +import aitoa.examples.jssp.JSSPUnaryOperator1Swap; +import aitoa.structure.IMetaheuristic; + +/** + * Test the {@linkplain aitoa.algorithms.EA2 evolutionary + * algorithm} on the JSSP + */ +public class TestEA2OnJSSP extends TestMetaheuristicOnJSSP { + + /** {@inheritDoc} */ + @Override + protected IMetaheuristic + getAlgorithm(final JSSPInstance instance) { + final Random rand = ThreadLocalRandom.current(); + final int mu = 1 + rand.nextInt(64); + final double cr = (mu > 1) ? rand.nextDouble() : 0; + return new EA2<>(new JSSPNullaryOperator(instance), // + new JSSPUnaryOperator1Swap(), // + new JSSPBinaryOperatorSequence(instance), // + cr, mu); + } +} diff --git a/src/test/java/aitoa/algorithms/jssp/TestPACOAgeOnJSSP.java b/src/test/java/aitoa/algorithms/jssp/TestPACOAgeOnJSSP.java index 8fea133..42e90a2 100644 --- a/src/test/java/aitoa/algorithms/jssp/TestPACOAgeOnJSSP.java +++ b/src/test/java/aitoa/algorithms/jssp/TestPACOAgeOnJSSP.java @@ -8,8 +8,8 @@ import aitoa.algorithms.EDA; import aitoa.algorithms.TestMetaheuristic; import aitoa.examples.jssp.JSSPInstance; -import aitoa.examples.jssp.aco.JSSPACOIndividual; import aitoa.examples.jssp.aco.JSSPACOMakespanObjectiveFunction; +import aitoa.examples.jssp.aco.JSSPACORecord; import aitoa.examples.jssp.aco.JSSPACOSpace; import aitoa.examples.jssp.aco.JSSPPACOModelAge; import aitoa.structure.IBlackBoxProcess; @@ -23,8 +23,8 @@ * {@linkplain aitoa.algorithms.PACOModelAge age-base PACO model} * on the JSSP */ -public class TestPACOAgeOnJSSP extends - TestMetaheuristic { +public class TestPACOAgeOnJSSP + extends TestMetaheuristic { /** * Get the EDA algorithm @@ -34,7 +34,7 @@ public class TestPACOAgeOnJSSP extends * @return the algorithm */ @SuppressWarnings("static-method") - protected EDA + protected EDA getAlgorithm(final JSSPInstance instance) { final ThreadLocalRandom rand = ThreadLocalRandom.current(); final int lambda = 1 + rand.nextInt(64); @@ -48,7 +48,7 @@ public class TestPACOAgeOnJSSP extends /** {@inheritDoc} */ @Override - protected IMetaheuristic + protected IMetaheuristic getInstance() { return this.getAlgorithm(new JSSPInstance("demo")); //$NON-NLS-1$ } @@ -65,15 +65,15 @@ public class TestPACOAgeOnJSSP extends */ protected void runTest(final JSSPInstance instance, final long maxFEs, final long maxTime) { - final ISpace space = + final ISpace space = new JSSPACOSpace(instance); - final EDA algo = + final EDA algo = this.getAlgorithm(instance); - try (final IBlackBoxProcess p = new TestBlackBoxProcessBuilder< - JSSPACOIndividual, JSSPACOIndividual>()// + try (final IBlackBoxProcess p = new TestBlackBoxProcessBuilder< + JSSPACORecord, JSSPACORecord>()// .setSearchSpace(space)// .setSolutionSpace(space)// .setObjectiveFunction( diff --git a/src/test/java/aitoa/bookExamples/jssp/JSSPDemoEDA.java b/src/test/java/aitoa/bookExamples/jssp/JSSPDemoEDA.java index 337104b..8da84cc 100644 --- a/src/test/java/aitoa/bookExamples/jssp/JSSPDemoEDA.java +++ b/src/test/java/aitoa/bookExamples/jssp/JSSPDemoEDA.java @@ -9,7 +9,7 @@ import aitoa.examples.jssp.JSSPNullaryOperator; import aitoa.examples.jssp.JSSPRepresentationMapping; import aitoa.examples.jssp.JSSPSearchSpace; -import aitoa.structure.Individual; +import aitoa.structure.Record; import aitoa.utils.RandomUtils; /** @@ -43,10 +43,10 @@ public static void main(final String[] args) { final int[] x = X.create(); - final Individual[] keep = new Individual[10]; + final Record[] keep = new Record[10]; for (int i = keep.length; (--i) >= 0;) { keep[i] = - new Individual<>(X.create(), Double.POSITIVE_INFINITY); + new Record<>(X.create(), Double.POSITIVE_INFINITY); } final int[][] M = new int[x.length][instance.n]; @@ -56,7 +56,7 @@ public static void main(final String[] args) { findExample: for (;;) { random.setSeed(seed); - for (final Individual k : keep) { + for (final Record k : keep) { k.quality = Double.POSITIVE_INFINITY; } @@ -87,7 +87,7 @@ public static void main(final String[] args) { int count = 0; for (int kk = got; (--kk) >= 0;) { - final Individual k = keep[kk]; + final Record k = keep[kk]; int nonequal = x.length; for (int uu = x.length; (--uu) >= 0;) { if (k.x[uu] == x[uu]) { @@ -112,7 +112,7 @@ public static void main(final String[] args) { for (final int[] k : M) { Arrays.fill(k, 0); } - for (final Individual k : keep) { + for (final Record k : keep) { for (int j = k.x.length; (--j) >= 0;) { ++M[j][k.x[j]]; } @@ -170,7 +170,7 @@ public static void main(final String[] args) { // print the solutions RandomUtils.shuffle(random, keep, 0, keep.length); for (int i = 0; i < keep.length; i++) { - final Individual k = keep[i]; + final Record k = keep[i]; if (i < 9) { System.out.print(' '); } @@ -255,7 +255,7 @@ public static void main(final String[] args) { continue sample; } - for (final Individual sel : keep) { + for (final Record sel : keep) { if (Arrays.equals(sel.x, x)) { continue sample; } diff --git a/src/test/java/aitoa/examples/jssp/TestJSSPUMDAModel.java b/src/test/java/aitoa/examples/jssp/TestJSSPUMDAModel.java index 3ee3043..8d44202 100644 --- a/src/test/java/aitoa/examples/jssp/TestJSSPUMDAModel.java +++ b/src/test/java/aitoa/examples/jssp/TestJSSPUMDAModel.java @@ -10,7 +10,7 @@ import aitoa.structure.IModel; import aitoa.structure.IModelTest; import aitoa.structure.ISpace; -import aitoa.structure.Individual; +import aitoa.structure.Record; /** test the univariate model for the JSSP */ public class TestJSSPUMDAModel extends IModelTest { @@ -63,9 +63,8 @@ public final void testModelTraining() { for (int z = 100; (--z) >= 0;) { model.initialize(); final int[] template = JSSPTestUtils.createValidX(demo); - final ArrayList> list = - new ArrayList<>(); - list.add(new Individual<>(template, 1)); + final ArrayList> list = new ArrayList<>(); + list.add(new Record<>(template, 1)); model.update(list); final int[] dest = new int[demo.m * demo.n]; diff --git a/src/test/java/aitoa/examples/jssp/aco/TestJSSPACOSpace.java b/src/test/java/aitoa/examples/jssp/aco/TestJSSPACOSpace.java index 4589022..5d7a135 100644 --- a/src/test/java/aitoa/examples/jssp/aco/TestJSSPACOSpace.java +++ b/src/test/java/aitoa/examples/jssp/aco/TestJSSPACOSpace.java @@ -13,8 +13,7 @@ import aitoa.structure.ISpaceTest; /** Test the search space we defined for the JSSP problem */ -public class TestJSSPACOSpace - extends ISpaceTest { +public class TestJSSPACOSpace extends ISpaceTest { /** the space we use */ private static final JSSPInstance PROBLEM = @@ -40,13 +39,13 @@ public TestJSSPACOSpace() { /** {@inheritDoc} */ @Override - protected ISpace getInstance() { + protected ISpace getInstance() { return TestJSSPACOSpace.INSTANCE; } /** {@inheritDoc} */ @Override - protected void assertValid(final JSSPACOIndividual a) { + protected void assertValid(final JSSPACORecord a) { JSSPTestUtils.assertY(a.solution, TestJSSPACOSpace.PROBLEM); Assert.assertEquals(TestJSSPACOSpace.F.evaluate(a.solution), a.makespan, 0d); @@ -65,8 +64,7 @@ protected void assertValid(final JSSPACOIndividual a) { /** {@inheritDoc} */ @Override - protected void - fillWithRandomData(final JSSPACOIndividual dest) { + protected void fillWithRandomData(final JSSPACORecord dest) { JSSPTestUtils.randomX(dest.permutation, TestJSSPACOSpace.PROBLEM); @@ -86,8 +84,8 @@ protected void assertValid(final JSSPACOIndividual a) { /** {@inheritDoc} */ @Override - protected JSSPACOIndividual createValid() { - final JSSPACOIndividual dest = new JSSPACOIndividual( + protected JSSPACORecord createValid() { + final JSSPACORecord dest = new JSSPACORecord( TestJSSPACOSpace.PROBLEM.m, TestJSSPACOSpace.PROBLEM.n); this.fillWithRandomData(dest); return dest; @@ -95,8 +93,8 @@ protected JSSPACOIndividual createValid() { /** {@inheritDoc} */ @Override - protected void assertEquals(final JSSPACOIndividual a, - final JSSPACOIndividual b) { + protected void assertEquals(final JSSPACORecord a, + final JSSPACORecord b) { if (a != b) { final int[][] sa = a.solution.schedule; final int[][] sb = b.solution.schedule; @@ -111,9 +109,9 @@ protected void assertEquals(final JSSPACOIndividual a, /** {@inheritDoc} */ @Override - protected JSSPACOIndividual createInvalid() { + protected JSSPACORecord createInvalid() { final ThreadLocalRandom random = ThreadLocalRandom.current(); - final JSSPACOIndividual a = this.createValid(); + final JSSPACORecord a = this.createValid(); switch (random.nextInt(3)) { case 0: { diff --git a/src/test/java/aitoa/examples/jssp/aco/TestJSSPPACOModelAge.java b/src/test/java/aitoa/examples/jssp/aco/TestJSSPPACOModelAge.java index 25bc241..5e53254 100644 --- a/src/test/java/aitoa/examples/jssp/aco/TestJSSPPACOModelAge.java +++ b/src/test/java/aitoa/examples/jssp/aco/TestJSSPPACOModelAge.java @@ -13,7 +13,7 @@ /** test the univariate model for the JSSP */ public class TestJSSPPACOModelAge - extends IModelTest { + extends IModelTest { /** the space we use */ private static final JSSPInstance PROBLEM = @@ -24,7 +24,7 @@ public class TestJSSPPACOModelAge new JSSPACOSpace(TestJSSPPACOModelAge.PROBLEM); /** the operator we use */ - private static final IModel OP = + private static final IModel OP = new JSSPPACOModelAge(TestJSSPPACOModelAge.PROBLEM, 3, 0.9, 5d, 1d); /** the internal representation mapping */ @@ -39,14 +39,14 @@ public class TestJSSPPACOModelAge /** {@inheritDoc} */ @Override - protected ISpace getSpace() { + protected ISpace getSpace() { return TestJSSPPACOModelAge.SPACE; } /** {@inheritDoc} */ @Override - protected IModel - getModel(final ISpace space) { + protected IModel + getModel(final ISpace space) { return TestJSSPPACOModelAge.OP; } @@ -57,7 +57,7 @@ protected ISpace getSpace() { * the destination */ private static final void - fillWithRandomData(final JSSPACOIndividual dest) { + fillWithRandomData(final JSSPACORecord dest) { JSSPTestUtils.randomX(dest.permutation, TestJSSPPACOModelAge.PROBLEM); @@ -77,9 +77,9 @@ protected ISpace getSpace() { /** {@inheritDoc} */ @Override - protected JSSPACOIndividual createValid() { - final JSSPACOIndividual dest = - new JSSPACOIndividual(TestJSSPPACOModelAge.PROBLEM.m, + protected JSSPACORecord createValid() { + final JSSPACORecord dest = + new JSSPACORecord(TestJSSPPACOModelAge.PROBLEM.m, TestJSSPPACOModelAge.PROBLEM.n); TestJSSPPACOModelAge.fillWithRandomData(dest); return dest; @@ -87,8 +87,8 @@ protected JSSPACOIndividual createValid() { /** {@inheritDoc} */ @Override - protected boolean equals(final JSSPACOIndividual a, - final JSSPACOIndividual b) { + protected boolean equals(final JSSPACORecord a, + final JSSPACORecord b) { return (a.makespan == b.makespan) && Arrays.equals(a.permutation, b.permutation) && Arrays.deepEquals(a.solution.schedule, diff --git a/src/test/java/aitoa/structure/IModelTest.java b/src/test/java/aitoa/structure/IModelTest.java index a253f8e..ad019fc 100644 --- a/src/test/java/aitoa/structure/IModelTest.java +++ b/src/test/java/aitoa/structure/IModelTest.java @@ -105,7 +105,7 @@ public final void testValidAfterUpdate() { model.initialize(); @SuppressWarnings("unchecked") - final Individual[] array = new Individual[48]; + final Record[] array = new Record[48]; @SuppressWarnings("unchecked") final X[] dest = ((X[]) (new Object[3 * (array.length + 1)])); @@ -114,7 +114,7 @@ public final void testValidAfterUpdate() { int diff = 0; outer: for (int j = 0; j < dest.length; j++) { for (int i = array.length; (--i) >= 0;) { - array[i] = new Individual<>(this.createValid(), + array[i] = new Record<>(this.createValid(), random.nextDouble(1d, 100d)); }