All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.barrybecker4.optimization.parameter.VariableLengthIntArray Maven / Gradle / Ivy

/** Copyright by Barry G. Becker, 2013. Licensed under MIT License: http://www.opensource.org/licenses/MIT  */
package com.barrybecker4.optimization.parameter;

import com.barrybecker4.common.math.MathUtil;
import com.barrybecker4.optimization.optimizee.Optimizee;
import com.barrybecker4.optimization.parameter.distancecalculators.DistanceCalculator;
import com.barrybecker4.optimization.parameter.distancecalculators.MagnitudeIgnoredDistanceCalculator;
import com.barrybecker4.optimization.parameter.improvement.DiscreteImprovementFinder;
import com.barrybecker4.optimization.parameter.improvement.Improvement;
import com.barrybecker4.optimization.parameter.sampling.VariableLengthGlobalSampler;
import com.barrybecker4.optimization.parameter.types.IntegerParameter;
import com.barrybecker4.optimization.parameter.types.Parameter;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

/**
 *  Represents a 1 dimensional, variable length, array of unique integer parameters.
 *  The order of the integers does not matter.
 *
 *  @author Barry Becker
 */
public class VariableLengthIntArray extends AbstractParameterArray {

    private List fullSet;

    /** Default constructor */
    protected VariableLengthIntArray() {}

    private DistanceCalculator distCalculator;

    /**
     * Constructor
     * @param params an array of params to initialize with.
     * @param fullSet the full set of all integer parameters.
     */
    public VariableLengthIntArray(List params, List fullSet, DistanceCalculator distCalc) {
        super(params);
        this.fullSet = fullSet;
        assert distCalc != null;
        this.distCalculator = distCalc;
    }

    public static VariableLengthIntArray createInstance(List params, List fullSet) {
        return createInstance(params, fullSet, new MagnitudeIgnoredDistanceCalculator());
    }

    public static VariableLengthIntArray createInstance(
            List params, List fullSet, DistanceCalculator distanceCalculator) {
        return new VariableLengthIntArray(params, fullSet, distanceCalculator);
    }


    /** @return the maximum length of the variable length array */
    public int getMaxLength() {
        return fullSet.size();
    }

    @Override
    protected VariableLengthIntArray createInstance() {
        return new VariableLengthIntArray();
    }

    /**
     * The distance computation will be quite different for this than a regular parameter array.
     * We want the distance to represent a measure of the amount of similarity between two instances.
     * There are two ways in which instance can differ, and the weighting assigned to each may depend on the problem.
     *  - the length of the parameter array
     *  - the set of values in the parameter array.
     * Generally, the distance is greater the greater the number of parameters that are different.
     * @return the distance between this parameter array and another.
     */
    public double distance(ParameterArray pa)  {
        return distCalculator.calculateDistance(this, pa);
    }

    /**
     * Create a new permutation that is not too distant from what we have now.
     * The two ways a configuration of marked nodes can change is
     *  - add or remove nodes
     *  - change values of nodes
     * @param radius a indication of the amount of variation to use. 0 is none, 2 is a lot.
     *   Change Math.min(1, 10 * radius * N/100) of the entries, where N is the number of params
     * @return the random nbr.
     */
    public VariableLengthIntArray getRandomNeighbor(double radius) {

        if (size() <= 1) return this;

        double probAddRemove = 1.0/(1.0 + radius);
        boolean add = false;
        boolean remove = false;
        if (MathUtil.RANDOM.nextDouble() > probAddRemove) {
            if ((MathUtil.RANDOM.nextDouble() > 0.5 || size() <= 1) && size() < getMaxLength()-1 ) {
                add = true;
            }
            else {
                remove = true;
            }
        }
        int numNodesToMove;
        VariableLengthIntArray nbr = (VariableLengthIntArray)this.copy();

        if (add || remove) {
            numNodesToMove = MathUtil.RANDOM.nextInt(Math.min(size(), (int)(radius + 1)));
        }
        else {
            numNodesToMove = 1 + MathUtil.RANDOM.nextInt(1 + (int)radius);
        }

        if (remove) {
            removeRandomParam(nbr);
        }
        if (add) {
            addRandomParam(nbr);
        }
        moveNodes(numNodesToMove, nbr);
        return nbr;
    }

    public void setCombination(List indices) {
         assert indices.size() <= getMaxLength() :
                "The number of indices (" + indices.size() + ") was greater than the size (" + size() + ")";
         List newParams = new ArrayList<>(size());
         for (int i : indices) {
             newParams.add(createParam(fullSet.get(i)));
         }
         params_ = newParams;
    }

    /**
     * Globally sample the parameter space.
     *
     * @param requestedNumSamples approximate number of samples to retrieve.
     * If the problem space is small and requestedNumSamples is large, it may not be possible to return this
     * many unique samples.
     * @return some number of unique samples.
     */
    public Iterator findGlobalSamples(long requestedNumSamples) {
        return new VariableLengthGlobalSampler(this, requestedNumSamples);
    }

    /**
     * {@inheritDoc}
     * Try swapping parameters randomly until we find an improvement (if we can).
     */
    public Improvement findIncrementalImprovement(Optimizee optimizee, double jumpSize,
                                                  Improvement lastImprovement, Set cache) {
        DiscreteImprovementFinder finder = new DiscreteImprovementFinder(this);
        return finder.findIncrementalImprovement(optimizee, jumpSize, cache);
    }

    /**
     * @return get a completely random solution in the parameter space.
     */
    public ParameterArray getRandomSample() {

        List marked = new LinkedList<>();
        for (int i = 0; i < getMaxLength(); i++) {
            if (MathUtil.RANDOM.nextDouble() > 0.5) {
                marked.add(fullSet.get(i));
            }
        }
        List newParams = new ArrayList<>();
        for (int markedNode : marked) {
            newParams.add(createParam(markedNode));
        }

        return new VariableLengthIntArray(newParams, fullSet, distCalculator);
    }

    /**
     * @return a copy of ourselves.
     */
    public AbstractParameterArray copy() {
        VariableLengthIntArray copy = (VariableLengthIntArray) super.copy();
        copy.fullSet = this.fullSet;
        copy.distCalculator = this.distCalculator;
        return copy;
    }

    /**
     * @param i the integer parameter's value. May be Negative
     * @return a new integer parameter.
     */
    private Parameter createParam(int i) {
        return  new IntegerParameter(i, (i<0) ? i : 0, (i>=0) ? i : 0, "p" + i);
    }

    private void removeRandomParam(VariableLengthIntArray nbr) {
        int indexToRemove = MathUtil.RANDOM.nextInt(size());
        assert nbr.size() > 0;
        List newParams = new ArrayList<>(nbr.size()-1);

        for (int i=0; i < nbr.size(); i++) {
            if (i != indexToRemove) {
                newParams.add(nbr.get(i));
            }
        }
        nbr.params_ = newParams;
    }

    private void addRandomParam(VariableLengthIntArray nbr) {

        List freeNodes = getFreeNodes(nbr);
        int newSize = nbr.size() + 1;
        assert newSize <= getMaxLength();
        List newParams = new ArrayList<>(newSize);
        for (Parameter p : nbr.params_) {
            newParams.add(p);
        }
        int value = freeNodes.get(MathUtil.RANDOM.nextInt(freeNodes.size()));
        newParams.add(createParam(value));
        nbr.params_ = newParams;
    }

    /**
     * select num free nodes randomly and and swap them with num randomly selected marked nodes.
     * @param numNodesToMove number of nodes to move to new locations
     * @param nbr neighbor parameter array
     */
    private void moveNodes(int numNodesToMove, VariableLengthIntArray nbr) {
        List freeNodes = getFreeNodes(nbr);
        int numSelect = Math.min(freeNodes.size(), numNodesToMove);
        List swapNodes = selectRandomNodes(numSelect, freeNodes);

        for (int i=0; i selectRandomNodes(int numNodesToSelect, List freeNodes) {
        List selected = new LinkedList<>();
        for (int i=0; i < numNodesToSelect; i++) {
            int node = freeNodes.get(MathUtil.RANDOM.nextInt(freeNodes.size()));
            selected.add(node);
            freeNodes.remove((Integer) node);
        }
        return selected;
    }

    private List getFreeNodes(VariableLengthIntArray nbr) {
        List freeNodes = new ArrayList<>(getMaxLength());
        Set markedNodes = new HashSet<>();
        for (Parameter p : nbr.params_) {
            markedNodes.add((int)p.getValue());
        }

        for (int i = 0; i < getMaxLength(); i++) {
            if (!markedNodes.contains(fullSet.get(i)))   {
                freeNodes.add(fullSet.get(i));
            }
        }
        return freeNodes;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy