-
Notifications
You must be signed in to change notification settings - Fork 95
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: unify the handling of list variable shadows (#1228)
All shadow variables related to a list variable are now handled in one unified loop, as opposed to each having its own variable listener. This should technically be a performance improvement, but in fact a barely significant regression of 3 % was measured in some cases. (Other cases show minor improvements.) This change also makes internalized previous/next shadows possible, and that in turn will help with the revamp of shadow variables that is currently in development. Therefore we introduce this change even though it may bring a small performance regression.
- Loading branch information
Showing
29 changed files
with
730 additions
and
789 deletions.
There are no files selected for viewing
36 changes: 36 additions & 0 deletions
36
...olver/core/impl/domain/variable/AbstractExternalizedNextPrevElementVariableProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package ai.timefold.solver.core.impl.domain.variable; | ||
|
||
import java.util.List; | ||
import java.util.Objects; | ||
|
||
import ai.timefold.solver.core.impl.domain.variable.descriptor.ShadowVariableDescriptor; | ||
import ai.timefold.solver.core.impl.score.director.InnerScoreDirector; | ||
|
||
abstract sealed class AbstractExternalizedNextPrevElementVariableProcessor<Solution_> | ||
permits ExternalizedNextElementVariableProcessor, ExternalizedPreviousElementVariableProcessor { | ||
|
||
protected final ShadowVariableDescriptor<Solution_> shadowVariableDescriptor; | ||
|
||
protected AbstractExternalizedNextPrevElementVariableProcessor( | ||
ShadowVariableDescriptor<Solution_> shadowVariableDescriptor) { | ||
this.shadowVariableDescriptor = Objects.requireNonNull(shadowVariableDescriptor); | ||
} | ||
|
||
public abstract void setElement(InnerScoreDirector<Solution_, ?> scoreDirector, List<Object> listVariable, Object element, | ||
int index); | ||
|
||
public abstract Object getElement(Object element); | ||
|
||
public void unsetElement(InnerScoreDirector<Solution_, ?> scoreDirector, Object element) { | ||
setValue(scoreDirector, element, null); | ||
} | ||
|
||
protected void setValue(InnerScoreDirector<Solution_, ?> scoreDirector, Object element, Object value) { | ||
if (getElement(element) != value) { | ||
scoreDirector.beforeVariableChanged(shadowVariableDescriptor, element); | ||
shadowVariableDescriptor.setValue(element, value); | ||
scoreDirector.afterVariableChanged(shadowVariableDescriptor, element); | ||
} | ||
} | ||
|
||
} |
45 changes: 45 additions & 0 deletions
45
...java/ai/timefold/solver/core/impl/domain/variable/ExternalizedIndexVariableProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package ai.timefold.solver.core.impl.domain.variable; | ||
|
||
import java.util.Objects; | ||
|
||
import ai.timefold.solver.core.impl.domain.variable.index.IndexShadowVariableDescriptor; | ||
import ai.timefold.solver.core.impl.score.director.InnerScoreDirector; | ||
|
||
final class ExternalizedIndexVariableProcessor<Solution_> { | ||
|
||
private final IndexShadowVariableDescriptor<Solution_> shadowVariableDescriptor; | ||
|
||
public ExternalizedIndexVariableProcessor(IndexShadowVariableDescriptor<Solution_> shadowVariableDescriptor) { | ||
this.shadowVariableDescriptor = shadowVariableDescriptor; | ||
} | ||
|
||
public void addElement(InnerScoreDirector<Solution_, ?> scoreDirector, Object element, Integer index) { | ||
updateIndex(scoreDirector, element, index); | ||
} | ||
|
||
public void removeElement(InnerScoreDirector<Solution_, ?> scoreDirector, Object element) { | ||
updateIndex(scoreDirector, element, null); | ||
} | ||
|
||
public void unassignElement(InnerScoreDirector<Solution_, ?> scoreDirector, Object element) { | ||
removeElement(scoreDirector, element); | ||
} | ||
|
||
public void changeElement(InnerScoreDirector<Solution_, ?> scoreDirector, Object element, Integer index) { | ||
updateIndex(scoreDirector, element, index); | ||
} | ||
|
||
private void updateIndex(InnerScoreDirector<Solution_, ?> scoreDirector, Object element, Integer index) { | ||
var oldIndex = getIndex(element); | ||
if (!Objects.equals(oldIndex, index)) { | ||
scoreDirector.beforeVariableChanged(shadowVariableDescriptor, element); | ||
shadowVariableDescriptor.setValue(element, index); | ||
scoreDirector.afterVariableChanged(shadowVariableDescriptor, element); | ||
} | ||
} | ||
|
||
public Integer getIndex(Object planningValue) { | ||
return shadowVariableDescriptor.getValue(planningValue); | ||
} | ||
|
||
} |
63 changes: 63 additions & 0 deletions
63
...i/timefold/solver/core/impl/domain/variable/ExternalizedListInverseVariableProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package ai.timefold.solver.core.impl.domain.variable; | ||
|
||
import ai.timefold.solver.core.impl.domain.variable.descriptor.ListVariableDescriptor; | ||
import ai.timefold.solver.core.impl.domain.variable.inverserelation.InverseRelationShadowVariableDescriptor; | ||
import ai.timefold.solver.core.impl.score.director.InnerScoreDirector; | ||
|
||
final class ExternalizedListInverseVariableProcessor<Solution_> { | ||
|
||
private final InverseRelationShadowVariableDescriptor<Solution_> shadowVariableDescriptor; | ||
private final ListVariableDescriptor<Solution_> sourceVariableDescriptor; | ||
|
||
public ExternalizedListInverseVariableProcessor( | ||
InverseRelationShadowVariableDescriptor<Solution_> shadowVariableDescriptor, | ||
ListVariableDescriptor<Solution_> sourceVariableDescriptor) { | ||
this.shadowVariableDescriptor = shadowVariableDescriptor; | ||
this.sourceVariableDescriptor = sourceVariableDescriptor; | ||
} | ||
|
||
public void addElement(InnerScoreDirector<Solution_, ?> scoreDirector, Object entity, Object element) { | ||
setInverseAsserted(scoreDirector, element, entity, null); | ||
} | ||
|
||
private void setInverseAsserted(InnerScoreDirector<Solution_, ?> scoreDirector, Object element, Object inverseEntity, | ||
Object expectedOldInverseEntity) { | ||
var oldInverseEntity = getInverseSingleton(element); | ||
if (oldInverseEntity == inverseEntity) { | ||
return; | ||
} | ||
if (scoreDirector.expectShadowVariablesInCorrectState() && oldInverseEntity != expectedOldInverseEntity) { | ||
throw new IllegalStateException(""" | ||
The entity (%s) has a list variable (%s) and one of its elements (%s) which has a shadow variable (%s) \ | ||
has an oldInverseEntity (%s) which is not that entity. | ||
Verify the consistency of your input problem for that shadow variable.""" | ||
.formatted(inverseEntity, sourceVariableDescriptor.getVariableName(), element, | ||
shadowVariableDescriptor.getVariableName(), oldInverseEntity)); | ||
} | ||
setInverse(scoreDirector, inverseEntity, element); | ||
} | ||
|
||
private void setInverse(InnerScoreDirector<Solution_, ?> scoreDirector, Object entity, Object element) { | ||
scoreDirector.beforeVariableChanged(shadowVariableDescriptor, element); | ||
shadowVariableDescriptor.setValue(element, entity); | ||
scoreDirector.afterVariableChanged(shadowVariableDescriptor, element); | ||
} | ||
|
||
public void removeElement(InnerScoreDirector<Solution_, ?> scoreDirector, Object entity, Object element) { | ||
setInverseAsserted(scoreDirector, element, null, entity); | ||
} | ||
|
||
public void unassignElement(InnerScoreDirector<Solution_, ?> scoreDirector, Object element) { | ||
changeElement(scoreDirector, null, element); | ||
} | ||
|
||
public void changeElement(InnerScoreDirector<Solution_, ?> scoreDirector, Object entity, Object element) { | ||
if (getInverseSingleton(element) != entity) { | ||
setInverse(scoreDirector, entity, element); | ||
} | ||
} | ||
|
||
public Object getInverseSingleton(Object planningValue) { | ||
return shadowVariableDescriptor.getValue(planningValue); | ||
} | ||
} |
160 changes: 73 additions & 87 deletions
160
...ava/ai/timefold/solver/core/impl/domain/variable/ExternalizedListVariableStateSupply.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.