SubsetSelector.java

/*
 * Copyright (C) 2013 Alberto Irurueta Carro (alberto@irurueta.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.irurueta.numerical.robust;

/**
 * Base class to pick subsets of samples.
 */
public abstract class SubsetSelector {

    /**
     * Constant defining minimum amount of allowed samples.
     */
    public static final int MIN_NUM_SAMPLES = 1;

    /**
     * Defines default subset selector type.
     */
    public static final SubsetSelectorType DEFAULT_SUBSET_SELECTOR_TYPE =
            SubsetSelectorType.FAST_RANDOM_SUBSET_SELECTOR;

    /**
     * Total number of samples to pick subsets from.
     * Subsets need to be always smaller or equal than total number of samples.
     */
    protected int numSamples;

    /**
     * Constructor.
     *
     * @param numSamples number of samples to select subsets from.
     * @throws IllegalArgumentException if provided number of samples is zero
     *                                  or negative.
     */
    protected SubsetSelector(final int numSamples) {
        setNumSamples(numSamples);
    }

    /**
     * Returns number of samples to select subsets from.
     *
     * @return number of samples to select subsets from.
     */
    public int getNumSamples() {
        return numSamples;
    }

    /**
     * Sets number of samples to select subsets from.
     *
     * @param numSamples number of samples to select subsets from.
     * @throws IllegalArgumentException if provided number of samples is zero or
     *                                  negative.
     */
    public final void setNumSamples(final int numSamples) {
        if (numSamples < MIN_NUM_SAMPLES) {
            throw new IllegalArgumentException();
        }
        this.numSamples = numSamples;
    }

    /**
     * Computes a random subset of indices within range of number of samples to
     * be used on robust estimators.
     * If subsets need to be computed repeatedly in a small span of time then it
     * is suggested to use computeRandomSubsets(int, int[]) for better memory
     * usage.
     *
     * @param subsetSize subset size to be computed. This value must be smaller
     *                   than total number of samples.
     * @return array containing indices to be picked.
     * @throws NotEnoughSamplesException  if subset size is greater than the
     *                                    total number of samples.
     * @throws InvalidSubsetSizeException if subset size is zero or if result
     *                                    array does not have at least a length of subsetSize.
     * @see #computeRandomSubsets(int, int[])
     */
    public int[] computeRandomSubsets(final int subsetSize) throws NotEnoughSamplesException,
            InvalidSubsetSizeException {
        final var result = new int[subsetSize];
        computeRandomSubsets(subsetSize, result);
        return result;
    }

    /**
     * Computes a random subset of indices within provided range of positions to
     * be used on robust estimators.
     * If subsets need to be computed repeatedly in a small span of time then it
     * is suggested to use computeRandomSubsets(int, int, int, bool, int[]) for
     * better memory usage.
     *
     * @param minPos     minimum position to be picked. This value must be greater
     *                   or equal than zero and smaller than the total number of samples and less
     *                   than maxPos.
     * @param maxPos     maximum position to be picked. This value must be greater
     *                   or equal than zero and smaller than the total number of samples and
     *                   greater than minPos.
     * @param subsetSize subset size to be computed. This value must be smaller
     *                   than total number of samples.
     * @param pickLast   true indicates that last sample in range must always be
     *                   picked within subset. This is done to obtain faster execution times and
     *                   greater stability on some algorithms.
     * @return array containing indices to be picked.
     * @throws NotEnoughSamplesException   if subset size is greater than the
     *                                     total number of samples or if maxPos is greater than the total number of
     *                                     samples.
     * @throws InvalidSubsetSizeException  if subset size is zero, or if subset
     *                                     size is greater than the allowed range of positions to be picked.
     * @throws InvalidSubsetRangeException if maximum position is smaller than
     *                                     minimum position or maximum or minimum position are negative.
     */
    public int[] computeRandomSubsetsInRange(
            final int minPos, final int maxPos, final int subsetSize, final boolean pickLast)
            throws NotEnoughSamplesException, InvalidSubsetSizeException, InvalidSubsetRangeException {
        final var result = new int[subsetSize];
        computeRandomSubsetsInRange(minPos, maxPos, subsetSize, pickLast, result);
        return result;
    }

    /**
     * Returns type of this subset selector.
     *
     * @return type of this subset selector.
     */
    public abstract SubsetSelectorType getType();

    /**
     * Computes a random subset of indices within range of number of samples to
     * be used on robust estimators.
     *
     * @param subsetSize subset size to be computed. This value must be smaller
     *                   than total number of samples.
     * @param result     array containing indices to be picked. Provided array must
     *                   be at least of length subsetSize. The former subsetSize entries of the
     *                   array will be modified by this method.
     * @throws NotEnoughSamplesException  if subset size is greater than the
     *                                    total number of samples.
     * @throws InvalidSubsetSizeException if subset size is zero or if result
     *                                    array does not have at least a length of subsetSize.
     */
    public abstract void computeRandomSubsets(final int subsetSize, final int[] result)
            throws NotEnoughSamplesException, InvalidSubsetSizeException;

    /**
     * Computes a random subset of indices within provided range of positions to
     * be used on robust estimators.
     *
     * @param minPos     minimum position to be picked. This value must be greater
     *                   or equal than zero and smaller than the total number of samples and less
     *                   than maxPos.
     * @param maxPos     maximum position to be picked. This value must be greater
     *                   or equal than zero and smaller than the total number of samples and
     *                   greater than minPos.
     * @param subsetSize subset size to be computed. This value must be smaller
     *                   than total number of samples.
     * @param pickLast   true indicates that last sample in range must always be
     *                   picked within subset. This is done to obtain faster execution times and
     *                   greater stability on some algorithms.
     * @param result     array containing indices to be picked. Provided array must
     *                   be at least of length subsetSize. The former subsetSize entries of the
     *                   array will be modified by this method.
     * @throws NotEnoughSamplesException   if subset size is greater than the
     *                                     total number of samples or if maxPos is greater than the total number of
     *                                     samples.
     * @throws InvalidSubsetSizeException  if subset size is zero or if result
     *                                     array does not have at least a length of subsetSize, or if subset size
     *                                     is greater than the allowed range of positions to be picked.
     * @throws InvalidSubsetRangeException if maximum position is smaller than
     *                                     minimum position or maximum or minimum position are negative.
     */
    public abstract void computeRandomSubsetsInRange(final int minPos, final int maxPos, final int subsetSize,
            final boolean pickLast, final int[] result) throws NotEnoughSamplesException, InvalidSubsetSizeException,
            InvalidSubsetRangeException;

    /**
     * Creates a new subset selector instance using provided total number of
     * samples and default subset selector type.
     *
     * @param numSamples number of samples to select subsets from.
     * @return a subset selector.
     * @throws IllegalArgumentException if provided number of samples is zero or
     *                                  negative.
     */
    public static SubsetSelector create(final int numSamples) {
        return new FastRandomSubsetSelector(numSamples);
    }
}