Skip to content

Commit 306bb78

Browse files
committed
chore: address comments
1 parent e5790ac commit 306bb78

22 files changed

+667
-377
lines changed

core/src/main/java/ai/timefold/solver/core/impl/domain/valuerange/sort/SelectionSorterAdapter.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,24 @@
1010

1111
@NullMarked
1212
public record SelectionSorterAdapter<Solution_, T>(Solution_ solution,
13-
SelectionSorter<Solution_, T> selectionSorter) implements ValueRangeSorter<T> {
13+
SelectionSorter<Solution_, T> innerSelectionSorter) implements ValueRangeSorter<T> {
1414

1515
public static <Solution_, T> ValueRangeSorter<T> of(Solution_ solution, SelectionSorter<Solution_, T> selectionSorter) {
1616
return new SelectionSorterAdapter<>(solution, selectionSorter);
1717
}
1818

1919
@Override
2020
public List<T> sort(List<T> selectionList) {
21-
return selectionSorter.sort(solution, selectionList);
21+
return innerSelectionSorter.sort(solution, selectionList);
2222
}
2323

2424
@Override
2525
public SortedSet<T> sort(Set<T> selectionSet) {
26-
return selectionSorter.sort(solution, selectionSet);
26+
return innerSelectionSorter.sort(solution, selectionSet);
27+
}
28+
29+
@Override
30+
public SelectionSorter<?, T> getInnerSorter() {
31+
return innerSelectionSorter;
2732
}
2833
}

core/src/main/java/ai/timefold/solver/core/impl/domain/valuerange/sort/ValueRangeSorter.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import java.util.Set;
55
import java.util.SortedSet;
66

7+
import ai.timefold.solver.core.impl.heuristic.selector.common.decorator.SelectionSorter;
8+
79
import org.jspecify.annotations.NullMarked;
810

911
/**
@@ -28,4 +30,8 @@ public interface ValueRangeSorter<T> {
2830
*/
2931
SortedSet<T> sort(Set<T> selectionSet);
3032

33+
/**
34+
* @return the inner sorter class that will be used to sort the data.
35+
*/
36+
<S> SelectionSorter<S, T> getInnerSorter();
3137
}

core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/common/ReachableValues.java

Lines changed: 67 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.util.ArrayList;
44
import java.util.Collections;
5+
import java.util.HashMap;
56
import java.util.IdentityHashMap;
67
import java.util.LinkedHashMap;
78
import java.util.List;
@@ -10,6 +11,8 @@
1011

1112
import ai.timefold.solver.core.config.util.ConfigUtils;
1213
import ai.timefold.solver.core.impl.domain.valuerange.descriptor.FromEntityPropertyValueRangeDescriptor;
14+
import ai.timefold.solver.core.impl.domain.valuerange.sort.ValueRangeSorter;
15+
import ai.timefold.solver.core.impl.heuristic.selector.common.decorator.SelectionSorter;
1316

1417
import org.jspecify.annotations.NullMarked;
1518
import org.jspecify.annotations.Nullable;
@@ -21,22 +24,25 @@
2124
* @see FromEntityPropertyValueRangeDescriptor
2225
*/
2326
@NullMarked
24-
public final class ReachableValues {
27+
public final class ReachableValues<E, V> {
2528

26-
private final Map<Object, ReachableItemValue> values;
29+
private final Map<V, ReachableItemValue<E, V>> values;
2730
private final @Nullable Class<?> valueClass;
31+
private final @Nullable ValueRangeSorter<V> valueRangeSorter;
2832
private final boolean acceptsNullValue;
29-
private @Nullable ReachableItemValue firstCachedObject;
30-
private @Nullable ReachableItemValue secondCachedObject;
33+
private @Nullable ReachableItemValue<E, V> firstCachedObject;
34+
private @Nullable ReachableItemValue<E, V> secondCachedObject;
3135

32-
public ReachableValues(Map<Object, ReachableItemValue> values, Class<?> valueClass, boolean acceptsNullValue) {
36+
public ReachableValues(Map<V, ReachableItemValue<E, V>> values, @Nullable Class<?> valueClass,
37+
@Nullable ValueRangeSorter<V> valueRangeSorter, boolean acceptsNullValue) {
3338
this.values = values;
3439
this.valueClass = valueClass;
40+
this.valueRangeSorter = valueRangeSorter;
3541
this.acceptsNullValue = acceptsNullValue;
3642
}
3743

38-
private @Nullable ReachableItemValue fetchItemValue(Object value) {
39-
ReachableItemValue selected = null;
44+
private @Nullable ReachableItemValue<E, V> fetchItemValue(V value) {
45+
ReachableItemValue<E, V> selected = null;
4046
if (firstCachedObject != null && firstCachedObject.value == value) {
4147
selected = firstCachedObject;
4248
} else if (secondCachedObject != null && secondCachedObject.value == value) {
@@ -54,27 +60,28 @@ public ReachableValues(Map<Object, ReachableItemValue> values, Class<?> valueCla
5460
return selected;
5561
}
5662

57-
public List<Object> extractEntitiesAsList(Object value) {
63+
public List<E> extractEntitiesAsList(V value) {
5864
var itemValue = fetchItemValue(value);
5965
if (itemValue == null) {
6066
return Collections.emptyList();
6167
}
6268
return itemValue.randomAccessEntityList;
6369
}
6470

65-
public List<Object> extractValuesAsList(Object value) {
71+
public List<V> extractValuesAsList(V value) {
6672
var itemValue = fetchItemValue(value);
6773
if (itemValue == null) {
6874
return Collections.emptyList();
6975
}
76+
itemValue.checkSorting(valueRangeSorter);
7077
return itemValue.randomAccessValueList;
7178
}
7279

7380
public int getSize() {
7481
return values.size();
7582
}
7683

77-
public boolean isEntityReachable(@Nullable Object origin, @Nullable Object entity) {
84+
public boolean isEntityReachable(@Nullable V origin, @Nullable E entity) {
7885
if (entity == null) {
7986
return true;
8087
}
@@ -88,7 +95,7 @@ public boolean isEntityReachable(@Nullable Object origin, @Nullable Object entit
8895
return originItemValue.entityMap.containsKey(entity);
8996
}
9097

91-
public boolean isValueReachable(Object origin, @Nullable Object otherValue) {
98+
public boolean isValueReachable(V origin, @Nullable V otherValue) {
9299
var originItemValue = fetchItemValue(Objects.requireNonNull(origin));
93100
if (originItemValue == null) {
94101
return false;
@@ -103,19 +110,34 @@ public boolean acceptsNullValue() {
103110
return acceptsNullValue;
104111
}
105112

106-
public boolean matchesValueClass(Object value) {
113+
public boolean matchesValueClass(V value) {
107114
return valueClass != null && valueClass.isAssignableFrom(Objects.requireNonNull(value).getClass());
108115
}
109116

117+
public @Nullable SelectionSorter<?, V> getValueSelectionSorter() {
118+
return valueRangeSorter != null ? valueRangeSorter.getInnerSorter() : null;
119+
}
120+
121+
public ReachableValues<E, V> copy(@Nullable ValueRangeSorter<V> valueRangeSorter) {
122+
Map<V, ReachableItemValue<E, V>> newValues = ConfigUtils.isGenericTypeImmutable(valueClass)
123+
? new HashMap<>(values.size())
124+
: new IdentityHashMap<>(values.size());
125+
for (Map.Entry<V, ReachableItemValue<E, V>> entry : values.entrySet()) {
126+
newValues.put(entry.getKey(), entry.getValue().copy());
127+
}
128+
return new ReachableValues<>(newValues, valueClass, valueRangeSorter, acceptsNullValue);
129+
}
130+
110131
@NullMarked
111-
public static final class ReachableItemValue {
112-
private final Object value;
113-
private final Map<Object, Object> entityMap;
114-
private final Map<Object, Object> valueMap;
115-
private final List<Object> randomAccessEntityList;
116-
private final List<Object> randomAccessValueList;
117-
118-
public ReachableItemValue(Object value, int entityListSize, int valueListSize) {
132+
public static final class ReachableItemValue<E, V> {
133+
private final V value;
134+
private final Map<E, E> entityMap;
135+
private final Map<V, V> valueMap;
136+
private final List<E> randomAccessEntityList;
137+
private final List<V> randomAccessValueList;
138+
private boolean sorted = false;
139+
140+
public ReachableItemValue(V value, int entityListSize, int valueListSize) {
119141
this.value = value;
120142
this.entityMap = new IdentityHashMap<>(entityListSize);
121143
this.randomAccessEntityList = new ArrayList<>(entityListSize);
@@ -124,17 +146,40 @@ public ReachableItemValue(Object value, int entityListSize, int valueListSize) {
124146
this.randomAccessValueList = new ArrayList<>(valueListSize);
125147
}
126148

127-
public void addEntity(Object entity) {
149+
private ReachableItemValue(V value, Map<E, E> entityMap, Map<V, V> valueMap, List<E> randomAccessEntityList,
150+
List<V> randomAccessValueList) {
151+
this.value = value;
152+
this.entityMap = entityMap;
153+
this.valueMap = valueMap;
154+
this.randomAccessEntityList = randomAccessEntityList;
155+
this.randomAccessValueList = randomAccessValueList;
156+
}
157+
158+
public void addEntity(E entity) {
128159
if (entityMap.put(entity, entity) == null) {
129160
randomAccessEntityList.add(entity);
130161
}
131162
}
132163

133-
public void addValue(Object value) {
164+
public void addValue(V value) {
134165
if (valueMap.put(value, value) == null) {
135166
randomAccessValueList.add(value);
136167
}
137168
}
169+
170+
private void checkSorting(@Nullable ValueRangeSorter<V> valueRangeSorter) {
171+
if (valueRangeSorter != null && !sorted) {
172+
var sortedList = valueRangeSorter.sort(randomAccessValueList);
173+
randomAccessValueList.clear();
174+
randomAccessValueList.addAll(sortedList);
175+
sorted = true;
176+
}
177+
}
178+
179+
public ReachableItemValue<E, V> copy() {
180+
return new ReachableItemValue<>(value, entityMap, valueMap, new ArrayList<>(randomAccessEntityList),
181+
new ArrayList<>(randomAccessValueList));
182+
}
138183
}
139184

140185
}

core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/entity/decorator/FilteringEntityByEntitySelector.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public final class FilteringEntityByEntitySelector<Solution_> extends AbstractDe
7272
private Object replayedEntity;
7373
private BasicVariableDescriptor<Solution_>[] basicVariableDescriptors;
7474
private ValueRangeManager<Solution_> valueRangeManager;
75-
private ReachableValues reachableValues;
75+
private ReachableValues<?, ?> reachableValues;
7676
private List<Object> allEntities;
7777

7878
public FilteringEntityByEntitySelector(EntitySelector<Solution_> childEntitySelector,

core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/entity/decorator/FilteringEntityByValueSelector.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public final class FilteringEntityByValueSelector<Solution_> extends AbstractDem
6969
private final boolean randomSelection;
7070

7171
private Object replayedValue;
72-
private ReachableValues reachableValues;
72+
private ReachableValues<?, ?> reachableValues;
7373
private long entitiesSize;
7474

7575
public FilteringEntityByValueSelector(EntitySelector<Solution_> childEntitySelector,

core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/value/IterableFromSolutionPropertyValueSelector.java

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public final class IterableFromSolutionPropertyValueSelector<Solution_>
2222
implements IterableValueSelector<Solution_> {
2323

2424
private final ValueRangeDescriptor<Solution_> valueRangeDescriptor;
25-
private final SelectionSorter<Solution_, Object> sorter;
25+
private final SelectionSorter<Solution_, Object> selectionSorter;
2626
private final SelectionCacheType minimumCacheType;
2727
private final boolean randomSelection;
2828
private final boolean valueRangeMightContainEntity;
@@ -32,9 +32,9 @@ public final class IterableFromSolutionPropertyValueSelector<Solution_>
3232
private boolean cachedEntityListIsDirty = false;
3333

3434
public IterableFromSolutionPropertyValueSelector(ValueRangeDescriptor<Solution_> valueRangeDescriptor,
35-
SelectionSorter<Solution_, Object> sorter, SelectionCacheType minimumCacheType, boolean randomSelection) {
35+
SelectionSorter<Solution_, Object> selectionSorter, SelectionCacheType minimumCacheType, boolean randomSelection) {
3636
this.valueRangeDescriptor = valueRangeDescriptor;
37-
this.sorter = sorter;
37+
this.selectionSorter = selectionSorter;
3838
this.minimumCacheType = minimumCacheType;
3939
this.randomSelection = randomSelection;
4040
valueRangeMightContainEntity = valueRangeDescriptor.mightContainEntity();
@@ -51,6 +51,10 @@ public SelectionCacheType getCacheType() {
5151
return (intrinsicCacheType.compareTo(minimumCacheType) > 0) ? intrinsicCacheType : minimumCacheType;
5252
}
5353

54+
public SelectionSorter<Solution_, Object> getSelectionSorter() {
55+
return selectionSorter;
56+
}
57+
5458
// ************************************************************************
5559
// Cache lifecycle methods
5660
// ************************************************************************
@@ -60,7 +64,7 @@ public void phaseStarted(AbstractPhaseScope<Solution_> phaseScope) {
6064
super.phaseStarted(phaseScope);
6165
var scoreDirector = phaseScope.getScoreDirector();
6266
cachedValueRange = scoreDirector.getValueRangeManager().getFromSolution(valueRangeDescriptor,
63-
scoreDirector.getWorkingSolution(), sorter);
67+
scoreDirector.getWorkingSolution(), selectionSorter);
6468
if (valueRangeMightContainEntity) {
6569
cachedEntityListRevision = scoreDirector.getWorkingEntityListRevision();
6670
cachedEntityListIsDirty = false;
@@ -77,7 +81,7 @@ public void stepStarted(AbstractStepScope<Solution_> stepScope) {
7781
cachedEntityListIsDirty = true;
7882
} else {
7983
cachedValueRange = scoreDirector.getValueRangeManager().getFromSolution(valueRangeDescriptor,
80-
scoreDirector.getWorkingSolution(), sorter);
84+
scoreDirector.getWorkingSolution(), selectionSorter);
8185
cachedEntityListRevision = scoreDirector.getWorkingEntityListRevision();
8286
}
8387
}
@@ -160,12 +164,12 @@ public boolean equals(Object o) {
160164
if (!(o instanceof IterableFromSolutionPropertyValueSelector<?> that))
161165
return false;
162166
return randomSelection == that.randomSelection && Objects.equals(valueRangeDescriptor, that.valueRangeDescriptor)
163-
&& Objects.equals(sorter, that.sorter) && minimumCacheType == that.minimumCacheType;
167+
&& Objects.equals(selectionSorter, that.selectionSorter) && minimumCacheType == that.minimumCacheType;
164168
}
165169

166170
@Override
167171
public int hashCode() {
168-
return Objects.hash(valueRangeDescriptor, sorter, minimumCacheType, randomSelection);
172+
return Objects.hash(valueRangeDescriptor, selectionSorter, minimumCacheType, randomSelection);
169173
}
170174

171175
@Override

0 commit comments

Comments
 (0)