mase.spec.StochasticHybridExchanger.java Source code

Java tutorial

Introduction

Here is the source code for mase.spec.StochasticHybridExchanger.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package mase.spec;

import ec.EvolutionState;
import ec.Individual;
import ec.Subpopulation;
import ec.simple.SimpleFitness;
import ec.util.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import mase.evaluation.BehaviourResult;
import org.apache.commons.lang3.tuple.Pair;

/**
 *
 * @author jorge
 */
public class StochasticHybridExchanger extends AbstractHybridExchanger {

    // algorithm parameter values
    public static final String P_MERGE_THRESHOLD = "merge-threshold";
    public static final String P_MAX_LOCKDOWN = "max-lockdown";

    // algorithm implementation modes
    public static final String P_DISTANCE_CALCULATOR = "distance-calculator";
    public static final String P_DISTANCE_ELITE = "distance-elite";
    public static final String P_MERGE_SELECTION = "merge-selection";
    public static final String P_MERGE_AGENTS = "merge-agents";
    public static final String P_SPLIT_AGENTS = "split-agents";

    private static final long serialVersionUID = 1L;

    public enum MergeSelection {

        truncate, fitnessproportionate, random
    }

    public enum MergeAgentsProportion {

        equal, largest, proportionate

    }

    public enum SplitAgentsProportion {

        one, half, random

    }

    double mergeThreshold;
    int maxLockdown;

    double distanceElite;
    MergeSelection mergeSelection;
    MergeAgentsProportion mergeProportion;
    SplitAgentsProportion splitProportion;

    DistanceCalculator distCalculator;
    double[][] distanceMatrix = new double[0][0];

    @Override
    public void setup(EvolutionState state, Parameter base) {
        super.setup(state, base);
        mergeThreshold = state.parameters.getDouble(base.push(P_MERGE_THRESHOLD), null);
        maxLockdown = state.parameters.getInt(base.push(P_MAX_LOCKDOWN), null);
        distanceElite = state.parameters.getDouble(base.push(P_DISTANCE_ELITE), null);
        mergeSelection = MergeSelection.valueOf(state.parameters.getString(base.push(P_MERGE_SELECTION), null));
        mergeProportion = MergeAgentsProportion
                .valueOf(state.parameters.getString(base.push(P_MERGE_AGENTS), null));
        splitProportion = SplitAgentsProportion
                .valueOf(state.parameters.getString(base.push(P_SPLIT_AGENTS), null));
        distCalculator = (DistanceCalculator) state.parameters
                .getInstanceForParameter(base.push(P_DISTANCE_CALCULATOR), null, DistanceCalculator.class);
        distCalculator.setup(state, base.push(DistanceCalculator.P_BASE));
    }

    @Override
    protected void initializationProcess(EvolutionState state) {
        super.initializationProcess(state);
        for (MetaPopulation mp : metaPops) {
            mp.lockDown = calculateLockDown(mp, state);
        }
    }

    protected int calculateLockDown(MetaPopulation pop, EvolutionState state) {
        return state.random[0].nextInt(maxLockdown);
    }

    /*****************
     * MERGE PROCESS
     *****************/

    /* Similar to bottom-up (agglomerative) hierarchical clustering */
    @Override
    protected int mergeProcess(EvolutionState state) {
        distanceMatrix = computeMetapopDistances(state);

        Pair<MetaPopulation, MetaPopulation> nextMerge = findNextMerge(distanceMatrix, state);

        // Merge if they are similar
        if (nextMerge != null) {
            state.output.message("*** Merging " + nextMerge.getLeft() + " with " + nextMerge.getRight() + " ***");
            MetaPopulation mpNew = mergePopulations(nextMerge.getLeft(), nextMerge.getRight(), state);
            mpNew.lockDown = calculateLockDown(mpNew, state);
            metaPops.remove(nextMerge.getLeft());
            metaPops.remove(nextMerge.getRight());
            metaPops.add(mpNew);
            return 1;
        }
        return 0;
    }

    protected Pair<MetaPopulation, MetaPopulation> findNextMerge(double[][] distanceMatrix, EvolutionState state) {
        MetaPopulation closeI = null, closeJ = null;
        double closest = 0;
        for (int i = 0; i < metaPops.size(); i++) {
            for (int j = i + 1; j < metaPops.size(); j++) {
                double d = distanceMatrix[i][j];
                if (metaPops.get(i).age >= metaPops.get(i).lockDown
                        && metaPops.get(j).age >= metaPops.get(j).lockDown && (closeI == null || d < closest)) {
                    closeI = metaPops.get(i);
                    closeJ = metaPops.get(j);
                    closest = d;
                }
            }
        }
        if (closeI != null) {
            state.output.message("*** Closest: " + closeI + " " + closeJ + " -- " + closest + " ***");
        }
        if (closeI != null && closest <= mergeThreshold) {
            return Pair.of(closeI, closeJ);
        }
        return null;
    }

    protected MetaPopulation mergePopulations(MetaPopulation mp1, MetaPopulation mp2, EvolutionState state) {
        // Create new MetaPop
        MetaPopulation mpNew = new MetaPopulation();
        if (mp1.age > mp2.age) {
            mpNew.agents = new ArrayList<>(mp1.agents);
            mpNew.agents.addAll(mp2.agents);
        } else {
            mpNew.agents = new ArrayList<>(mp2.agents);
            mpNew.agents.addAll(mp1.agents);
        }

        if (mp1.agents.size() > mp2.agents.size()) {
            mpNew.pop = (Subpopulation) mp1.pop.emptyClone();
        } else {
            mpNew.pop = (Subpopulation) mp2.pop.emptyClone();
        }
        mpNew.pop.individuals = new Individual[mp1.pop.individuals.length];
        fillNewPopulation(mpNew, mp1, mp2, state);
        return mpNew;
    }

    protected void fillNewPopulation(MetaPopulation mpNew, MetaPopulation mp1, MetaPopulation mp2,
            EvolutionState state) {
        // The number of individuals to pick from each pop
        int from1 = 0;
        if (mergeProportion == MergeAgentsProportion.equal) {
            from1 = mpNew.pop.individuals.length / 2;
        } else if (mergeProportion == MergeAgentsProportion.largest) {
            from1 = mp1.agents.size() >= mp2.agents.size() ? mpNew.pop.individuals.length : 0;
        } else if (mergeProportion == MergeAgentsProportion.proportionate) {
            from1 = Math.round((float) mp1.agents.size() / (mp1.agents.size() + mp2.agents.size())
                    * mpNew.pop.individuals.length);
        }
        int from2 = mpNew.pop.individuals.length - from1;

        Individual[] picked1 = selectIndividuals(mp1.pop.individuals, from1, mergeSelection, state);
        Individual[] picked2 = selectIndividuals(mp2.pop.individuals, from2, mergeSelection, state);
        System.arraycopy(picked1, 0, mpNew.pop.individuals, 0, picked1.length);
        System.arraycopy(picked2, 0, mpNew.pop.individuals, picked1.length, picked2.length);
    }

    protected Individual[] selectIndividuals(Individual[] pool, int num, MergeSelection mode,
            EvolutionState state) {
        Individual[] picked = new Individual[num];
        if (mode == MergeSelection.truncate) {
            Individual[] sorted = sortedCopy(pool);
            System.arraycopy(sorted, 0, picked, 0, num);
        } else if (mode == MergeSelection.fitnessproportionate) {
            double total = 0;
            LinkedList<Individual> poolList = new LinkedList<>();
            for (Individual ind : pool) {
                poolList.add(ind);
                total += ((SimpleFitness) ind.fitness).fitness();
            }
            int index = 0;
            while (index < num) {
                double accum = 0;
                double rand = state.random[0].nextDouble() * total;
                Iterator<Individual> iter = poolList.iterator();
                while (iter.hasNext()) {
                    Individual ind = iter.next();
                    accum += ((SimpleFitness) ind.fitness).fitness();
                    if (accum >= rand) {
                        picked[index++] = ind;
                        iter.remove();
                        total -= ((SimpleFitness) ind.fitness).fitness();
                        break;
                    }
                }
            }
        } else if (mode == MergeSelection.random) {
            LinkedList<Individual> poolList = new LinkedList<>(Arrays.asList(pool));
            int index = 0;
            while (index < num) {
                int rand = state.random[0].nextInt(poolList.size());
                picked[index++] = poolList.get(rand);
                poolList.remove(rand);
            }
        } else {
            state.output.fatal("Unknown picking mode: " + mode);
        }
        return picked;
    }

    protected double[][] computeMetapopDistances(EvolutionState state) {
        // Retrieve agent behaviours, aggregated by MetaPopulation
        List<BehaviourResult>[] behavs = new List[metaPops.size()];
        for (int i = 0; i < metaPops.size(); i++) {
            MetaPopulation mp = metaPops.get(i);
            if (mp.age < mp.lockDown) {
                behavs[i] = Collections.EMPTY_LIST;
            } else {
                behavs[i] = new ArrayList<>();
                Individual[] elite = getElitePortion(mp.pop.individuals, (int) Math.ceil(distanceElite * popSize));
                for (Individual ind : elite) {
                    for (Integer a : mp.agents) {
                        behavs[i].add(getAgentBR(ind, a, behaviourIndex));
                    }
                }
            }
        }
        return distCalculator.computeDistances(behavs, state);
    }

    /*****************
     * SPLIT PROCESS
     *****************/

    @Override
    protected int splitProcess(EvolutionState state) {
        // Find new splits
        MetaPopulation parent = findNextSplit(state);

        // Create new metapop
        if (parent != null) {
            MetaPopulation child = fork(parent, state);
            metaPops.add(child);
            parent.lockDown = calculateLockDown(parent, state);
            child.lockDown = parent.lockDown; // same lockdown to give them a chance to remerge before any further splits
            state.output.message("*** Spliting " + child + " from " + parent + " ***");
            return 1;
        }
        return 0;
    }

    protected MetaPopulation findNextSplit(EvolutionState state) {
        MetaPopulation chosen = null;
        for (MetaPopulation mp : metaPops) {
            if (mp.agents.size() > 1 && mp.age >= mp.lockDown && (chosen == null || mp.age > chosen.age)) {
                chosen = mp;
            }
        }
        if (chosen != null) {
            return chosen;
        }
        return null;
    }

    protected MetaPopulation fork(MetaPopulation parent, EvolutionState state) {
        List<Integer> forkAgents = new ArrayList<>();

        if (splitProportion == SplitAgentsProportion.one || parent.agents.size() <= 2) { // A random one is split
            int ag = state.random[0].nextInt(parent.agents.size());
            forkAgents.add(parent.agents.get(ag));
        } else if (splitProportion == SplitAgentsProportion.half) { // A random sample of size = half of agents
            int num = parent.agents.size() / 2;
            List<Integer> copy = new ArrayList<>(parent.agents);
            Collections.shuffle(copy);
            for (int i = 0; i < num; i++) {
                forkAgents.add(copy.get(i));
            }
        } else if (splitProportion == SplitAgentsProportion.random) { // A random sample of random size
            int num = 1 + state.random[0].nextInt(parent.agents.size() - 1);
            List<Integer> copy = new ArrayList<>(parent.agents);
            Collections.shuffle(copy);
            for (int i = 0; i < num; i++) {
                forkAgents.add(copy.get(i));
            }
        }

        parent.age = 0;
        parent.agents.removeAll(forkAgents);

        MetaPopulation child = new MetaPopulation();
        child.agents.addAll(forkAgents);
        child.pop = (Subpopulation) parent.pop.emptyClone();
        child.pop.individuals = new Individual[parent.pop.individuals.length];
        for (int k = 0; k < parent.pop.individuals.length; k++) {
            child.pop.individuals[k] = (Individual) parent.pop.individuals[k].clone();
        }
        return child;
    }
}