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

com.github.rinde.rinsim.scenario.generator.TimeSeries Maven / Gradle / Ivy

There is a newer version: 4.4.6
Show newest version
/*
 * Copyright (C) 2011-2016 Rinde van Lon, iMinds-DistriNet, KU Leuven
 *
 * 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.github.rinde.rinsim.scenario.generator;

import static com.google.common.base.Preconditions.checkArgument;

import java.util.Iterator;
import java.util.List;

import javax.annotation.Nullable;

import org.apache.commons.math3.distribution.ExponentialDistribution;
import org.apache.commons.math3.distribution.NormalDistribution;
import org.apache.commons.math3.distribution.RealDistribution;
import org.apache.commons.math3.distribution.UniformRealDistribution;
import org.apache.commons.math3.random.MersenneTwister;
import org.apache.commons.math3.random.RandomGenerator;

import com.github.rinde.rinsim.scenario.generator.IntensityFunctions.IntensityFunction;
import com.github.rinde.rinsim.util.StochasticSupplier;
import com.github.rinde.rinsim.util.StochasticSuppliers;
import com.google.common.base.Predicate;
import com.google.common.collect.AbstractSequentialIterator;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterators;
import com.google.common.collect.Range;

/**
 * Utilities for generating time series.
 * @author Rinde van Lon
 */
public final class TimeSeries {
  private TimeSeries() {}

  /**
   * Creates a homogenous Poisson process of the specified length. The intensity
   * is calculated as numEvents / length.
   * @param length The length of Poisson process, all generated times will be in
   *          the interval [0,length).
   * @param numEvents The number of events that will be generated (on average).
   * @return A newly constructed Poisson process {@link TimeSeriesGenerator}.
   */
  public static TimeSeriesGenerator homogenousPoisson(double length,
      int numEvents) {
    checkArgument(length > 0d);
    checkArgument(numEvents > 0);
    return new PoissonProcess(length, numEvents / length);
  }

  /**
   * Creates a non-homogenous Poisson process of the specified length. The
   * intensity is specified by the {@link IntensityFunction}. The non-homogenous
   * Poisson process is implemented using the thinning method as described in
   * [1].
   * 

* References *

    *
  1. Lewis, P.A.W. and Shedler, G.S. Simulation of nonhomogenous Poisson * processes by thinning. Naval Research Logistic Quarterly 26, (1979), * 403–414.
  2. *
* @param length The length of Poisson process, all generated times will be in * the interval [0,length). * @param function The intensity function. * @return A newly constructed non-homogenous Poisson process * {@link TimeSeriesGenerator}. */ public static TimeSeriesGenerator nonHomogenousPoisson(double length, IntensityFunction function) { checkArgument(length > 0d); checkArgument(function.getMax() > 0d); return new NonHomogenous(length, function); } /** * Creates a non-homogenous Poisson process of the specified length. The * intensity is specified by the {@link StochasticSupplier}. Each time * {@link TimeSeriesGenerator#generate(long)} is called, a new * {@link IntensityFunction} is requested from the {@link StochasticSupplier}. * The non-homogenous Poisson process is implemented using the thinning method * as described in [1]. *

* References *

    *
  1. Lewis, P.A.W. and Shedler, G.S. Simulation of nonhomogenous Poisson * processes by thinning. Naval Research Logistic Quarterly 26, (1979), * 403–414.
  2. *
* @param length The length of Poisson process, all generated times will be in * the interval [0,length). * @param functionSupplier The intensity function supplier. * @return A newly constructed non-homogenous Poisson process * {@link TimeSeriesGenerator}. */ public static TimeSeriesGenerator nonHomogenousPoisson(double length, StochasticSupplier functionSupplier) { checkArgument(length > 0d); return new SuppliedNonHomogenous(length, functionSupplier); } /** * Creates a {@link TimeSeriesGenerator} using a uniform distribution for the * inter arrival times of events. * @param length The length of the time series, all generated times will be in * the interval [0,length). * @param numEvents The total number of events in the time series (on * average). * @param maxDeviation The maximum deviation from the mean for the uniform * distribution. * @return A {@link TimeSeriesGenerator} based on a uniform distribution. */ public static TimeSeriesGenerator uniform(double length, int numEvents, double maxDeviation) { checkArgument(length > 0d); checkArgument(numEvents > 0); checkArgument(maxDeviation > 0d); final double average = length / numEvents; return new UniformTimeSeries(length, average, StochasticSuppliers.constant(maxDeviation)); } /** * Creates a {@link TimeSeriesGenerator} using a uniform distribution for the * inter arrival times of events. The spread of the uniform distribution is * defined by the maxDeviation {@link StochasticSupplier}, this * means that each time a time series is generated a different max deviation * settings is used. * @param length The length of the time series, all generated times will be in * the interval [0,length). * @param numEvents The total number of events in the time series (on * average). * @param maxDeviation A supplier that is used for max deviation values. * @return A {@link TimeSeriesGenerator} based on a uniform distribution, each * time series that is generated has a different max deviation drawn * from the supplier. */ public static TimeSeriesGenerator uniform(double length, int numEvents, StochasticSupplier maxDeviation) { checkArgument(length > 0d); checkArgument(numEvents > 0); final double average = length / numEvents; return new UniformTimeSeries(length, average, StochasticSuppliers.checked( maxDeviation, Range.atLeast(0d))); } /** * Creates a {@link TimeSeriesGenerator} that uses a truncated normal * distribution for the inter arrival times of events. The normal distribution * is truncated at a lower bound of 0 since it is not allowed (it * makes no sense) to have negative inter arrival times. For more information * about the normal distribution see {@link StochasticSuppliers#normal()}. * @param length The length of the time series, all generated times will be in * the interval [0,length). * @param numEvents The total number of events in the time series (on * average). * @param sd The standard deviation of the normal distribution. * @return A {@link TimeSeriesGenerator} based on a normal distribution. */ public static TimeSeriesGenerator normal(double length, int numEvents, double sd) { checkArgument(length > 0d); checkArgument(numEvents > 0); final double average = length / numEvents; return toTimeSeries(length, StochasticSuppliers.normal() .mean(average) .std(sd) .lowerBound(0d) .redrawWhenOutOfBounds() .scaleMean() .buildDouble()); } /** * Converts a {@link StochasticSupplier} of {@link Double}s into a * {@link TimeSeriesGenerator}. Each time in the time series is created by * t[n] = t[n-1] + ss.get(..), here ss is the * {@link Double} supplier. * @param length The length of the time series, all generated times will be in * the interval [0,length). * @param interArrivalTimesSupplier The supplier to use for computing event * inter arrival times. * @return A new {@link TimeSeriesGenerator} based on the supplier. */ public static TimeSeriesGenerator toTimeSeries(double length, StochasticSupplier interArrivalTimesSupplier) { return new SupplierTimeSeries(length, interArrivalTimesSupplier); } /** * Decorates the specified {@link TimeSeriesGenerator} such that it only * generates time series which conform to the specified {@link Predicate}. * Predicates can be combined by using the methods provided by * {@link com.google.common.base.Predicates}. Note that when an impossible * {@link Predicate} is specified, such as * {@link com.google.common.base.Predicates#alwaysFalse()} the resulting * {@link TimeSeriesGenerator} will enter an infinite loop. * @param tsg The {@link TimeSeriesGenerator} to filter. * @param predicate All returned {@link TimeSeriesGenerator}s will conform to * this predicate. * @return A filtered generator. */ public static TimeSeriesGenerator filter(TimeSeriesGenerator tsg, Predicate> predicate) { return new FilteredTSG(tsg, predicate); } /** * Creates a {@link Predicate} for a specified number of events. This * predicate only accepts time series with exactly num events. * @param num The number of events a time series should have. * @return A newly created predicate. */ public static Predicate> numEventsPredicate(final int num) { return new Predicate>() { @Override public boolean apply(@Nullable List input) { assert input != null; return input.size() == num; } }; } /** * Generator of a time series. * @author Rinde van Lon */ public interface TimeSeriesGenerator { /** * Should generate a time series. * @param seed The random seed to use. * @return An immutable list of times in ascending order, may contain * duplicates. */ ImmutableList generate(long seed); } static class FilteredTSG implements TimeSeriesGenerator { private final TimeSeriesGenerator delegate; private final Predicate> predicate; private final RandomGenerator rng; FilteredTSG(TimeSeriesGenerator tsg, Predicate> pred) { delegate = tsg; predicate = pred; rng = new MersenneTwister(); } @Override public ImmutableList generate(long seed) { rng.setSeed(seed); while (true) { final ImmutableList timeSeries = delegate.generate(rng .nextLong()); if (predicate.apply(timeSeries)) { return timeSeries; } } } } static class PoissonProcess implements TimeSeriesGenerator { /** * Random generator used for drawing random numbers. */ protected final RandomGenerator rng; final double length; final double intensity; PoissonProcess(double len, double intens) { length = len; intensity = intens; rng = new MersenneTwister(); } /** * All times will be in the interval [0,length). * @return The upper bound of the interval. */ public double getLength() { return length; } // internal use only! Iterator iterator() { return new TimeSeriesIterator(new ExponentialDistribution(rng, 1d / intensity, ExponentialDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY), length); } @Override public ImmutableList generate(long seed) { rng.setSeed(seed); return ImmutableList.copyOf(iterator()); } } static class NonHomogenous extends PoissonProcess { final IntensityFunction lambd; NonHomogenous(double l, IntensityFunction func) { super(l, func.getMax()); lambd = func; } @Override public Iterator iterator() { return Iterators.filter(super.iterator(), new NHPredicate(rng, lambd)); } } static class SuppliedNonHomogenous implements TimeSeriesGenerator { final double length; final StochasticSupplier lambdSup; final RandomGenerator rng; SuppliedNonHomogenous(double l, StochasticSupplier funcSup) { length = l; lambdSup = funcSup; rng = new MersenneTwister(); } @Override public ImmutableList generate(long seed) { rng.setSeed(seed); final TimeSeriesGenerator tsg = new NonHomogenous(length, lambdSup.get(rng.nextLong())); return tsg.generate(rng.nextLong()); } } static class SupplierTimeSeries implements TimeSeriesGenerator { private final double length; private final StochasticSupplier supplier; SupplierTimeSeries(double len, StochasticSupplier sup) { length = len; supplier = sup; } @Override public ImmutableList generate(long seed) { return ImmutableList.copyOf(new SupplierIterator(length, supplier, new MersenneTwister(seed))); } } static class SupplierIterator extends AbstractSequentialIterator { private final double length; private final StochasticSupplier supplier; private final RandomGenerator randomNumberGenerator; SupplierIterator(double len, StochasticSupplier sup, RandomGenerator rng) { super(next(0d, len, sup, rng)); length = len; supplier = sup; randomNumberGenerator = rng; } @SuppressWarnings("null") @Nullable @Override protected Double computeNext(Double previous) { return next(previous, length, supplier, randomNumberGenerator); } @Nullable static Double next(Double prev, double len, StochasticSupplier supplier, RandomGenerator rng) { final double nextVal = prev + getValue(supplier, rng); if (nextVal < len) { return nextVal; } return null; } static double getValue(StochasticSupplier ed, RandomGenerator rng) { final double sample = ed.get(rng.nextLong()); checkArgument( sample >= 0d, "A StochasticSupplier used in a TimeSeries may not return negative " + "values, was: %s.", sample); return sample; } } static class UniformTimeSeries implements TimeSeriesGenerator { static final double SMALLEST_DEVIATION = .0000001; private final RandomGenerator rng; private final double length; private final double average; private final StochasticSupplier deviationSupplier; UniformTimeSeries(double len, double avg, StochasticSupplier dev) { rng = new MersenneTwister(); length = len; average = avg; deviationSupplier = dev; } @Override public ImmutableList generate(long seed) { rng.setSeed(seed); double deviation = deviationSupplier.get(rng.nextLong()); deviation = Math.min(average, deviation); final double lowerBound = average - deviation; final double upperBound = average + deviation; if (deviation < SMALLEST_DEVIATION) { return ImmutableList.copyOf(new FixedTimeSeriesIterator(rng, length, average)); } return ImmutableList.copyOf(new TimeSeriesIterator( new UniformRealDistribution(rng, lowerBound, upperBound), length)); } } static class NormalTimeSeries implements TimeSeriesGenerator { private final double length; private final RealDistribution distribution; NormalTimeSeries(double len, double avg, double sd) { length = len; distribution = new NormalDistribution(avg, sd); } @Override public ImmutableList generate(long seed) { distribution.reseedRandomGenerator(seed); return ImmutableList.copyOf(new TimeSeriesIterator( distribution, length)); } } static class FixedTimeSeriesIterator extends AbstractSequentialIterator { private final double length; private final double average; protected FixedTimeSeriesIterator(RandomGenerator rng, double len, double avg) { super(rng.nextDouble() * avg); length = len; average = avg; } @SuppressWarnings("null") @Nullable @Override protected Double computeNext(Double prev) { final double nextVal = prev + average; if (nextVal < length) { return nextVal; } return null; } } static class TimeSeriesIterator extends AbstractSequentialIterator { private final RealDistribution ed; private final double length; TimeSeriesIterator(RealDistribution distr, double len) { super(next(0d, len, distr)); length = len; ed = distr; } @SuppressWarnings("null") @Nullable @Override protected Double computeNext(Double previous) { return next(previous, length, ed); } @Nullable static Double next(Double prev, double len, RealDistribution ed) { final double nextVal = prev + getFirstPositive(ed); if (nextVal < len) { return nextVal; } return null; } static double getFirstPositive(RealDistribution ed) { double sample = ed.sample(); while (sample < 0) { sample = ed.sample(); } return sample; } } static class NHPredicate implements Predicate { private final RandomGenerator rng; private final IntensityFunction lambda; private final double lambdaMax; NHPredicate(RandomGenerator r, IntensityFunction l) { rng = r; lambda = l; lambdaMax = lambda.getMax(); } @Override public boolean apply(@Nullable Double input) { assert input != null; return rng.nextDouble() <= lambda.apply(input) / lambdaMax; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy