Skip to content

Commit d5f1d06

Browse files
feat: Add the concurrent usage collector (#800)
- Renamed the consecutive interval collector to the connected range collector and move it to core. - Added methods for finding maximum and minimum connected ranges. - Fixed bugs relating to duplicate ranges. - Added docs.
1 parent e7f9bb2 commit d5f1d06

File tree

53 files changed

+2284
-1717
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+2284
-1717
lines changed

core/src/main/java/ai/timefold/solver/core/api/score/stream/ConstraintCollectors.java

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.math.RoundingMode;
66
import java.time.Duration;
77
import java.time.Period;
8+
import java.time.temporal.Temporal;
89
import java.util.Collection;
910
import java.util.Comparator;
1011
import java.util.HashMap;
@@ -36,6 +37,7 @@
3637
import ai.timefold.solver.core.api.function.TriFunction;
3738
import ai.timefold.solver.core.api.function.TriPredicate;
3839
import ai.timefold.solver.core.api.score.stream.bi.BiConstraintCollector;
40+
import ai.timefold.solver.core.api.score.stream.common.ConnectedRangeChain;
3941
import ai.timefold.solver.core.api.score.stream.common.SequenceChain;
4042
import ai.timefold.solver.core.api.score.stream.quad.QuadConstraintCollector;
4143
import ai.timefold.solver.core.api.score.stream.tri.TriConstraintCollector;
@@ -1955,6 +1957,274 @@ public static <A, ResultContainer_, Result_> UniConstraintCollector<A, ResultCon
19551957
return InnerQuadConstraintCollectors.toConsecutiveSequences(resultMap, indexMap);
19561958
}
19571959

1960+
// *****************************************************************
1961+
// toConnectedRanges
1962+
// *****************************************************************
1963+
/**
1964+
* Creates a constraint collector that returns {@link ConnectedRangeChain} about the first fact.
1965+
*
1966+
* For instance, {@code [Equipment fromInclusive=2, toExclusive=4] [Equipment fromInclusive=3, toExclusive=5]
1967+
* [Equipment fromInclusive=6, toExclusive=7] [Equipment fromInclusive=7, toExclusive=8]}
1968+
* returns the following information:
1969+
*
1970+
* <pre>
1971+
* {@code
1972+
* ConnectedRanges: [minOverlap: 1, maxOverlap: 2,
1973+
* [Equipment fromInclusive=2, toExclusive=4] [Equipment fromInclusive=3, toExclusive=5]],
1974+
* [minConcurrentUsage: 1, maxConcurrentUsage: 1,
1975+
* [Equipment fromInclusive=6, toExclusive=7] [Equipment fromInclusive=7, toExclusive=8]]
1976+
* Breaks: [[Break from=5, to=6, length=1]]
1977+
* }
1978+
* </pre>
1979+
*
1980+
* This can be used to ensure a limited resource is not over-assigned.
1981+
*
1982+
* @param startInclusiveMap Maps the fact to its start
1983+
* @param endExclusiveMap Maps the fact to its end
1984+
* @param differenceFunction Computes the difference between two points. The second argument is always
1985+
* larger than the first (ex: {@link Duration#between}
1986+
* or {@code (a,b) -> b - a}).
1987+
* @param <A> type of the first mapped fact
1988+
* @param <PointType_> type of the fact endpoints
1989+
* @param <DifferenceType_> type of difference between points
1990+
* @return never null
1991+
*/
1992+
public static <A, PointType_ extends Comparable<PointType_>, DifferenceType_ extends Comparable<DifferenceType_>>
1993+
UniConstraintCollector<A, ?, ConnectedRangeChain<A, PointType_, DifferenceType_>>
1994+
toConnectedRanges(Function<A, PointType_> startInclusiveMap, Function<A, PointType_> endExclusiveMap,
1995+
BiFunction<PointType_, PointType_, DifferenceType_> differenceFunction) {
1996+
return InnerUniConstraintCollectors.toConnectedRanges(ConstantLambdaUtils.identity(), startInclusiveMap,
1997+
endExclusiveMap,
1998+
differenceFunction);
1999+
}
2000+
2001+
/**
2002+
* Specialized version of {@link #toConnectedRanges(Function,Function,BiFunction)} for
2003+
* {@link Temporal} types.
2004+
*
2005+
* @param <A> type of the first mapped fact
2006+
* @param <PointType_> temporal type of the endpoints
2007+
* @param startInclusiveMap Maps the fact to its start
2008+
* @param endExclusiveMap Maps the fact to its end
2009+
* @return never null
2010+
*/
2011+
public static <A, PointType_ extends Temporal & Comparable<PointType_>>
2012+
UniConstraintCollector<A, ?, ConnectedRangeChain<A, PointType_, Duration>>
2013+
toConnectedTemporalRanges(Function<A, PointType_> startInclusiveMap, Function<A, PointType_> endExclusiveMap) {
2014+
return toConnectedRanges(startInclusiveMap, endExclusiveMap, Duration::between);
2015+
}
2016+
2017+
/**
2018+
* Specialized version of {@link #toConnectedRanges(Function,Function,BiFunction)} for Long.
2019+
*
2020+
* @param startInclusiveMap Maps the fact to its start
2021+
* @param endExclusiveMap Maps the fact to its end
2022+
* @param <A> type of the first mapped fact
2023+
* @return never null
2024+
*/
2025+
public static <A> UniConstraintCollector<A, ?, ConnectedRangeChain<A, Long, Long>>
2026+
toConnectedRanges(ToLongFunction<A> startInclusiveMap, ToLongFunction<A> endExclusiveMap) {
2027+
return toConnectedRanges(startInclusiveMap::applyAsLong, endExclusiveMap::applyAsLong, (a, b) -> b - a);
2028+
}
2029+
2030+
/**
2031+
* As defined by {@link #toConnectedRanges(Function,Function,BiFunction)}.
2032+
*
2033+
* @param intervalMap Maps both facts to an item in the cluster
2034+
* @param startInclusiveMap Maps the item to its start
2035+
* @param endExclusiveMap Maps the item to its end
2036+
* @param differenceFunction Computes the difference between two points. The second argument is always
2037+
* larger than the first (ex: {@link Duration#between}
2038+
* or {@code (a,b) -> b - a}).
2039+
* @param <A> type of the first mapped fact
2040+
* @param <B> type of the second mapped fact
2041+
* @param <IntervalType_> type of the item in the cluster
2042+
* @param <PointType_> type of the item endpoints
2043+
* @param <DifferenceType_> type of difference between points
2044+
* @return never null
2045+
*/
2046+
public static <A, B, IntervalType_, PointType_ extends Comparable<PointType_>, DifferenceType_ extends Comparable<DifferenceType_>>
2047+
BiConstraintCollector<A, B, ?, ConnectedRangeChain<IntervalType_, PointType_, DifferenceType_>>
2048+
toConnectedRanges(BiFunction<A, B, IntervalType_> intervalMap,
2049+
Function<IntervalType_, PointType_> startInclusiveMap,
2050+
Function<IntervalType_, PointType_> endExclusiveMap,
2051+
BiFunction<PointType_, PointType_, DifferenceType_> differenceFunction) {
2052+
return InnerBiConstraintCollectors.toConnectedRanges(intervalMap, startInclusiveMap, endExclusiveMap,
2053+
differenceFunction);
2054+
}
2055+
2056+
/**
2057+
* As defined by {@link #toConnectedTemporalRanges(Function,Function)}.
2058+
*
2059+
* @param intervalMap Maps the three facts to an item in the cluster
2060+
* @param startInclusiveMap Maps the fact to its start
2061+
* @param endExclusiveMap Maps the fact to its end
2062+
* @param <A> type of the first mapped fact
2063+
* @param <B> type of the second mapped fact
2064+
* @param <IntervalType_> type of the item in the cluster
2065+
* @param <PointType_> temporal type of the endpoints
2066+
* @return never null
2067+
*/
2068+
public static <A, B, IntervalType_, PointType_ extends Temporal & Comparable<PointType_>>
2069+
BiConstraintCollector<A, B, ?, ConnectedRangeChain<IntervalType_, PointType_, Duration>>
2070+
toConnectedTemporalRanges(BiFunction<A, B, IntervalType_> intervalMap,
2071+
Function<IntervalType_, PointType_> startInclusiveMap,
2072+
Function<IntervalType_, PointType_> endExclusiveMap) {
2073+
return toConnectedRanges(intervalMap, startInclusiveMap, endExclusiveMap, Duration::between);
2074+
}
2075+
2076+
/**
2077+
* As defined by {@link #toConnectedRanges(ToLongFunction, ToLongFunction)}.
2078+
*
2079+
* @param startInclusiveMap Maps the fact to its start
2080+
* @param endExclusiveMap Maps the fact to its end
2081+
* @param <A> type of the first mapped fact
2082+
* @param <B> type of the second mapped fact
2083+
* @param <IntervalType_> type of the item in the cluster
2084+
* @return never null
2085+
*/
2086+
public static <A, B, IntervalType_>
2087+
BiConstraintCollector<A, B, ?, ConnectedRangeChain<IntervalType_, Long, Long>>
2088+
toConnectedRanges(BiFunction<A, B, IntervalType_> intervalMap, ToLongFunction<IntervalType_> startInclusiveMap,
2089+
ToLongFunction<IntervalType_> endExclusiveMap) {
2090+
return toConnectedRanges(intervalMap, startInclusiveMap::applyAsLong, endExclusiveMap::applyAsLong, (a, b) -> b - a);
2091+
}
2092+
2093+
/**
2094+
* As defined by {@link #toConnectedRanges(Function,Function,BiFunction)}.
2095+
*
2096+
* @param intervalMap Maps the three facts to an item in the cluster
2097+
* @param startInclusiveMap Maps the item to its start
2098+
* @param endExclusiveMap Maps the item to its end
2099+
* @param differenceFunction Computes the difference between two points. The second argument is always
2100+
* larger than the first (ex: {@link Duration#between}
2101+
* or {@code (a,b) -> b - a}).
2102+
* @param <A> type of the first mapped fact
2103+
* @param <B> type of the second mapped fact
2104+
* @param <C> type of the third mapped fact
2105+
* @param <IntervalType_> type of the item in the cluster
2106+
* @param <PointType_> type of the item endpoints
2107+
* @param <DifferenceType_> type of difference between points
2108+
* @return never null
2109+
*/
2110+
public static <A, B, C, IntervalType_, PointType_ extends Comparable<PointType_>, DifferenceType_ extends Comparable<DifferenceType_>>
2111+
TriConstraintCollector<A, B, C, ?, ConnectedRangeChain<IntervalType_, PointType_, DifferenceType_>>
2112+
toConnectedRanges(TriFunction<A, B, C, IntervalType_> intervalMap,
2113+
Function<IntervalType_, PointType_> startInclusiveMap,
2114+
Function<IntervalType_, PointType_> endExclusiveMap,
2115+
BiFunction<PointType_, PointType_, DifferenceType_> differenceFunction) {
2116+
return InnerTriConstraintCollectors.toConnectedRanges(intervalMap, startInclusiveMap, endExclusiveMap,
2117+
differenceFunction);
2118+
}
2119+
2120+
/**
2121+
* As defined by {@link #toConnectedTemporalRanges(Function,Function)}.
2122+
*
2123+
* @param intervalMap Maps the three facts to an item in the cluster
2124+
* @param startInclusiveMap Maps the fact to its start
2125+
* @param endExclusiveMap Maps the fact to its end
2126+
* @param <A> type of the first mapped fact
2127+
* @param <B> type of the second mapped fact
2128+
* @param <C> type of the third mapped fact
2129+
* @param <IntervalType_> type of the item in the cluster
2130+
* @param <PointType_> temporal type of the endpoints
2131+
* @return never null
2132+
*/
2133+
public static <A, B, C, IntervalType_, PointType_ extends Temporal & Comparable<PointType_>>
2134+
TriConstraintCollector<A, B, C, ?, ConnectedRangeChain<IntervalType_, PointType_, Duration>>
2135+
toConnectedTemporalRanges(TriFunction<A, B, C, IntervalType_> intervalMap,
2136+
Function<IntervalType_, PointType_> startInclusiveMap,
2137+
Function<IntervalType_, PointType_> endExclusiveMap) {
2138+
return toConnectedRanges(intervalMap, startInclusiveMap, endExclusiveMap, Duration::between);
2139+
}
2140+
2141+
/**
2142+
* As defined by {@link #toConnectedRanges(ToLongFunction, ToLongFunction)}.
2143+
*
2144+
* @param startInclusiveMap Maps the fact to its start
2145+
* @param endExclusiveMap Maps the fact to its end
2146+
* @param <A> type of the first mapped fact
2147+
* @param <B> type of the second mapped fact
2148+
* @param <C> type of the third mapped fact
2149+
* @param <IntervalType_> type of the item in the cluster
2150+
* @return never null
2151+
*/
2152+
public static <A, B, C, IntervalType_>
2153+
TriConstraintCollector<A, B, C, ?, ConnectedRangeChain<IntervalType_, Long, Long>>
2154+
toConnectedRanges(TriFunction<A, B, C, IntervalType_> intervalMap, ToLongFunction<IntervalType_> startInclusiveMap,
2155+
ToLongFunction<IntervalType_> endExclusiveMap) {
2156+
return toConnectedRanges(intervalMap, startInclusiveMap::applyAsLong, endExclusiveMap::applyAsLong, (a, b) -> b - a);
2157+
}
2158+
2159+
/**
2160+
* As defined by {@link #toConnectedRanges(Function,Function,BiFunction)}.
2161+
*
2162+
* @param intervalMap Maps the four facts to an item in the cluster
2163+
* @param startInclusiveMap Maps the item to its start
2164+
* @param endExclusiveMap Maps the item to its end
2165+
* @param differenceFunction Computes the difference between two points. The second argument is always
2166+
* larger than the first (ex: {@link Duration#between}
2167+
* or {@code (a,b) -> b - a}).
2168+
* @param <A> type of the first mapped fact
2169+
* @param <B> type of the second mapped fact
2170+
* @param <C> type of the third mapped fact
2171+
* @param <D> type of the fourth mapped fact
2172+
* @param <IntervalType_> type of the item in the cluster
2173+
* @param <PointType_> type of the item endpoints
2174+
* @param <DifferenceType_> type of difference between points
2175+
* @return never null
2176+
*/
2177+
public static <A, B, C, D, IntervalType_, PointType_ extends Comparable<PointType_>, DifferenceType_ extends Comparable<DifferenceType_>>
2178+
QuadConstraintCollector<A, B, C, D, ?, ConnectedRangeChain<IntervalType_, PointType_, DifferenceType_>>
2179+
toConnectedRanges(QuadFunction<A, B, C, D, IntervalType_> intervalMap,
2180+
Function<IntervalType_, PointType_> startInclusiveMap, Function<IntervalType_, PointType_> endExclusiveMap,
2181+
BiFunction<PointType_, PointType_, DifferenceType_> differenceFunction) {
2182+
return InnerQuadConstraintCollectors.toConnectedRanges(intervalMap, startInclusiveMap, endExclusiveMap,
2183+
differenceFunction);
2184+
}
2185+
2186+
/**
2187+
* As defined by {@link #toConnectedTemporalRanges(Function,Function)}.
2188+
*
2189+
* @param intervalMap Maps the three facts to an item in the cluster
2190+
* @param startInclusiveMap Maps the fact to its start
2191+
* @param endExclusiveMap Maps the fact to its end
2192+
* @param <A> type of the first mapped fact
2193+
* @param <B> type of the second mapped fact
2194+
* @param <C> type of the third mapped fact
2195+
* @param <D> type of the fourth mapped fact
2196+
* @param <IntervalType_> type of the item in the cluster
2197+
* @param <PointType_> temporal type of the endpoints
2198+
* @return never null
2199+
*/
2200+
public static <A, B, C, D, IntervalType_, PointType_ extends Temporal & Comparable<PointType_>>
2201+
QuadConstraintCollector<A, B, C, D, ?, ConnectedRangeChain<IntervalType_, PointType_, Duration>>
2202+
toConnectedTemporalRanges(QuadFunction<A, B, C, D, IntervalType_> intervalMap,
2203+
Function<IntervalType_, PointType_> startInclusiveMap,
2204+
Function<IntervalType_, PointType_> endExclusiveMap) {
2205+
return toConnectedRanges(intervalMap, startInclusiveMap, endExclusiveMap, Duration::between);
2206+
}
2207+
2208+
/**
2209+
* As defined by {@link #toConnectedRanges(ToLongFunction, ToLongFunction)}.
2210+
*
2211+
* @param startInclusiveMap Maps the fact to its start
2212+
* @param endExclusiveMap Maps the fact to its end
2213+
* @param <A> type of the first mapped fact
2214+
* @param <B> type of the second mapped fact
2215+
* @param <C> type of the third mapped fact
2216+
* @param <D> type of the fourth mapped fact
2217+
* @param <IntervalType_> type of the item in the cluster
2218+
* @return never null
2219+
*/
2220+
public static <A, B, C, D, IntervalType_>
2221+
QuadConstraintCollector<A, B, C, D, ?, ConnectedRangeChain<IntervalType_, Long, Long>>
2222+
toConnectedRanges(QuadFunction<A, B, C, D, IntervalType_> intervalMap,
2223+
ToLongFunction<IntervalType_> startInclusiveMap,
2224+
ToLongFunction<IntervalType_> endExclusiveMap) {
2225+
return toConnectedRanges(intervalMap, startInclusiveMap::applyAsLong, endExclusiveMap::applyAsLong, (a, b) -> b - a);
2226+
}
2227+
19582228
private ConstraintCollectors() {
19592229
}
19602230
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package ai.timefold.solver.core.api.score.stream.common;
2+
3+
/**
4+
* Represents a collection of ranges that are connected, meaning
5+
* the union of all the ranges results in the range
6+
* [{@link #getStart()}, {@link #getEnd()}) without gaps.
7+
*
8+
* @param <Range_> The type of range in the collection.
9+
* @param <Point_> The type of the start and end points for each range.
10+
* @param <Difference_> The type of difference between start and end points.
11+
*/
12+
public interface ConnectedRange<Range_, Point_ extends Comparable<Point_>, Difference_ extends Comparable<Difference_>>
13+
extends Iterable<Range_> {
14+
/**
15+
* Get the number of ranges contained by this {@link ConnectedRange}.
16+
*
17+
* @return never null, the number of ranges contained by this {@link ConnectedRange}.
18+
*/
19+
int getContainedRangeCount();
20+
21+
/**
22+
* True if this {@link ConnectedRange} has at least one pair of
23+
* ranges that overlaps each other, false otherwise.
24+
*
25+
* @return never null, true iff there at least one pair of overlapping ranges in this {@link ConnectedRange}.
26+
*/
27+
boolean hasOverlap();
28+
29+
/**
30+
* Get the minimum number of overlapping ranges for any point contained by
31+
* this {@link ConnectedRange}.
32+
*
33+
* @return never null, the minimum number of overlapping ranges for any point
34+
* in this {@link ConnectedRange}.
35+
*/
36+
int getMinimumOverlap();
37+
38+
/**
39+
* Get the maximum number of overlapping ranges for any point contained by
40+
* this {@link ConnectedRange}.
41+
*
42+
* @return never null, the maximum number of overlapping ranges for any point
43+
* in this {@link ConnectedRange}.
44+
*/
45+
int getMaximumOverlap();
46+
47+
/**
48+
* Get the length of this {@link ConnectedRange}.
49+
*
50+
* @return The difference between {@link #getEnd()} and {@link #getStart()}.
51+
*/
52+
Difference_ getLength();
53+
54+
/**
55+
* Gets the first start point represented by this {@link ConnectedRange}.
56+
*
57+
* @return never null, the first start point represented by this {@link ConnectedRange}.
58+
*/
59+
Point_ getStart();
60+
61+
/**
62+
* Gets the last end point represented by this {@link ConnectedRange}.
63+
*
64+
* @return never null, the last end point represented by this {@link ConnectedRange}.
65+
*/
66+
Point_ getEnd();
67+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package ai.timefold.solver.core.api.score.stream.common;
2+
3+
/**
4+
* Contains info regarding {@link ConnectedRange}s and {@link RangeGap}s for a collection of ranges.
5+
*
6+
* @param <Range_> The type of range in the collection.
7+
* @param <Point_> The type of the start and end points for each range.
8+
* @param <Difference_> The type of difference between start and end points.
9+
*/
10+
public interface ConnectedRangeChain<Range_, Point_ extends Comparable<Point_>, Difference_ extends Comparable<Difference_>> {
11+
12+
/**
13+
* @return never null, an iterable that iterates through the {@link ConnectedRange}s
14+
* contained in the collection in ascending order of their start points
15+
*/
16+
Iterable<ConnectedRange<Range_, Point_, Difference_>> getConnectedRanges();
17+
18+
/**
19+
* @return never null, an iterable that iterates through the {@link RangeGap}s contained in
20+
* the collection in ascending order of their start points
21+
*/
22+
Iterable<RangeGap<Point_, Difference_>> getGaps();
23+
}

0 commit comments

Comments
 (0)