rinde.sim.pdptw.generator.times.PoissonProcessArrivalTimes Maven / Gradle / Ivy
The newest version!
package rinde.sim.pdptw.generator.times;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Lists.newArrayList;
import static java.util.Collections.nCopies;
import java.math.RoundingMode;
import java.util.Collections;
import java.util.List;
import org.apache.commons.math3.distribution.ExponentialDistribution;
import org.apache.commons.math3.random.RandomAdaptor;
import org.apache.commons.math3.random.RandomGenerator;
import com.google.common.collect.ImmutableList;
import com.google.common.math.DoubleMath;
/**
* The arrival times generated by this generator are the result of a Poisson
* process, i.e. they have exponentially distributed inter-arrival times.
* However, due to discretization and a strict control on the dynamism
* properties, the resulting Poisson distribution has a slightly lower mean.
*
* @author Rinde van Lon
*/
public class PoissonProcessArrivalTimes implements ArrivalTimesGenerator {
private final long length;
private final double gai;
private final double opa;
// scenario length in minutes
// intensity in announcements per hour
public PoissonProcessArrivalTimes(long scenarioLength,
double globalAnnouncementIntensity, double ordersPerAnnouncement) {
length = scenarioLength;
gai = globalAnnouncementIntensity;
opa = ordersPerAnnouncement;
// System.out.println("gai " + gai + " opa " + opa);
}
// deprecated: dynamism stuff is no longer valid
@Override
@Deprecated
public ImmutableList generate(RandomGenerator rng) {
// we model the announcements as a Poisson process, which means that
// the interarrival times are exponentially distributed.
final ExponentialDistribution ed = new ExponentialDistribution(1d / gai);
ed.reseedRandomGenerator(rng.nextLong());
double sum = 0;
final List arrivalTimes = newArrayList();
while (sum < length) {
// final long nt = DoubleMath
// .roundToLong(ed.sample() * 60d, RoundingMode.HALF_DOWN);
// ignore values which are smaller than the time unit (one
// minute), unless its the first value.
// if (/*nt > 0 ||*/ arrivalTimes.isEmpty()) {
sum += ed.sample() * 60d;
if (sum < length) {
arrivalTimes.add(sum);
} else if (arrivalTimes.isEmpty()) {
// there is a small probability where the first
// generated arrival time is greater than length. This
// case is undesirable, when it happens, we just try
// again by resetting sum.
sum = 0;
} else {
break;
}
// }
// else {
// // System.out.println("reject");
// }
}
// now we know the real number of announcements.
if (DoubleMath.isMathematicalInteger(opa)) {
// if ordersPerAnnouncement is an integer, we can just use a
// double for loop for setting the arrival times.
final ImmutableList.Builder lb = ImmutableList.builder();
for (final double arrivalTime : arrivalTimes) {
for (int i = 0; i < opa; i++) {
lb.add(arrivalTime);
}
}
return lb.build();
}
// If it is not an integer, we need to use a randomized approach to
// create a fair allocation of orders per announcement.
// For example: if the opa value is 1.2, there will be announcements
// with either 1 or 2 orders:
// - 1 order [80%]
// - 2 orders [20%]
// The following code makes sure that this ratio is maintained as
// far as possible.
final int floor = DoubleMath.roundToInt(opa, RoundingMode.FLOOR);
final int ceiling = DoubleMath.roundToInt(opa, RoundingMode.CEILING);
final double ratio = opa - floor;
final int floorTimes = DoubleMath.roundToInt((1 - ratio)
* arrivalTimes.size(), RoundingMode.HALF_DOWN);
final int ceilTimes = DoubleMath
.roundToInt(ratio * arrivalTimes.size(), RoundingMode.HALF_UP);
checkState(floorTimes + ceilTimes == arrivalTimes.size());
final List orderCountList = newArrayList();
orderCountList.addAll(nCopies(floorTimes, floor));
orderCountList.addAll(nCopies(ceilTimes, ceiling));
Collections.shuffle(orderCountList, new RandomAdaptor(rng));
final ImmutableList.Builder lb = ImmutableList.builder();
for (int i = 0; i < arrivalTimes.size(); i++) {
for (int j = 0; j < orderCountList.get(i); j++) {
lb.add(arrivalTimes.get(i));
}
}
return lb.build();
}
public long getScenarioLength() {
return length;
}
public double getOrdersPerAnnouncement() {
return opa;
}
public double getGlobalAnnouncementIntensity() {
return gai;
}
}