org.optaplanner.examples.common.experimental.impl.ConsecutiveIntervalInfoImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of optaplanner-examples Show documentation
Show all versions of optaplanner-examples Show documentation
OptaPlanner solves planning problems.
This lightweight, embeddable planning engine implements powerful and scalable algorithms
to optimize business resource scheduling and planning.
This module contains the examples which demonstrate how to use it in a normal Java application.
package org.optaplanner.examples.common.experimental.impl;
import java.util.Map;
import java.util.NavigableMap;
import java.util.NavigableSet;
import java.util.Objects;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.function.BiFunction;
import org.optaplanner.examples.common.experimental.api.ConsecutiveIntervalInfo;
import org.optaplanner.examples.common.experimental.api.IntervalBreak;
import org.optaplanner.examples.common.experimental.api.IntervalCluster;
public final class ConsecutiveIntervalInfoImpl, Difference_ extends Comparable>
implements ConsecutiveIntervalInfo {
private final NavigableMap, IntervalClusterImpl> clusterStartSplitPointToCluster;
private final NavigableSet> splitPointSet;
private final NavigableMap, IntervalBreakImpl> clusterStartSplitPointToNextBreak;
private final Iterable> intervalClusterIterable;
private final BiFunction differenceFunction;
private final Iterable> breaksIterable;
public ConsecutiveIntervalInfoImpl(TreeSet> splitPointSet,
BiFunction differenceFunction) {
this.clusterStartSplitPointToCluster = new TreeMap<>();
this.clusterStartSplitPointToNextBreak = new TreeMap<>();
this.intervalClusterIterable = new MapValuesIterable<>(clusterStartSplitPointToCluster);
this.breaksIterable = new MapValuesIterable<>(clusterStartSplitPointToNextBreak);
this.splitPointSet = splitPointSet;
this.differenceFunction = differenceFunction;
}
void addInterval(Interval interval) {
NavigableMap, IntervalClusterImpl> intersectedIntervalClusterMap =
clusterStartSplitPointToCluster.subMap(
Objects.requireNonNullElseGet(clusterStartSplitPointToCluster.floorKey(interval.getStartSplitPoint()),
interval::getStartSplitPoint),
true, interval.getEndSplitPoint(), true);
// Case: the interval cluster before this interval does not intersect this interval
if (!intersectedIntervalClusterMap.isEmpty()
&& intersectedIntervalClusterMap.firstEntry().getValue().getEndSplitPoint()
.isBefore(interval.getStartSplitPoint())) {
// Get the tail map after the first cluster
intersectedIntervalClusterMap = intersectedIntervalClusterMap.subMap(intersectedIntervalClusterMap.firstKey(),
false, intersectedIntervalClusterMap.lastKey(), true);
}
if (intersectedIntervalClusterMap.isEmpty()) {
// Interval does not intersect anything
// Ex:
// -----
//---- -----
createNewIntervalCluster(interval);
return;
}
// Interval intersect at least one cluster
// Ex:
// -----------------
// ------ ------ --- ----
IntervalClusterImpl firstIntersectedIntervalCluster =
intersectedIntervalClusterMap.firstEntry().getValue();
IntervalSplitPoint oldStartSplitPoint = firstIntersectedIntervalCluster.getStartSplitPoint();
firstIntersectedIntervalCluster.addInterval(interval);
// Merge all the intersected interval clusters into the first intersected
// interval cluster
intersectedIntervalClusterMap.tailMap(oldStartSplitPoint, false).values()
.forEach(firstIntersectedIntervalCluster::mergeIntervalCluster);
// Remove all the intersected interval clusters after the first intersected
// one, since they are now merged in the first
intersectedIntervalClusterMap.tailMap(oldStartSplitPoint, false).clear();
removeSpannedBreaksAndUpdateIntersectedBreaks(interval, firstIntersectedIntervalCluster);
// If the first intersected interval cluster start after the interval,
// we need to make the interval start point the key for this interval
// cluster in the map
if (oldStartSplitPoint.isAfter(firstIntersectedIntervalCluster.getStartSplitPoint())) {
clusterStartSplitPointToCluster.remove(oldStartSplitPoint);
clusterStartSplitPointToCluster.put(firstIntersectedIntervalCluster.getStartSplitPoint(),
firstIntersectedIntervalCluster);
var nextBreak = clusterStartSplitPointToNextBreak.get(firstIntersectedIntervalCluster.getStartSplitPoint());
if (nextBreak != null) {
nextBreak.setPreviousCluster(firstIntersectedIntervalCluster);
nextBreak.setLength(differenceFunction.apply(nextBreak.getPreviousIntervalClusterEnd(),
nextBreak.getNextIntervalClusterStart()));
}
}
}
private void createNewIntervalCluster(Interval interval) {
// Interval does not intersect anything
// Ex:
// -----
//---- -----
IntervalSplitPoint startSplitPoint = splitPointSet.floor(interval.getStartSplitPoint());
IntervalClusterImpl newCluster =
new IntervalClusterImpl<>(splitPointSet, differenceFunction, startSplitPoint);
clusterStartSplitPointToCluster.put(startSplitPoint, newCluster);
// If there a cluster after this interval, add a new break
// between this interval and the next cluster
Map.Entry, IntervalClusterImpl> nextClusterEntry =
clusterStartSplitPointToCluster.higherEntry(startSplitPoint);
if (nextClusterEntry != null) {
IntervalClusterImpl nextCluster = nextClusterEntry.getValue();
Difference_ difference = differenceFunction.apply(newCluster.getEnd(), nextCluster.getStart());
IntervalBreakImpl newBreak =
new IntervalBreakImpl<>(newCluster, nextCluster, difference);
clusterStartSplitPointToNextBreak.put(startSplitPoint, newBreak);
}
// If there a cluster before this interval, add a new break
// between this interval and the previous cluster
// (this will replace the old break, if there was one)
Map.Entry, IntervalClusterImpl> previousClusterEntry =
clusterStartSplitPointToCluster.lowerEntry(startSplitPoint);
if (previousClusterEntry != null) {
IntervalClusterImpl previousCluster = previousClusterEntry.getValue();
Difference_ difference = differenceFunction.apply(previousCluster.getEnd(), newCluster.getStart());
IntervalBreakImpl newBreak =
new IntervalBreakImpl<>(previousCluster, newCluster, difference);
clusterStartSplitPointToNextBreak.put(previousClusterEntry.getKey(), newBreak);
}
}
private void removeSpannedBreaksAndUpdateIntersectedBreaks(Interval interval,
IntervalClusterImpl intervalCluster) {
IntervalSplitPoint firstBreakSplitPointBeforeInterval =
Objects.requireNonNullElseGet(clusterStartSplitPointToNextBreak.floorKey(interval.getStartSplitPoint()),
interval::getStartSplitPoint);
NavigableMap, IntervalBreakImpl> intersectedIntervalBreakMap =
clusterStartSplitPointToNextBreak.subMap(firstBreakSplitPointBeforeInterval, true, interval.getEndSplitPoint(),
true);
if (intersectedIntervalBreakMap.isEmpty()) {
return;
}
IntervalClusterImpl clusterBeforeFirstIntersectedBreak =
(IntervalClusterImpl) (intersectedIntervalBreakMap.firstEntry().getValue()
.getPreviousIntervalCluster());
IntervalClusterImpl clusterAfterFinalIntersectedBreak =
(IntervalClusterImpl) (intersectedIntervalBreakMap.lastEntry().getValue()
.getNextIntervalCluster());
// All breaks that are not the first or last intersected breaks will
// be removed (as interval span them)
if (!interval.getStartSplitPoint()
.isAfter(clusterBeforeFirstIntersectedBreak.getEndSplitPoint())) {
if (!interval.getEndSplitPoint().isBefore(clusterAfterFinalIntersectedBreak.getStartSplitPoint())) {
// Case: interval spans all breaks
// Ex:
// -----------
//---- ------ -----
intersectedIntervalBreakMap.clear();
} else {
// Case: interval span first break, but does not span the final break
// Ex:
// -----------
//---- ------ -----
IntervalBreakImpl finalBreak =
intersectedIntervalBreakMap.lastEntry().getValue();
finalBreak.setPreviousCluster(intervalCluster);
finalBreak.setLength(
differenceFunction.apply(finalBreak.getPreviousIntervalClusterEnd(),
finalBreak.getNextIntervalClusterStart()));
intersectedIntervalBreakMap.clear();
clusterStartSplitPointToNextBreak.put(intervalCluster.getStartSplitPoint(), finalBreak);
}
} else if (!interval.getEndSplitPoint().isBefore(clusterAfterFinalIntersectedBreak.getStartSplitPoint())) {
// Case: interval span final break, but does not span the first break
// Ex:
// -----------
//---- ----- -----
Map.Entry, IntervalBreakImpl> previousBreakEntry =
intersectedIntervalBreakMap.firstEntry();
IntervalBreakImpl previousBreak = previousBreakEntry.getValue();
previousBreak.setNextCluster(intervalCluster);
previousBreak.setLength(
differenceFunction.apply(previousBreak.getPreviousIntervalClusterEnd(), intervalCluster.getStart()));
intersectedIntervalBreakMap.clear();
clusterStartSplitPointToNextBreak
.put(((IntervalClusterImpl) (previousBreak
.getPreviousIntervalCluster())).getStartSplitPoint(), previousBreak);
} else {
// Case: interval does not span either the first or final break
// Ex:
// ---------
//---- ------ -----
IntervalBreakImpl finalBreak =
intersectedIntervalBreakMap.lastEntry().getValue();
finalBreak.setLength(
differenceFunction.apply(finalBreak.getPreviousIntervalClusterEnd(),
finalBreak.getNextIntervalClusterStart()));
Map.Entry, IntervalBreakImpl> previousBreakEntry =
intersectedIntervalBreakMap.firstEntry();
IntervalBreakImpl previousBreak = previousBreakEntry.getValue();
previousBreak.setNextCluster(intervalCluster);
previousBreak.setLength(
differenceFunction.apply(previousBreak.getPreviousIntervalClusterEnd(), intervalCluster.getStart()));
intersectedIntervalBreakMap.clear();
clusterStartSplitPointToNextBreak.put(previousBreakEntry.getKey(), previousBreak);
clusterStartSplitPointToNextBreak.put(intervalCluster.getStartSplitPoint(), finalBreak);
}
}
void removeInterval(Interval interval) {
Map.Entry, IntervalClusterImpl> intervalClusterEntry =
clusterStartSplitPointToCluster.floorEntry(interval.getStartSplitPoint());
IntervalClusterImpl intervalCluster = intervalClusterEntry.getValue();
clusterStartSplitPointToCluster.remove(intervalClusterEntry.getKey());
Map.Entry, IntervalBreakImpl> previousBreakEntry =
clusterStartSplitPointToNextBreak.lowerEntry(intervalClusterEntry.getKey());
Map.Entry, IntervalClusterImpl> nextIntervalClusterEntry =
clusterStartSplitPointToCluster.higherEntry(intervalClusterEntry.getKey());
clusterStartSplitPointToNextBreak.remove(intervalClusterEntry.getKey());
IntervalBreakImpl previousBreak =
(previousBreakEntry != null) ? previousBreakEntry.getValue() : null;
IntervalClusterImpl previousIntervalCluster = (previousBreak != null)
? (IntervalClusterImpl) previousBreak.getPreviousIntervalCluster()
: null;
for (IntervalClusterImpl newIntervalCluster : intervalCluster
.removeInterval(interval)) {
if (previousBreak != null) {
previousBreak.setNextCluster(newIntervalCluster);
previousBreak.setLength(differenceFunction.apply(previousBreak.getPreviousIntervalCluster().getEnd(),
newIntervalCluster.getStart()));
clusterStartSplitPointToNextBreak
.put(((IntervalClusterImpl) previousBreak
.getPreviousIntervalCluster()).getStartSplitPoint(), previousBreak);
}
previousBreak = new IntervalBreakImpl<>(newIntervalCluster, null, null);
previousIntervalCluster = newIntervalCluster;
clusterStartSplitPointToCluster.put(newIntervalCluster.getStartSplitPoint(), newIntervalCluster);
}
if (nextIntervalClusterEntry != null && previousBreak != null) {
previousBreak.setNextCluster(nextIntervalClusterEntry.getValue());
previousBreak.setLength(differenceFunction.apply(previousIntervalCluster.getEnd(),
nextIntervalClusterEntry.getValue().getStart()));
clusterStartSplitPointToNextBreak.put(previousIntervalCluster.getStartSplitPoint(),
previousBreak);
} else if (previousBreakEntry != null && previousBreak == previousBreakEntry.getValue()) {
// i.e. interval was the last interval in the cluster,
// (previousBreak == previousBreakEntry.getValue()),
// and there is no interval cluster after it
// (previousBreak != null as previousBreakEntry != null,
// so it must be the case nextIntervalClusterEntry == null)
clusterStartSplitPointToNextBreak.remove(previousBreakEntry.getKey());
}
}
@Override
public Iterable> getIntervalClusters() {
return intervalClusterIterable;
}
@Override
public Iterable> getBreaks() {
return breaksIterable;
}
@Override
public String toString() {
return "Clusters {" +
"intervalClusters=" + intervalClusterIterable +
", breaks=" + breaksIterable +
'}';
}
}