Skip to content
Snippets Groups Projects
Commit 038ff9c3 authored by Vojtech Moravec's avatar Vojtech Moravec
Browse files

Implemented JADE DE Algorithm.

parent 11930afe
Branches
No related tags found
No related merge requests found
import org.apache.commons.math3.distribution.CauchyDistribution; import org.apache.commons.math3.distribution.CauchyDistribution;
import quantization.LloydMaxU16ScalarQuantization; import quantization.LloydMaxU16ScalarQuantization;
import quantization.Utils; import quantization.Utils;
import quantization.de.DeException;
import quantization.de.jade.JadeSolver;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
public class DataCompressor { public class DataCompressor {
public static void main(String[] args) throws FileNotFoundException { public static void main(String[] args) throws FileNotFoundException {
final String sourceFile = "D:\\tmp\\server-dump\\small.bin"; final String sourceFile = "D:\\tmp\\server-dump\\small.bin";
final int NumberOfBits = 8; final int NumberOfBits = 4;
final int Dimension = (int) Math.pow(2, NumberOfBits);
int[] values = Utils.convertU16BytesToInt(Utils.readFileBytes(sourceFile)); int[] values = Utils.convertU16BytesToInt(Utils.readFileBytes(sourceFile));
LloydMaxU16ScalarQuantization quantization = new LloydMaxU16ScalarQuantization(values, NumberOfBits); // LloydMaxU16ScalarQuantization quantization = new LloydMaxU16ScalarQuantization(values, NumberOfBits);
quantization.train(); // quantization.train();
JadeSolver jadeSolver = new JadeSolver(Dimension, 10 * Dimension, 200, 0.05, 0.1);
jadeSolver.setTrainingData(values);
try {
jadeSolver.train();
} catch (DeException e) {
e.printStackTrace();
}
System.out.println("Finished learning..."); System.out.println("Finished learning...");
......
...@@ -4,9 +4,6 @@ import java.io.FileNotFoundException; ...@@ -4,9 +4,6 @@ import java.io.FileNotFoundException;
public class LloydMaxU16ScalarQuantization { public class LloydMaxU16ScalarQuantization {
private final static int U16Max = 0xffff;
private final static int U16Min = 0x0;
private final int[] trainingData; private final int[] trainingData;
private final int bitCount; private final int bitCount;
private int intervalCount; private int intervalCount;
...@@ -30,7 +27,7 @@ public class LloydMaxU16ScalarQuantization { ...@@ -30,7 +27,7 @@ public class LloydMaxU16ScalarQuantization {
int intValue; int intValue;
for (int i = 0; i < src.length; i++) { for (int i = 0; i < src.length; i++) {
intValue = shortBitsToInt(src[i]); intValue = shortBitsToInt(src[i]);
if (intValue < U16Min || intValue > U16Max) { if (intValue < U16.Min || intValue > U16.Max) {
throw new RuntimeException("Source value is outside of bounds for 16-bit unsigned integer."); throw new RuntimeException("Source value is outside of bounds for 16-bit unsigned integer.");
} }
result[i] = intValue; result[i] = intValue;
...@@ -61,17 +58,17 @@ public class LloydMaxU16ScalarQuantization { ...@@ -61,17 +58,17 @@ public class LloydMaxU16ScalarQuantization {
centroids = new int[intervalCount]; centroids = new int[intervalCount];
boundaryPoints = new int[intervalCount + 1]; boundaryPoints = new int[intervalCount + 1];
boundaryPoints[0] = U16Min; boundaryPoints[0] = U16.Min;
boundaryPoints[intervalCount] = U16Max; boundaryPoints[intervalCount] = U16.Max;
double intervalSize = (double) (U16Max - U16Min) / (double) intervalCount; double intervalSize = (double) (U16.Max - U16.Min) / (double) intervalCount;
for (int i = 0; i < intervalCount; i++) { for (int i = 0; i < intervalCount; i++) {
centroids[i] = (int) Math.floor(((double) i + 0.5) * intervalSize); centroids[i] = (int) Math.floor(((double) i + 0.5) * intervalSize);
} }
} }
private void initializeProbabilityDensityFunction() { private void initializeProbabilityDensityFunction() {
pdf = new double[U16Max + 1]; pdf = new double[U16.Max + 1];
for (int i = 0; i < trainingData.length; i++) { for (int i = 0; i < trainingData.length; i++) {
pdf[trainingData[i]] += 1; pdf[trainingData[i]] += 1;
} }
......
package quantization;
public class Quantizer {
private int[] m_centroids;
private int[] m_boundaryPoints;
public Quantizer(final int min, final int max, final int[] centroids) {
m_centroids = centroids;
m_boundaryPoints = new int[centroids.length + 1];
m_boundaryPoints[0] = min;
m_boundaryPoints[centroids.length] = max;
for (int j = 1; j < centroids.length; j++) {
m_boundaryPoints[j] = (m_centroids[j] + m_centroids[j - 1]) / 2;
}
}
public int quantize(final int value) {
for (int intervalId = 1; intervalId <= m_centroids.length; intervalId++) {
if ((value >= m_boundaryPoints[intervalId - 1]) && (value < m_boundaryPoints[intervalId])) {
return m_centroids[intervalId - 1];
}
}
throw new RuntimeException("Value couldn't be quantized!");
}
public double getMse(final int[] data) {
double mse = 0.0;
for (int i = 0; i < data.length; i++) {
int quantizedValue = quantize(data[i]);
mse += Math.pow(((double) data[i] - (double) quantizedValue), 2);
}
mse /= (double) data.length;
return mse;
}
}
package quantization;
public class U16 {
public static final int Min = 0x0;
public static final int Max = 0xffff;
}
...@@ -15,6 +15,22 @@ public class Utils { ...@@ -15,6 +15,22 @@ public class Utils {
return new byte[0]; return new byte[0];
} }
public static <T> boolean arrayContains(final T[] array, final T element) {
for (int i = 0; i < array.length; i++) {
if (array[i].equals(element))
return true;
}
return false;
}
public static boolean arrayContainsToIndex(final int[] array, final int toIndex, final int element) {
for (int i = 0; i < toIndex; i++) {
if (array[i] == element)
return true;
}
return false;
}
public static int[] convertU16BytesToInt(final byte[] bytes) { public static int[] convertU16BytesToInt(final byte[] bytes) {
assert (bytes.length % 2 == 0); assert (bytes.length % 2 == 0);
int[] values = new int[bytes.length / 2]; int[] values = new int[bytes.length / 2];
...@@ -22,8 +38,7 @@ public class Utils { ...@@ -22,8 +38,7 @@ public class Utils {
int index = 0; int index = 0;
for (int i = 0; i < bytes.length; i += 2) { for (int i = 0; i < bytes.length; i += 2) {
final int value = (int) (((bytes[i] & 0xff) << 8) | (bytes[i + 1] & 0xff)); final int value = (int) (((bytes[i] & 0xff) << 8) | (bytes[i + 1] & 0xff));
if (value > 0) if (value > 0) {
{
values[index++] = value; values[index++] = value;
continue; continue;
} }
......
package quantization.de;
public class DeException extends Exception {
public DeException(final String message) {
super(message);
}
}
package quantization.de;
public interface IDESolver {
void setMinimalValueConstraint(final int min);
void setMaximalValueConstraint(final int max);
void setPopulationSize(final int populationSize) throws DeException;
void setGenerationCount(final int generationCount);
void setDimension(final int dimension);
void train() throws DeException;
void setTrainingData(final int[] data);
IIndividual getBestSolution();
}
package quantization.de;
public interface IIndividual {
double getFitness();
void setFitness(final double fitness);
}
package quantization.de.jade;
import org.apache.commons.math3.distribution.UniformRealDistribution;
import quantization.de.IIndividual;
import java.util.Arrays;
public class JadeIndividual implements IIndividual, Comparable<JadeIndividual> {
/**
* Quantization values of the individual.
*/
int[] m_attributes;
private double m_fitness;
private double m_crossoverProbability;
private double m_mutationFactor;
public JadeIndividual(final int dimension) {
m_attributes = new int[dimension];
}
public static JadeIndividual NewIndividual(final int[] attributes) {
JadeIndividual individual = new JadeIndividual(attributes.length);
individual.m_attributes = attributes;
return individual;
}
public JadeIndividual createOffspring(final int[] mutationVector, final int jRand,
UniformRealDistribution rndCrDist) {
assert (m_attributes.length == mutationVector.length);
JadeIndividual offspring = new JadeIndividual(m_attributes.length);
for (int j = 0; j < m_attributes.length; j++) {
double crRnd = rndCrDist.sample();
if ((j == jRand) || (crRnd < m_crossoverProbability)) {
offspring.m_attributes[j] = mutationVector[j];
} else {
offspring.m_attributes[j] = m_attributes[j];
}
}
return offspring;
}
public int getAttribute(final int attributeIndex) {
return m_attributes[attributeIndex];
}
public final int[] getAttributes() {
return m_attributes;
}
@Override
public double getFitness() {
return m_fitness;
}
@Override
public void setFitness(double fitness) {
m_fitness = fitness;
}
public double getMutationFactor() {
return m_mutationFactor;
}
public void setMutationFactor(final double mutationFactor) {
this.m_mutationFactor = mutationFactor;
}
public double getCrossoverProbability() {
return m_crossoverProbability;
}
public void setCrossoverProbability(final double crossoverProb) {
this.m_crossoverProbability = crossoverProb;
}
@Override
public int compareTo(JadeIndividual other) {
return Double.compare(m_fitness, other.m_fitness);
}
@Override
public int hashCode() {
return Arrays.hashCode(m_attributes);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof JadeIndividual) {
return (this.hashCode() == ((JadeIndividual) obj).hashCode());
} else {
return super.equals(obj);
}
}
}
package quantization.de.jade;
import org.apache.commons.math3.distribution.CauchyDistribution;
import org.apache.commons.math3.distribution.NormalDistribution;
import org.apache.commons.math3.distribution.UniformIntegerDistribution;
import org.apache.commons.math3.distribution.UniformRealDistribution;
import org.apache.commons.math3.geometry.Vector;
import org.apache.commons.math3.random.MersenneTwister;
import org.apache.commons.math3.random.RandomGenerator;
import quantization.Quantizer;
import quantization.U16;
import quantization.Utils;
import quantization.de.IDESolver;
import quantization.de.IIndividual;
import quantization.de.DeException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
public class JadeSolver implements IDESolver {
public static final int MinimalPopulationSize = 5;
private int m_minConstraint = U16.Min;
private int m_maxConstraint = U16.Max;
private int m_populationSize;
private int m_generationCount;
private int m_dimension;
private double m_muCr = 0.5;
private double m_muF = 0.5;
private double m_parameterAdaptationRate = 0.05;
private double m_mutationGreediness = 0.1;
private JadeIndividual[] m_currentPopulation;
private int m_maxArchiveSize;
private ArrayList<JadeIndividual> m_archive;
private RandomGenerator rg;
private int[] m_trainingData;
public JadeSolver() {
rg = new MersenneTwister();
}
public JadeSolver(final int dimension, final int populationSize, final int generationCount) {
this();
m_dimension = dimension;
m_populationSize = populationSize;
m_generationCount = generationCount;
m_maxArchiveSize = m_populationSize;
}
public JadeSolver(final int dimension, final int populationSize, final int generationCount,
final double parameterAdaptationRate, final double mutationGreediness) {
this(dimension, populationSize, generationCount);
m_parameterAdaptationRate = parameterAdaptationRate;
m_mutationGreediness = mutationGreediness;
}
private double arithmeticMean(final ArrayList<Double> values) {
double sum = 0.0;
for (int i = 0; i < values.size(); i++) {
sum += values.get(i);
}
double result = sum / (double) values.size();
return result;
}
private double lehmerMean(final ArrayList<Double> values) {
double numerator = 0.0;
double denominator = 0.0;
double value;
for (int i = 0; i < values.size(); i++) {
value = values.get(i);
numerator += Math.pow(value, 2);
denominator += value;
}
double result = numerator / denominator;
return result;
}
private void assertPopulationSize() throws DeException {
if (m_populationSize < MinimalPopulationSize) {
throw new DeException("Population size is too low. Required population size >= 5.");
}
}
private void assertDimension() throws DeException {
if (m_dimension < 1) {
throw new DeException("Dimension is too low. Required dimension >= 1.");
}
}
/**
* Generate individual attributes, so that all are unique.
*
* @param distribution Uniform integer distribution with constraints.
* @return Array of unique attributes.
*/
private int[] generateIndividualAttribues(UniformIntegerDistribution distribution) {
int[] attributes = new int[m_dimension];
for (int dim = 0; dim < m_dimension; dim++) {
int rndValue = distribution.sample();
while (Utils.arrayContainsToIndex(attributes, dim, rndValue)) {
rndValue = distribution.sample();
}
attributes[dim] = rndValue;
}
Arrays.sort(attributes);
return attributes;
}
/**
* Generate initial population based on population size and constraints. Also allocates archive.
*
* @throws DeException Throws exception on wrong population size or wrong dimension.
*/
private void generateInitialPopulation() throws DeException {
assertPopulationSize();
assertDimension();
assert m_populationSize > 0;
m_currentPopulation = new JadeIndividual[m_populationSize];
m_archive = new ArrayList<JadeIndividual>(m_maxArchiveSize);
UniformIntegerDistribution uniformIntDistribution = new UniformIntegerDistribution(rg, m_minConstraint, m_maxConstraint);
// NOTE(Moravec): What if we cheat and hardcode the first and last parameter?
for (int individualIndex = 0; individualIndex < m_populationSize; individualIndex++) {
int[] individualAttributes = generateIndividualAttribues(uniformIntDistribution);
m_currentPopulation[individualIndex] = JadeIndividual.NewIndividual(individualAttributes);
}
}
/**
* Select random individual from p*100% top individuals.
*
* @param pBestDistribution Distribution for p*100% random.
* @param others Other individuals.
* @return Random individual from p*100%.
*/
private JadeIndividual getRandomFromPBest(UniformIntegerDistribution pBestDistribution,
final JadeIndividual... others) {
JadeIndividual[] sorted = Arrays.copyOf(m_currentPopulation, m_currentPopulation.length);
Arrays.sort(sorted);
int rndIndex = pBestDistribution.sample();
while (Utils.arrayContains(others, sorted[rndIndex])) {
rndIndex = pBestDistribution.sample();
}
return sorted[rndIndex];
}
/**
* Get random individual from current population, distinct from the other.
*
* @param rndIndDist Distribution of current population random.
* @param others Other individuals.
* @return Distinct random individual from the another one.
*/
private JadeIndividual getRandomFromCurrentPopulation(UniformIntegerDistribution rndIndDist, final JadeIndividual... others) {
JadeIndividual rndIndiv = m_currentPopulation[rndIndDist.sample()];
while (Utils.arrayContains(others, rndIndiv)) {
rndIndiv = m_currentPopulation[rndIndDist.sample()];
}
return rndIndiv;
}
/**
* Get random individual (different from two others) from union of current population and archive.
*
* @param rndUnionDist Random distribution for the union of current population and archive.
* @param others Other individuals.
* @return Random individual from union of current population and archive.
*/
private JadeIndividual getRandomFromUnion(UniformIntegerDistribution rndUnionDist,
final JadeIndividual... others) {
int rndIndex = rndUnionDist.sample();
JadeIndividual rndIndiv = (rndIndex >= m_populationSize) ? m_archive.get(rndIndex - m_populationSize) : m_currentPopulation[rndIndex];
while (Utils.arrayContains(others, rndIndiv)) {
rndIndex = rndUnionDist.sample();
rndIndiv = (rndIndex >= m_populationSize) ? m_archive.get(rndIndex - m_populationSize) : m_currentPopulation[rndIndex];
}
return rndIndiv;
}
private double getIndividualFitness(final JadeIndividual individual) {
Quantizer individualQuantizer = new Quantizer(m_minConstraint, m_maxConstraint, individual.getAttributes());
double mse = individualQuantizer.getMse(m_trainingData);
return mse;
}
/**
* Calculate fitness values for individuals in current population.
*/
private double calculateFitnessForPopulation() {
double avg = 0.0;
// TODO(Moravec): Parallelize.
for (int individualIndex = 0; individualIndex < m_populationSize; individualIndex++) {
double individualFitness = getIndividualFitness(m_currentPopulation[individualIndex]);
m_currentPopulation[individualIndex].setFitness(individualFitness);
avg += individualFitness;
System.out.print(String.format("\rFinished fitness for individual %d/%d", individualIndex + 1, m_populationSize));
}
System.out.println();
avg /= (double) m_populationSize;
return avg;
}
private int[] createMutationVector(final JadeIndividual current,
final JadeIndividual x_p_Best,
final JadeIndividual x_r1,
final JadeIndividual x_r2) {
int[] mutationVector = new int[m_dimension];
double mutationFactor = current.getMutationFactor();
for (int j = 0; j < m_dimension; j++) {
// TODO: Check constraints! (p.3)
mutationVector[j] = (int) Math.floor(current.getAttribute(j) +
(mutationFactor * ((double) x_p_Best.getAttribute(j) - current.getAttribute(j))) +
(mutationFactor * ((double) x_r1.getAttribute(j) - x_r2.getAttribute(j))));
if (mutationVector[j] < m_minConstraint) {
mutationVector[j] = (m_minConstraint + current.getAttribute(j)) / 2;
} else if (mutationVector[j] > m_maxConstraint) {
mutationVector[j] = (m_maxConstraint + current.getAttribute(j)) / 2;
assert (mutationVector[j] <= m_maxConstraint);
}
}
return mutationVector;
}
private void truncateArchive() {
int deleteCount = m_archive.size() - m_maxArchiveSize;
if (deleteCount > 0) {
Random random = new Random();
for (int i = 0; i < deleteCount; i++) {
m_archive.remove(random.nextInt(m_archive.size()));
}
}
assert (m_archive.size() <= m_maxArchiveSize);
}
private double generateMutationFactor(CauchyDistribution dist) {
double factor = dist.sample();
while (factor <= 0.0) {
factor = dist.sample();
}
if (factor > 1.0) {
factor = 1.0;
}
assert (factor > 0.0 && factor <= 1.0);
return factor;
}
private double generateCrossoverProbability(NormalDistribution dist) {
double prob = dist.sample();
if (prob < 0.0) {
prob = 0.0;
} else if (prob > 1.0) {
prob = 1.0;
}
return prob;
}
@Override
public void train() throws DeException {
if (m_trainingData == null || m_trainingData.length <= 0) {
throw new DeException("Training data weren't set.");
}
m_muCr = 0.5;
m_muF = 0.5;
int pBestUpperLimit = (int) Math.floor(m_populationSize * m_mutationGreediness);
UniformIntegerDistribution rndPBestDist =
new UniformIntegerDistribution(rg, 0, (pBestUpperLimit - 1));
UniformIntegerDistribution rndIndDist =
new UniformIntegerDistribution(rg, 0, (m_populationSize - 1));
UniformIntegerDistribution rndJRandDist = new UniformIntegerDistribution(rg, 0, (m_dimension - 1));
UniformRealDistribution rndCrDist = new UniformRealDistribution(rg, 0.0, 1.0);
generateInitialPopulation();
double avgFitness = calculateFitnessForPopulation();
System.out.println(String.format("Generation %d average fitness(COST): %.5f", 0, avgFitness));
ArrayList<Double> successfulCr = new ArrayList<Double>(m_populationSize);
ArrayList<Double> successfulF = new ArrayList<Double>(m_populationSize);
NormalDistribution crNormalDistribution;
CauchyDistribution fCauchyDistribution;
for (int generation = 0; generation < m_generationCount; generation++) {
successfulCr.clear();
successfulF.clear();
JadeIndividual[] nextPopulation = new JadeIndividual[m_populationSize];
UniformIntegerDistribution rndPopArchiveDist =
new UniformIntegerDistribution(rg, 0, ((m_populationSize - 1) + m_archive.size()));
crNormalDistribution = new NormalDistribution(rg, m_muCr, 0.1);
fCauchyDistribution = new CauchyDistribution(rg, m_muF, 0.1);
// NOTE(Moravec): Inner code could be parallelized.
for (int i = 0; i < m_populationSize; i++) {
JadeIndividual current = m_currentPopulation[i];
current.setCrossoverProbability(generateCrossoverProbability(crNormalDistribution));
assert (current.getCrossoverProbability() >= 0.0 && current.getCrossoverProbability() <= 1.0);
current.setMutationFactor(generateMutationFactor(fCauchyDistribution));
JadeIndividual x_p_Best = getRandomFromPBest(rndPBestDist, current);
JadeIndividual x_r1 = getRandomFromCurrentPopulation(rndIndDist, current, x_p_Best);
JadeIndividual x_r2 = getRandomFromUnion(rndPopArchiveDist, current, x_p_Best, x_r1);
int[] mutationVector = createMutationVector(current, x_p_Best, x_r1, x_r2);
int jRand = rndJRandDist.sample();
JadeIndividual offspring = current.createOffspring(mutationVector, jRand, rndCrDist);
double offspringFitness = getIndividualFitness(offspring);
// NOTE(Moravec): We are minimalizing!
if (offspringFitness <= current.getFitness()) {
nextPopulation[i] = offspring;
successfulCr.add(current.getCrossoverProbability());
successfulF.add(current.getMutationFactor());
m_archive.add(current);
} else {
nextPopulation[i] = current;
}
}
double oldMuCr = m_muCr, oldMuF = m_muF;
m_muCr = ((1.0 - m_parameterAdaptationRate) * m_muCr) + (m_parameterAdaptationRate * arithmeticMean(successfulCr));
m_muF = ((1.0 - m_parameterAdaptationRate) * m_muF) + (m_parameterAdaptationRate * lehmerMean(successfulF));
System.out.println(String.format("Old μCR: %.4f New μCR: %.4f\nOld μF: %.4f New μF: %.4f",
oldMuCr, m_muCr, oldMuF, m_muF));
truncateArchive();
m_currentPopulation = nextPopulation;
avgFitness = calculateFitnessForPopulation();
System.out.println(String.format("Generation %d average fitness(COST): %.5f", (generation + 1), avgFitness));
}
}
@Override
public void setTrainingData(int[] data) {
m_trainingData = data;
}
@Override
public void setMinimalValueConstraint(final int min) {
m_minConstraint = min;
}
@Override
public void setMaximalValueConstraint(final int max) {
m_maxConstraint = max;
}
@Override
public void setPopulationSize(final int populationSize) throws DeException {
assertPopulationSize();
m_populationSize = populationSize;
}
@Override
public void setGenerationCount(final int generationCount) {
m_generationCount = generationCount;
}
@Override
public void setDimension(final int dimension) {
m_dimension = dimension;
}
@Override
public IIndividual getBestSolution() {
return null;
}
public double getParameterAdaptationRate() {
return m_parameterAdaptationRate;
}
public void setParameterAdaptationRate(double parameterAdaptationRate) {
this.m_parameterAdaptationRate = parameterAdaptationRate;
}
public double getMutationGreediness() {
return m_mutationGreediness;
}
public void setMutationGreediness(double mutationGreediness) {
this.m_mutationGreediness = mutationGreediness;
}
public int getMaxArchiveSize() {
return m_maxArchiveSize;
}
public void setMaxArchiveSize(int maxArchiveSize) {
this.m_maxArchiveSize = maxArchiveSize;
}
}
package quantization.jade;
public class Individual {
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment