Skip to content

Commit

Permalink
further refactoring: Individual -> Record, plus additional EA variant
Browse files Browse the repository at this point in the history
  • Loading branch information
thomasWeise committed Oct 20, 2020
1 parent 58c068f commit 6930380
Show file tree
Hide file tree
Showing 42 changed files with 502 additions and 320 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ 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
<dependencies>
<dependency>
<groupId>com.github.thomasWeise</groupId>
<artifactId>aitoa-code</artifactId>
<version>0.8.70</version>
<version>0.8.71</version>
</dependency>
</dependencies>
```
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>aitoa</groupId>
<artifactId>aitoa-code</artifactId>
<version>0.8.70</version>
<version>0.8.71</version>
<packaging>jar</packaging>
<name>aitoa-code</name>
<description>Example Source Codes from the Book "Introduction to Optimization Algorithms"</description>
Expand Down
19 changes: 9 additions & 10 deletions src/main/java/aitoa/algorithms/EA.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -119,14 +119,13 @@ public void solve(final IBlackBoxProcess<X, Y> process) {
final ISpace<X> searchSpace = process.getSearchSpace();
int p2; // to hold index of second selected record

final Individual<X>[] P =
new Individual[this.mu + this.lambda];
final Record<X>[] 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
Expand All @@ -135,8 +134,8 @@ public void solve(final IBlackBoxProcess<X, Y> 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
Expand All @@ -147,16 +146,16 @@ public void solve(final IBlackBoxProcess<X, Y> process) {
return; // best solution is stored in process
}

final Individual<X> dest = P[index];
final Record<X> dest = P[index];
p1 = (p1 + 1) % this.mu; // step the parent 1 index
final Individual<X> sel = P[p1];
final Record<X> 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
Expand Down
17 changes: 7 additions & 10 deletions src/main/java/aitoa/algorithms/EA1p1WithFitness.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,30 +58,27 @@ public EA1p1WithFitness(
public void solve(final IBlackBoxProcess<X, Y> process) {
// init local variables P, nullary, unary, random
// end relevant
final FitnessIndividual<X>[] P = new FitnessIndividual[] {
new FitnessIndividual<>(
process.getSearchSpace().create(),
final FitnessRecord<X>[] 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
this.unary.apply(P[0].x, P[1].x, random);
// 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<X> 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<X> temp = P[0];
P[0] = P[1]; // if new solution has better or
P[1] = temp; // equal fitness: accept it
}
} // until time is up
Expand Down
147 changes: 147 additions & 0 deletions src/main/java/aitoa/algorithms/EA2.java
Original file line number Diff line number Diff line change
@@ -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 <X>
* the search space
* @param <Y>
* the solution space
*/
// start relevant
public final class EA2<X, Y> extends Metaheuristic2<X, Y> {
// 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<X> pNullary,
final IUnarySearchOperator<X> pUnary,
final IBinarySearchOperator<X> 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<X, Y> 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<X> searchSpace = process.getSearchSpace();
int p2; // to hold index of second selected record

final Record<X>[] P = new Record[this.mu];
Record<X> 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<X> 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
13 changes: 6 additions & 7 deletions src/main/java/aitoa/algorithms/EAWithClearing.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -108,15 +108,14 @@ public void solve(final IBlackBoxProcess<X, Y> process) {
final ISpace<X> searchSpace = process.getSearchSpace();
int p2;

final Individual<X>[] P =
new Individual[this.mu + this.lambda];
final Record<X>[] 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
}
Expand All @@ -136,9 +135,9 @@ public void solve(final IBlackBoxProcess<X, Y> process) {
return; // The best solution is stored in process.
}
// start relevant
final Individual<X> dest = P[index]; // offspring
final Record<X> dest = P[index]; // offspring
p1 = (p1 + 1) % u; // parent 1 index
final Individual<X> sel = P[p1]; // parent 1
final Record<X> sel = P[p1]; // parent 1
if ((u >= 2) && (random.nextDouble() <= this.cr)) {
do { // find a second, different record
p2 = random.nextInt(u);
Expand Down
18 changes: 9 additions & 9 deletions src/main/java/aitoa/algorithms/EAWithFitness.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,15 @@ public void solve(final IBlackBoxProcess<X, Y> process) {
final Random random = process.getRandom();
final ISpace<X> searchSpace = process.getSearchSpace();
int p2; // to hold index of second selected record
final FitnessIndividual<X>[] P =
new FitnessIndividual[this.mu + this.lambda];
final FitnessRecord<X>[] 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
Expand All @@ -115,9 +115,9 @@ public void solve(final IBlackBoxProcess<X, Y> 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
Expand All @@ -128,14 +128,14 @@ public void solve(final IBlackBoxProcess<X, Y> process) {
return; // best solution is stored in process
}

final FitnessIndividual<X> dest = P[index];
final FitnessRecord<X> dest = P[index];
p1 = (p1 + 1) % this.mu;
final FitnessIndividual<X> sel = P[p1];
final FitnessRecord<X> 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
Expand Down
Loading

0 comments on commit 6930380

Please sign in to comment.