Skip to content

Commit

Permalink
feat: Adding Nearby configuration by exception (#673)
Browse files Browse the repository at this point in the history
This pull request adds easy configuration of the Nearby Selection
feature and updates the local search phase factory to provide default
selectors when custom configuration is not provided. The configuration
for the selectors is listed below:
1. Basic variables selectors: Change, Swap, Change with nearby
selection, and Swap with nearby selection.
2. List variables selectors: Change, Swap, 2-OPT, Change with nearby selection,
Swap with nearby selection, 2-OPT with nearby selection.
3. Chained variables selectors: Change, Swap, Tail Chain Swap, Change with nearby
selection, Swap with nearby selection, Tail Chain Swap with nearby
selection.

The following XML configuration can be used to enable Nearby selection
quickly:

`<nearbyDistanceMeterClass>...</nearbyDistanceMeterClass>`

If there are multiple entities, we will fail fast.
  • Loading branch information
zepfred authored Mar 5, 2024
1 parent 6b47e7b commit 55c373b
Show file tree
Hide file tree
Showing 9 changed files with 258 additions and 13 deletions.
3 changes: 3 additions & 0 deletions benchmark/src/main/resources/benchmark.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,9 @@
<xs:element minOccurs="0" name="termination" type="tns:terminationConfig"/>


<xs:element minOccurs="0" name="nearbyDistanceMeterClass" type="xs:string"/>


<xs:choice maxOccurs="unbounded" minOccurs="0">


Expand Down
13 changes: 13 additions & 0 deletions core/core-impl/src/build/revapi-differences.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@
{
"extension": "revapi.differences",
"configuration": {
"differences": [
{
"ignore": true,
"code": "java.annotation.attributeValueChanged",
"old": "class ai.timefold.solver.core.config.solver.SolverConfig",
"new": "class ai.timefold.solver.core.config.solver.SolverConfig",
"annotationType": "jakarta.xml.bind.annotation.XmlType",
"attribute": "propOrder",
"oldValue": "{\"environmentMode\", \"daemon\", \"randomType\", \"randomSeed\", \"randomFactoryClass\", \"moveThreadCount\", \"moveThreadBufferSize\", \"threadFactoryClass\", \"monitoringConfig\", \"solutionClass\", \"entityClassList\", \"domainAccessType\", \"scoreDirectorFactoryConfig\", \"terminationConfig\", \"phaseConfigList\"}",
"newValue": "{\"environmentMode\", \"daemon\", \"randomType\", \"randomSeed\", \"randomFactoryClass\", \"moveThreadCount\", \"moveThreadBufferSize\", \"threadFactoryClass\", \"monitoringConfig\", \"solutionClass\", \"entityClassList\", \"domainAccessType\", \"scoreDirectorFactoryConfig\", \"terminationConfig\", \"nearbyDistanceMeterClass\", \"phaseConfigList\"}",
"justification": "Adding new Nearby root property"
}
]
}
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import ai.timefold.solver.core.config.solver.termination.TerminationConfig;
import ai.timefold.solver.core.config.util.ConfigUtils;
import ai.timefold.solver.core.impl.domain.common.accessor.MemberAccessor;
import ai.timefold.solver.core.impl.heuristic.selector.common.nearby.NearbyDistanceMeter;
import ai.timefold.solver.core.impl.io.jaxb.SolverConfigIO;
import ai.timefold.solver.core.impl.io.jaxb.TimefoldXmlSerializationException;
import ai.timefold.solver.core.impl.phase.PhaseFactory;
Expand All @@ -70,6 +71,7 @@
"domainAccessType",
"scoreDirectorFactoryConfig",
"terminationConfig",
"nearbyDistanceMeterClass",
"phaseConfigList",
})
public class SolverConfig extends AbstractConfig<SolverConfig> {
Expand Down Expand Up @@ -240,6 +242,8 @@ public static SolverConfig createFromXmlReader(Reader reader, ClassLoader classL
@XmlElement(name = "termination")
private TerminationConfig terminationConfig;

protected Class<? extends NearbyDistanceMeter<?, ?>> nearbyDistanceMeterClass = null;

@XmlElements({
@XmlElement(name = ConstructionHeuristicPhaseConfig.XML_ELEMENT_NAME,
type = ConstructionHeuristicPhaseConfig.class),
Expand Down Expand Up @@ -411,6 +415,14 @@ public void setTerminationConfig(TerminationConfig terminationConfig) {
this.terminationConfig = terminationConfig;
}

public Class<? extends NearbyDistanceMeter<?, ?>> getNearbyDistanceMeterClass() {
return nearbyDistanceMeterClass;
}

public void setNearbyDistanceMeterClass(Class<? extends NearbyDistanceMeter<?, ?>> nearbyDistanceMeterClass) {
this.nearbyDistanceMeterClass = nearbyDistanceMeterClass;
}

public List<PhaseConfig> getPhaseConfigList() {
return phaseConfigList;
}
Expand Down Expand Up @@ -561,6 +573,11 @@ public SolverConfig withTerminationSpentLimit(Duration spentLimit) {
return this;
}

public SolverConfig withNearbyDistanceMeterClass(Class<? extends NearbyDistanceMeter<?, ?>> distanceMeterClass) {
this.nearbyDistanceMeterClass = distanceMeterClass;
return this;
}

public SolverConfig withPhaseList(List<PhaseConfig> phaseConfigList) {
this.phaseConfigList = phaseConfigList;
return this;
Expand Down Expand Up @@ -657,6 +674,8 @@ public SolverConfig inherit(SolverConfig inheritedConfig) {
scoreDirectorFactoryConfig = ConfigUtils.inheritConfig(scoreDirectorFactoryConfig,
inheritedConfig.getScoreDirectorFactoryConfig());
terminationConfig = ConfigUtils.inheritConfig(terminationConfig, inheritedConfig.getTerminationConfig());
nearbyDistanceMeterClass = ConfigUtils.inheritOverwritableProperty(nearbyDistanceMeterClass,
inheritedConfig.getNearbyDistanceMeterClass());
phaseConfigList = ConfigUtils.inheritMergeableListConfig(phaseConfigList, inheritedConfig.getPhaseConfigList());
monitoringConfig = ConfigUtils.inheritConfig(monitoringConfig, inheritedConfig.getMonitoringConfig());
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ThreadFactory;

import ai.timefold.solver.core.config.heuristic.selector.entity.EntitySorterManner;
import ai.timefold.solver.core.config.heuristic.selector.value.ValueSorterManner;
import ai.timefold.solver.core.config.solver.EnvironmentMode;
import ai.timefold.solver.core.config.util.ConfigUtils;
import ai.timefold.solver.core.impl.domain.solution.descriptor.SolutionDescriptor;
import ai.timefold.solver.core.impl.heuristic.selector.common.nearby.NearbyDistanceMeter;
import ai.timefold.solver.core.impl.heuristic.selector.entity.EntitySelector;
import ai.timefold.solver.core.impl.heuristic.selector.entity.mimic.EntityMimicRecorder;
import ai.timefold.solver.core.impl.heuristic.selector.list.SubListSelector;
Expand Down Expand Up @@ -36,6 +38,8 @@ public class HeuristicConfigPolicy<Solution_> {
private final boolean reinitializeVariableFilterEnabled;
private final boolean initializedChainedValueFilterEnabled;
private final boolean unassignedValuesAllowed;
private final Class<? extends NearbyDistanceMeter<?, ?>> nearbyDistanceMeterClass;
private final Random random;

private final Map<String, EntityMimicRecorder<Solution_>> entityMimicRecorderMap = new HashMap<>();
private final Map<String, SubListMimicRecorder<Solution_>> subListMimicRecorderMap = new HashMap<>();
Expand All @@ -55,6 +59,8 @@ private HeuristicConfigPolicy(Builder<Solution_> builder) {
this.reinitializeVariableFilterEnabled = builder.reinitializeVariableFilterEnabled;
this.initializedChainedValueFilterEnabled = builder.initializedChainedValueFilterEnabled;
this.unassignedValuesAllowed = builder.unassignedValuesAllowed;
this.nearbyDistanceMeterClass = builder.nearbyDistanceMeterClass;
this.random = builder.random;
}

public EnvironmentMode getEnvironmentMode() {
Expand Down Expand Up @@ -109,13 +115,22 @@ public boolean isUnassignedValuesAllowed() {
return unassignedValuesAllowed;
}

public Class<? extends NearbyDistanceMeter> getNearbyDistanceMeterClass() {
return nearbyDistanceMeterClass;
}

public Random getRandom() {
return random;
}

// ************************************************************************
// Builder methods
// ************************************************************************

public Builder<Solution_> cloneBuilder() {
return new Builder<>(environmentMode, moveThreadCount, moveThreadBufferSize, threadFactoryClass, initializingScoreTrend,
solutionDescriptor, classInstanceCache).withLogIndentation(logIndentation);
return new Builder<>(environmentMode, moveThreadCount, moveThreadBufferSize, threadFactoryClass,
nearbyDistanceMeterClass, random, initializingScoreTrend, solutionDescriptor, classInstanceCache)
.withLogIndentation(logIndentation);
}

public HeuristicConfigPolicy<Solution_> createPhaseConfigPolicy() {
Expand Down Expand Up @@ -214,13 +229,20 @@ public static class Builder<Solution_> {
private boolean initializedChainedValueFilterEnabled = false;
private boolean unassignedValuesAllowed = false;

private final Class<? extends NearbyDistanceMeter<?, ?>> nearbyDistanceMeterClass;
private final Random random;

public Builder(EnvironmentMode environmentMode, Integer moveThreadCount, Integer moveThreadBufferSize,
Class<? extends ThreadFactory> threadFactoryClass, InitializingScoreTrend initializingScoreTrend,
SolutionDescriptor<Solution_> solutionDescriptor, ClassInstanceCache classInstanceCache) {
Class<? extends ThreadFactory> threadFactoryClass,
Class<? extends NearbyDistanceMeter<?, ?>> nearbyDistanceMeterClass, Random random,
InitializingScoreTrend initializingScoreTrend, SolutionDescriptor<Solution_> solutionDescriptor,
ClassInstanceCache classInstanceCache) {
this.environmentMode = environmentMode;
this.moveThreadCount = moveThreadCount;
this.moveThreadBufferSize = moveThreadBufferSize;
this.threadFactoryClass = threadFactoryClass;
this.nearbyDistanceMeterClass = nearbyDistanceMeterClass;
this.random = random;
this.initializingScoreTrend = initializingScoreTrend;
this.solutionDescriptor = solutionDescriptor;
this.classInstanceCache = classInstanceCache;
Expand Down
Loading

0 comments on commit 55c373b

Please sign in to comment.