Skip to content

Commit 0130996

Browse files
committed
chore: minor Nearby fix
1 parent 51d1163 commit 0130996

File tree

3 files changed

+38
-10
lines changed

3 files changed

+38
-10
lines changed

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,12 +107,16 @@ public EntitySelector<Solution_> buildEntitySelector(HeuristicConfigPolicy<Solut
107107
var entitySelector = buildBaseEntitySelector(entityDescriptor, baseSelectionCacheType,
108108
baseRandomSelection);
109109
var instanceCache = configPolicy.getClassInstanceCache();
110-
entitySelector = applyEntityValueRangeFiltering(configPolicy, entitySelector, entityValueRangeRecorderId,
111-
minimumCacheType, inheritedSelectionOrder, baseRandomSelection);
112110
if (nearbySelectionConfig != null) {
113111
// TODO Static filtering (such as movableEntitySelectionFilter) should affect nearbySelection
114112
entitySelector = applyNearbySelection(configPolicy, nearbySelectionConfig, minimumCacheType,
115113
resolvedSelectionOrder, entitySelector);
114+
} else {
115+
// The nearby selector will implement its own logic to filter out unreachable elements.
116+
// Therefore, we only apply entity value range filtering if the nearby feature is not enabled;
117+
// otherwise, we would end up applying the filtering logic twice.
118+
entitySelector = applyEntityValueRangeFiltering(configPolicy, entitySelector, entityValueRangeRecorderId,
119+
minimumCacheType, inheritedSelectionOrder, baseRandomSelection);
116120
}
117121
entitySelector = applyFiltering(entitySelector, instanceCache);
118122
entitySelector = applySorting(resolvedCacheType, resolvedSelectionOrder, entitySelector, instanceCache);

core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/list/DestinationSelectorFactory.java

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ public DestinationSelector<Solution_> buildDestinationSelector(HeuristicConfigPo
4040
minimumCacheType, selectionOrder, entityValueRangeRecorderId);
4141
var baseDestinationSelector =
4242
new ElementDestinationSelector<>(entitySelector, valueSelector, selectionOrder.toRandomSelectionBoolean());
43-
return applyNearbySelection(configPolicy, minimumCacheType, selectionOrder, baseDestinationSelector);
43+
return applyNearbySelection(configPolicy, minimumCacheType, selectionOrder, baseDestinationSelector,
44+
entityValueRangeRecorderId != null);
4445
}
4546

4647
private IterableValueSelector<Solution_> buildIterableValueSelector(
@@ -72,14 +73,34 @@ private IterableValueSelector<Solution_> buildIterableValueSelector(
7273
}
7374

7475
private DestinationSelector<Solution_> applyNearbySelection(HeuristicConfigPolicy<Solution_> configPolicy,
75-
SelectionCacheType minimumCacheType, SelectionOrder resolvedSelectionOrder,
76-
ElementDestinationSelector<Solution_> destinationSelector) {
76+
SelectionCacheType minimumCacheType, SelectionOrder selectionOrder,
77+
ElementDestinationSelector<Solution_> destinationSelector, boolean enableEntityValueRange) {
7778
NearbySelectionConfig nearbySelectionConfig = config.getNearbySelectionConfig();
7879
if (nearbySelectionConfig == null) {
7980
return destinationSelector;
8081
}
81-
return TimefoldSolverEnterpriseService.loadOrFail(TimefoldSolverEnterpriseService.Feature.NEARBY_SELECTION)
82-
.applyNearbySelection(config, configPolicy, minimumCacheType, resolvedSelectionOrder, destinationSelector);
82+
// The nearby selector will implement its own logic to filter out unreachable elements.
83+
// It requires the child selectors to not be FilteringEntityValueRangeSelector or FilteringValueRangeSelector,
84+
// as it needs to iterate over all available values to construct the distance matrix.
85+
if (enableEntityValueRange) {
86+
var entitySelector =
87+
EntitySelectorFactory.<Solution_> create(Objects.requireNonNull(config.getEntitySelectorConfig()))
88+
.buildEntitySelector(configPolicy, minimumCacheType, selectionOrder);
89+
var valueSelector = ValueSelectorFactory
90+
.<Solution_> create(Objects.requireNonNull(config.getValueSelectorConfig()))
91+
.buildValueSelector(configPolicy, entitySelector.getEntityDescriptor(), minimumCacheType,
92+
selectionOrder, configPolicy.isReinitializeVariableFilterEnabled(),
93+
ValueSelectorFactory.ListValueFilteringType.ACCEPT_ASSIGNED, null, false);
94+
var updatedDestinationSelector =
95+
new ElementDestinationSelector<>(entitySelector, (IterableValueSelector<Solution_>) valueSelector,
96+
selectionOrder.toRandomSelectionBoolean());
97+
return TimefoldSolverEnterpriseService.loadOrFail(TimefoldSolverEnterpriseService.Feature.NEARBY_SELECTION)
98+
.applyNearbySelection(config, configPolicy, minimumCacheType, selectionOrder,
99+
updatedDestinationSelector);
100+
} else {
101+
return TimefoldSolverEnterpriseService.loadOrFail(TimefoldSolverEnterpriseService.Feature.NEARBY_SELECTION)
102+
.applyNearbySelection(config, configPolicy, minimumCacheType, selectionOrder, destinationSelector);
103+
}
83104
}
84105

85106
}

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,13 +126,16 @@ public ValueSelector<Solution_> buildValueSelector(HeuristicConfigPolicy<Solutio
126126
buildBaseValueSelector(variableDescriptor, SelectionCacheType.max(minimumCacheType, resolvedCacheType),
127127
randomSelection);
128128
var instanceCache = configPolicy.getClassInstanceCache();
129-
valueSelector = applyValueRangeFiltering(configPolicy, valueSelector, entityDescriptor, minimumCacheType,
130-
inheritedSelectionOrder, randomSelection, entityValueRangeRecorderId,
131-
assertBothSides);
132129
if (nearbySelectionConfig != null) {
133130
// TODO Static filtering (such as movableEntitySelectionFilter) should affect nearbySelection too
134131
valueSelector = applyNearbySelection(configPolicy, entityDescriptor, minimumCacheType,
135132
resolvedSelectionOrder, valueSelector);
133+
} else {
134+
// The nearby selector will implement its own logic to filter out unreachable elements.
135+
// Therefore, we only apply entity value range filtering if the nearby feature is not enabled;
136+
// otherwise, we would end up applying the filtering logic twice.
137+
valueSelector = applyValueRangeFiltering(configPolicy, valueSelector, entityDescriptor, minimumCacheType,
138+
inheritedSelectionOrder, randomSelection, entityValueRangeRecorderId, assertBothSides);
136139
}
137140
valueSelector = applyFiltering(valueSelector, instanceCache);
138141
valueSelector = applyInitializedChainedValueFilter(configPolicy, variableDescriptor, valueSelector);

0 commit comments

Comments
 (0)